上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

《c语言深度解剖》--一套非常经典的笔试题

guduadmin271月前

学习完c语言,需要对所学知识进行一个检测,下面有一套笔试题,

你有四十分钟进行检测,每道题五分,严格要求自己打分。

根据作者原话:在没有何提示的情况下,如果能得满分,那你可以扔掉本书了,因为你的水平已经大大超过了作者;如果能得80分以上,说明你的C语言基础还不错,学习本书可能会比较轻松;如果得分在50分以下,也不要气馁,努力学习就行了;如果不小心得了10分以下,那就得给自己敲敲警钟了;如果不幸得了0分,那实在是不应该,因为毕竟很多题是很简单的。

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第1张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第2张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第3张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第4张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第5张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第6张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第7张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第8张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第9张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第10张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第11张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第12张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第13张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第14张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第15张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第16张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第17张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第18张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第19张

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第20张

答案解析:

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第1张

1.根据隐式类型转换存储规则:

long double
double
float
unsigned long int
long int
unsigned int
int
当连个不同类型的值比较或相加减时,默认从低到高转换,即向上转换

所以,a和b相加时,b会隐式转换成unsigned int类型,对于-20来说,其补码为:

100000000 00000000 00000000 00010100 -- 原码
1111111111 111111111 111111111  11101011 -- 反码
1111111111 111111111 111111111  11101100 -- 补码

此时b是无符号整型,首位不再被当成符号位,所以b成了一个很大的数字,加上a当然比6大。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第2张

2.运行到strcpy的时候可能会发生异常,因为str1存储了10个字符a之后,后面存储的不是’\0’,

strcpy拷贝时会寻找’\0’作为结束标志,str1中第10个字符a之后的空间不是str1的,是内存的空间,

strcpy从此地方开始向后寻找’\0’时,会造成内存的非法访问,可能会导致程序出现异常。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第3张

3.i = 10,j = 1.

由于静态的局部变量是再内存中的静态区存储,静态局部变量在初始化一次后不会被销毁,且只初始化一次,在下一次使用时仍然保持上一次的值。所以i只初始化一次,每次继续调用该函数时,仍然保留上一次i的值。

对于j来说,j是静态全局变量,每次调用函数都会被初始化.


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第4张

4.除了(3)是400以外,其余全都是4。

(1)p是指针,被置为空, 所以p所指向的地址就是一个空地址,既然是地址,题目限定了在32位系统下,所以就是4个字节。

(2)p其实就是对p进行解引用,p是所指向的对象是一个int类型,计算p其实就是计算int类型的大小。

(3)对于数组来说,数组名表示数组首元素地址,除了下面两个例外:

1.sizeof(数组名),即数组名单独放在sizeof内部,表示的是整个数组的大小。

2.&数组名,取到的是整个数组的地址。

除了上面两个特例,其余的数组名都是表示数组首元素地址。

很明显:(3)中的数组名单独放在sizeof内部,计算的就是整个数组大小,a数组有100个元素,每个元素是int类型,总大小就是400字节。

(4)a[100]就是计算a的第100个元素的大小,虽然a[100]并不存在,也不影响计算a的第100个元素的大小,也是一个int类型。比如说:int b[1] = {0}; 计算sizeof(b[10]),大小仍为4个字节。

(5)&a,是上面两个特例中的第二个特例,取到的是整个数组的地址,其实,整个数组的地址就是首元素的地址,只是如果取到整个数组的地址后+1,会跳过整个数组。

既然它是地址,地址就是4个字节。

(6)&a[0],就是取首元素地址,是地址就是4字节。

(7)这是一个典型的数组传参问题,b传参时,因为b是数组首元素地址,本质上传的是一个int*的指针,形参部分int b[100],实际上这是一个指针,所以计算b的大小,就是计算指针的大小,即4个字节。(b不再是数组首元素地址,b是一个指针)


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第5张

5.对于数组a来说,它的元素类型是signed char类型,范围是-128~127。a[0] = -1,a[1] = -2,…a[127] = -128,此时a[128]不可能是-129,因为:

-128的二进制原码是:

100000000 00000000 00000000 10000000

补码是:

11111111 11111111 11111111 10000000

存入a[127]中会发生截断,补码的低8位放进去,即

10000000放入,转化成原码为:

1 00000000 

而该原码规定就是-128。

而-129的二进制原码为:

100000000 00000000 00000000 10000001

补码为:

11111111 11111111 11111111 01111111

截取低8位存入a[128]中,是

01111111

转换成二进制原码是:

01111111

这是正数,正数的原码反码补码相同,转换成十进制是127.

所以a[128] = 127, a[129] = 126… ,直到a[255] = 0;

strlen计算长度时,就需要找’\0’作为结束标志,而0就代表’\0’。所以计算数组的长度时,大小为255.


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第26张

6.(1)const放在p之前,限制的是 * p,所以p不可更改,但是p未被限制,所以p指向的对象可以更改。

(2)同(1),同样放在*p之前,结果同(1)

(3)const放在p之前,p之后,限制的是p,所以p指向的对象不可更改,p可以更改

(4)p和p都被限制了,所以p指向的对象和p都不可更改。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第27张

7.代码(1):这时候编译器对代码进行优化,因为在代码(1)的两条语句中,i没有被用作左值(没有被赋值)。这时候编译器认为i的值没有发生改变,所以在第1条语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在第⒉条语句时继续用这个值给k赋值。编译器不会生成出汇编代码重新从内存里取i的值(不会编译生成装载内存的的汇编指令﹐比如ARM的LDM指令),这样提高了效率。但要注意:两条语句之间i没有被用作左值(没有被赋值)才行。

代码(2)的使用时机:如果i是一个寄存器变量、表示一个端口数据或者是多个线程的共享数据,那么就容易出错,所以说

volatile可以保证对特殊地址的稳定访问。

代码(2)的使用时机:如果i是一个寄存器变量、表示一个端口数据或者是多个线程的共享数据,那么就容易出错,所以说

volatile可以保证对特殊地址的稳定访问。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第8张

8.这道题需要画图理解。结果是 5,2000000

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第29张

数组a的5个元素的十六进制表示形式在内存中的布局如上图:

对于ptr1来说,&a取到了整个数组的地址,+1跳过了整个数组,再强转成int*,所以ptr1指向的是数组的后一位的地址。如下图:

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第30张

以16进制的形式打印ptr[-1]时,其实可以理解成打印 *(ptr-1),指针-1,跳过指针所指向对象的类型,ptr1指向的对象是int类型,所以-1跳过一个int。如下图:

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第31张

指针指向的对象是一个int类型,所以一次取出4个字节。

由于计算机是小端存储模式,所以,低位放在低地址处,高位放在高地址处,拿出来就是 00 00 00 05,以十六进制的形式打印,5前面的0是不显示的,没什么意义,所以打印出来就是5。

对于ptr2来说,a是数组首元素地址,强转成(int),所以a的地址就被强转成了一个整型,假设a的地址为:0x0012ff40,该地址就被强转成了一个整型,此时0x0012ff40是一个整型,整型+1之后,就是0x0012ff41,加了一个字节(整型+1就是+1)。0x0012ff41再强转成一个int*的指针赋给ptr2。此时ptr2指向的位置如下图:

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第32张

ptr2指向的对象是int类型,所以ptr2一次性会读取4个字节,即读取

00 00 00 02,由于计算机是小端存储,低位放在低地址,高位放在高地址。

所以以十六进制的形式表示是0x 02 00 00 00,以十六进制的形式打印出来是2 00 00 00,(02中的0会被去掉)


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第9张

9.值为32

+号的优先级高于<<,先计算2+3,再计算0x01<<, 即

0x01<<5,

将0x01的十六进制补全是 0x00 00 00 01,转换成二进制的形式为:

00000000 00000000 00000000 00000001

<<操作符移动的是二进制位,左移5位后变成:

00000000 00000000 00000000 00100000

转换成十进制是32


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第10张

10.答案:

#define SQR(x) ((x)*(x))

然而,该宏是有缺陷的,如果传入x++,++x这样有副作用的参数,宏就会出现问题。

(指出宏有缺陷者满分)


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第11张

11.结构体在内存中的存储较为复杂,具体请看

结构体内存对齐

这里直接说明答案即可:

(在未说明的情况下,默认对齐数为8)

代码(1)的结构体大小为12字节,代码(2)的结构体大小为8字节。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第12张

12.实际上,这道题目是存在一点问题的,实际中不能随便找一个地址,就对该地址的内容进行访问,这是野指针问题,是非法访问的问题,对编译器来说这是违法行为。

相当于题目让你闯红灯,闯红灯本来就是违法行为。(这里没有针对任何人,只是就本道题说一下我的看法);

可能题目是想让写题人知道指针的强大和灵活,让写题人重视指针的运用。

不过如果真的要写,就是下面的写法:

int *p = (int*)0x12ff7c;
*p = 0x100;

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第37张

13.

a表示数组首元素地址,a+1跳过了一个元素的大小,所以a+1就是a[1]的地址,*(a+1) 就拿到了a[1],所以结果是1。

对ptr来说,&a取到的是整个数组的地址,+1跳过了整个数组,所以ptr指向了元素5的后一位, * (ptr-1) 就指向5,打印出来就是5。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第14张

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


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第15张

15.考察逗号表达式。

(0,1)计算的结果是1,(2,3)计算结果是3,(4,5)计算结果是5

a初始化后的结果为:

1 2
5 0
0 0

a[0]就是第一行元素的地址,此时p就是指向第一行元素的地址,然后打印p[0],相当于找到了第一行第一个元素,打印结果为1


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第16张

16.(1)传参的时候有问题,b[10]实际上是不存在的。

(2)fun的参数部分,如果它是想对数组的a[3]进行修改,传参应该传b即可,因为b是数组名,数组名表示首元素地址,类型是char* ,想改变它的值,应该使用char*的指针来接收。

编译器会有一个警告,说类型不匹配,因为b[10]的类型是char,而函数参数类型是char*。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第17张

17.(1)在使用完pstu后没有置空,这是一个野指针问题。

(2)结构体的成员name也是一个指针,代码并没有给该指针申请空间,直接使用name会出现越界访问问题。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第18张

18,典型的递归问题,直接上答案:

0 
1 
2 
5 
10

《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第19张

19.

getchar函数的返回类型是一个int,但是c是一个char类型,将一个int类型放入char类型中会发生截断。‘

假如读取失败,getchar会返回EOF,EOF是一个宏,其本质是-1,

但有可能getchar读取失败返回的EOF不再-128~127这个范围内,导致if条件判断语句不进入,存在潜在的危险。


《c语言深度解剖》--一套非常经典的笔试题,在这里插入图片描述,第20张

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分,那实在是不应该,因为毕竟很多题是很简单的。

网友评论

搜索
最新文章
热门文章
热门标签
 
 梦见自己去相亲  梦见打死狗是什么预兆解梦  女人梦见抱别人的小孩拉稀