本章问题
1.如果一个值的类型无法简单的通过观察它的位模式来判断,那么机器是如何知道应该怎样对这个值进行操纵的?
answer:The machine doesn't make this determination.The compiler creates the appropriate instructions(指令) based on the declared type of the value,and the machine blindly executes the instructions.
(机器无法检测这个值,编译器在声明一个值的类型的基础上创建一个合适的指令,然后机器只能够执行指令)
2.C为什么没有一种方法来声明字面值指针常量呢?
answer:They are rarely used because you can't tell ahead of time where the compiler will put variables.
(它们几乎很少用,因为你不可能在编译器创建变量之前知道它在哪个位置)
3.假定一个整数的值为244,为什么机器不会把这个值解释为一个内存地址呢?
answer:The value is an integer,so the compiler will not generate instructions to dereference it.
(这个值是一个整型,所以编译器不会产生这样的指令)
4.在有些机器上,编译器在内存位置零存储0这个值,对NULL指针进行解引用操作将访问这个位置,这种方法产生什么后果?
answer:It is dangerous,First,the result of dereferencing a NULL pointer is implementation specific,so programs should not do it,Allowing a program to continue after such an access is unfortunate,because of the strong possibility that the program is not operating correctly.
(这是非常危险的,首先,对NULL指针解引用操作的结果因编译器而异,所以程序不该这么做,允许一个程序在这种访问之后继续运行是非常不幸的,因为有很大的可能程序不能正确运行)
5.表达式(a)和(b)的求值过程有没有什么区别?如果有的话,区别在哪里?假定变量offset的值为3.
int i[10]; int *p = &i[0]; int offset; p += offset; (a) p += 3; (b)
answer:Even if offset has the same value as the literal(字面值) in the next expression.it is more time consuming(消耗) to evaluate(计算) the first expression because the multiplication to scale(规模) offset to the size of an integer must be done at run time.This is because the variable might contain any value,and the compiler has no way of knowing ahead of time what the value might actually be.On the other hand,The literal three can be scaled to an integer by multiplying it at compiler time,and the result of that multiplication is simply added to p at run time,In other words,the second expression can be implemented by simply adding 12 to p(on a machine with four-byte integers);no runtime multiplication is needed.
(即使offset跟后面一个表达式的字面值的值相等,但它比计算第一个表达式要消耗更多的时间,因为对整型大小的偏移量必须在运行时才知道,这是因为变量可能包括更多的值,而编译器不能在变量赋值之前知道它的值,另一方面,在编译的时候字面值3可以被缩放到一个整数中,结果在运行是添加到p,换句话说,第二个表达式可以简单的用12加上p,在四位机器上,不需要运行时的再相加)
6.下面的代码有没有问题,如果有的话,问题在哪里?
int array[ARRAY_SIZE]; int *pi; for(pi = &array[0];pi < array[ARRAY_SIZE];) *++pi = 0;
answer:有两个错误,对增值之后的指针进行解引用时,数组的第一个元素没有被初始化为0,另外,指针在越过数组的右边界以后仍然进行解引用,它将把其他内存地址的内容清零。注意pi在数组之后立即声明,如果编译器恰好把它放在紧跟数组之后的位置,结果将是灾难性的,会导致一个微妙的无限循环。
7.下面的表显示了几个内存位置的内容。每个位置由它的地址和存储于该位置的变量名标识。所有数字以十进制形式表示。使用这些值,用4中方法分别计算下面各表达式的值,首先,假定所有的变量都是整型,找到表达式的右值,再找到表达式的左值,给出它所指定的内存位置的地址。接着,假定所有的变量都是指向整型的指针,重复上述步骤,注意:在执行地址运算时,假定整型和指针的长度都是4个字节。
变量 | 地址 | 内容 | 变量 | 地址 | 内容 |
a | 1040 | 1028 | o | 1096 | 1024 |
c | 1056 | 1076 | q | 1084 | 1072 |
d | 1008 | 1016 | r | 1068 | 1048 |
e | 1032 | 1088 | s | 1004 | 2000 |
f | 1052 | 1044 | t | 1060 | 1012 |
g | 1000 | 1064 | u | 1036 | 1092 |
h | 1080 | 1020 | v | 1092 | 1036 |
i | 1020 | 1080 | w | 1012 | 1060 |
j | 1064 | 1000 | x | 1072 | 1080 |
k | 1044 | 1052 | y | 1048 | 1068 |
m | 1016 | 1008 | z | 2000 | 1000 |
n | 1076 | 1056 |
a. m b. v + 1 c. j – 4 d. a – d e. v – w f. &c g. &e + 1 h. &o – 4 i. &( f + 2 ) j. *g k. *k + 1 l. *( n + 1 ) m. *h – 4 n. *( u – 4 ) o. *f – g p. *f - *g q. *s - *q r. *( r – t ) s. y > i t. y > *i u. *y > *i v. **h w. c++ x. ++c y. *q++ z. (*q)++ aa. *++q bb. ++*q cc. *++*q dd. ++*(*q)++
answer:
|--------整型------------------|--------指针-------------------| 表达式--------右值--------左值地址--------右值--------左值地址 a. m 1008 1016 1008 1016 b. v + 1 1037 illegal 1040 illegal c. j – 4 0996 illegal 0984 illegal d. a – d 12 illegal 3 illegal e. v – w -24 illegal -6 illegal f. &c 1056 illegal 1056 illegal g. &e + 1 1036 illegal 1036 illegal h. &o – 4 1080 illegal 1080 illegal i. &( f + 2 ) illegal illegal illegal illegal j. *g illegal illegal 1000 1064 k. *k + 1 illegal illegal 1045 illegal l. *( n + 1 ) illegal illegal 1012 1060 m. *h – 4 illegal illegal 1076 illegal n. *( u – 4 ) illegal illegal 1056 1076 o. *f – g illegal illegal illegal illegal p. *f - *g illegal illegal 52 illegal q. *s - *q illegal illegal -80 illegal r. *( r – t ) illegal illegal illegal illegal s. y > i 0 illegal 0 illegal t. y > *i illegal illegal illegal illegal u. *y > *i illegal illegal 1 illegal v. **h illegal illegal 1080 1020 w. c++ 1076 illegal 1076 illegal x. ++c 1077 illegal 1080 illegal y. *q++ illegal illegal 1080 1072 z. (*q)++ illegal illegal 1080 illegal aa. *++q illegal illegal 1056 1076 bb. ++*q illegal illegal 1081 illegal cc. *++*q illegal illegal illegal illegal dd. ++*(*q)++ illegal illegal 1021 illegal
本章练习
1.请编写一个函数,它在一个字符串中进行搜索,查找所有在一个给定字符集合中出现的字符。这个函数的原型应该如下:
char *find_char(char const *source,char const *chars);
它的基本想法是查找source字符串中匹配chars字符串中任何字符的第一个字符,函数然后返回一个指向source中第一个匹配所找到的位置的指针,如果任何一个参数NULL,或任何一个参数所指向的字符串为空,函数也返回一个NULL指针。
举个例子,假定source指向ABCDEF。如果chars指向XYZ、JURY或QQQQ,函数就返回一个NULL指针。如果chars指向XRCQEF,函数就返回一个指向source中C字符的指针。参数所指向的字符串是不会被修改的。
碰巧,C函数库中存在一个名叫strpbrk的函数,它的功能几乎和这个你要编写的函数一模一样。但这个程序的目的是让你自己练习操纵指针,所以:
a 你不应该使用任何用于操纵字符串的库函数(如strcpy,strcmp,index等)
b 函数中的任何地方都不应该使用下标
answer:
/*遇到一个头疼的问题,由于两个参数都是const变量, 导致声明char*类型的变量指向source和chars时引出警告 initialization discards ‘const’ qualifier from pointer target type, 类型不对啊,如果声明的char*类型为const变量, 那么就无法改变指针的值,并且返回的类型也不对 所以,顶着警告还是可以运行的*/ char *find_char(char const *source,char const *chars) { char *ps = source; char *pc = NULL; if(source == NULL || chars == NULL) return NULL; while(*ps != '