6.(1)const放在p之前,限制的是 * p,所以p不可更改,但是p未被限制,所以p指向的对象可以更改。
(2)同(1),同样放在*p之前,结果同(1)
(3)const放在p之前,p之后,限制的是p,所以p指向的对象不可更改,p可以更改
(4)p和p都被限制了,所以p指向的对象和p都不可更改。
7.代码(1):这时候编译器对代码进行优化,因为在代码(1)的两条语句中,i没有被用作左值(没有被赋值)。这时候编译器认为i的值没有发生改变,所以在第1条语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在第⒉条语句时继续用这个值给k赋值。编译器不会生成出汇编代码重新从内存里取i的值(不会编译生成装载内存的的汇编指令﹐比如ARM的LDM指令),这样提高了效率。但要注意:两条语句之间i没有被用作左值(没有被赋值)才行。
代码(2)的使用时机:如果i是一个寄存器变量、表示一个端口数据或者是多个线程的共享数据,那么就容易出错,所以说
volatile可以保证对特殊地址的稳定访问。
代码(2)的使用时机:如果i是一个寄存器变量、表示一个端口数据或者是多个线程的共享数据,那么就容易出错,所以说
volatile可以保证对特殊地址的稳定访问。
8.这道题需要画图理解。结果是 5,2000000
数组a的5个元素的十六进制表示形式在内存中的布局如上图:
对于ptr1来说,&a取到了整个数组的地址,+1跳过了整个数组,再强转成int*,所以ptr1指向的是数组的后一位的地址。如下图:
以16进制的形式打印ptr[-1]时,其实可以理解成打印 *(ptr-1),指针-1,跳过指针所指向对象的类型,ptr1指向的对象是int类型,所以-1跳过一个int。如下图:
指针指向的对象是一个int类型,所以一次取出4个字节。
由于计算机是小端存储模式,所以,低位放在低地址处,高位放在高地址处,拿出来就是 00 00 00 05,以十六进制的形式打印,5前面的0是不显示的,没什么意义,所以打印出来就是5
对于ptr2来说,a是数组首元素地址,强转成(int),所以a的地址就被强转成了一个整型,假设a的地址为:0x0012ff40,该地址就被强转成了一个整型,此时0x0012ff40是一个整型,整型+1之后,就是0x0012ff41,加了一个字节(整型+1就是+1)。0x0012ff41再强转成一个int*的指针赋给ptr2。此时ptr2指向的位置如下图:
ptr2指向的对象是int类型,所以ptr2一次性会读取4个字节,即读取
00 00 00 02,由于计算机是小端存储,低位放在低地址,高位放在高地址。
所以以十六进制的形式表示是0x 02 00 00 00,以十六进制的形式打印出来是2 00 00 00,(02中的0会被去掉)
9.值为32
+号的优先级高于<<,先计算2+3,再计算0x01<<, 即
0x01<<5,
将0x01的十六进制补全是 0x00 00 00 01,转换成二进制的形式为:
00000000 00000000 00000000 00000001
<<操作符移动的是二进制位,左移5位后变成:
00000000 00000000 00000000 00100000
转换成十进制是32
10.答案:
#define SQR(x) ((x)*(x))
然而,该宏是有缺陷的,如果传入x++,++x这样有副作用的参数,宏就会出现问题。
(指出宏有缺陷者满分)
11.结构体在内存中的存储较为复杂,具体请看
这里直接说明答案即可:
(在未说明的情况下,默认对齐数为8)
代码(1)的结构体大小为12字节,代码(2)的结构体大小为8字节。
12.实际上,这道题目是存在一点问题的,实际中不能随便找一个地址,就对该地址的内容进行访问,这是野指针问题,是非法访问的问题,对编译器来说这是违法行为。
相当于题目让你闯红灯,闯红灯本来就是违法行为。(这里没有针对任何人,只是就本道题说一下我的看法);
可能题目是想让写题人知道指针的强大和灵活,让写题人重视指针的运用。
不过如果真的要写,就是下面的写法:
int *p = (int*)0x12ff7c; *p = 0x100;
13.
a表示数组首元素地址,a+1跳过了一个元素的大小,所以a+1就是a[1]的地址,*(a+1) 就拿到了a[1],所以结果是1。
对ptr来说,&a取到的是整个数组的地址,+1跳过了整个数组,所以ptr指向了元素5的后一位, * (ptr-1) 就指向5,打印出来就是5。
14. 0x100014,0x100001,0x010004
(1)
首先需要知道结构体的大小是多少,由上面结构体内存存储模式可知,该结构体大小为20字节。
p是一个结构体指针,指针+0x1,跳过了一个结构体的大小,所以p+1跳过了20字节,因为p的值为0x100000,所以p+1为0x100014,(20)转换为十六进制是14。
(2)
将p强转成unsigned long类型后,p的值就是一个长整型,即0x100000是unsigned long类型,整型+1就是+1.
结果为:0x100001
(3)将p强转成unsigned int*的指针,+1后跳过一个int类型,所以结果为0x100004
15.考察逗号表达式。
(0,1)计算的结果是1,(2,3)计算结果是3,(4,5)计算结果是5
a初始化后的结果为:
1 2 5 0 0 0
a[0]就是第一行元素的地址,此时p就是指向第一行元素的地址,然后打印p[0],相当于找到了第一行第一个元素,打印结果为1
16.(1)传参的时候有问题,b[10]实际上是不存在的。
(2)fun的参数部分,如果它是想对数组的a[3]进行修改,传参应该传b即可,因为b是数组名,数组名表示首元素地址,类型是char* ,想改变它的值,应该使用char*的指针来接收。
编译器会有一个警告,说类型不匹配,因为b[10]的类型是char,而函数参数类型是char*。
17.(1)在使用完pstu后没有置空,这是一个野指针问题。
(2)结构体的成员name也是一个指针,代码并没有给该指针申请空间,直接使用name会出现越界访问问题.
18,典型的递归问题,直接上答案:
0 1 2 5 10
19.
getchar函数的返回类型是一个int,但是c是一个char类型,将一个int类型放入char类型中会发生截断。‘
假如读取失败,getchar会返回EOF,EOF是一个宏,其本质是-1,
但有可能getchar读取失败返回的EOF不再-128~127这个范围内,导致if条件判断语句不进入,存在潜在的危险。
20.方法1:
int main() { int a = 1; int ret = *((char*)&a); if (ret == 1) { printf("小端\n"); } else { printf("大端\n"); } }
方法2:
union un { int a; char c; }s; int main() { s.a = 1; if (s.c == 1) { printf("小端\n"); } else { printf("大端\n"); } return 0; }
在没有何提示的情况下,如果能得满分,那你可以扔掉本书了,因为你的水平已经大大超过了作者;如果能得80分以上,说明你的C语言基础还不错,学习本书可能会比较轻松;如果得分在50分以下,也不要气馁,努力学习就行了;如果不小心得了10分以下,那就得给自己敲敲警钟了;如果不幸得了0分,那实在是不应该,因为毕竟很多题是很简单的。