1.下面的代码输出是什么?为什么?

void foo(void)
{unsigned int a = 6; int b = -20;(a + b>6)? puts(">6"):puts("<=6");
}

答案:输出  >6
原因:当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
6的补码:00000000 00000000 00000000 00000110
-20补码:11111111 11111111 11111111 11110010
相加后按照无符号形式输出:4294967282 >6。


2.下面的代码有什么问题?为什么?

void foo(void)
{char string[10],str1[10]; int i;for(i=0; i<10; i++) str1[i] = ’a’;strcpy(string , str1); printf(“%s”,string);
}

答案:程序运行崩溃
原因:strcpy在运行时只有碰到‘\0’时才会停下来,而字符数组string在初始化时末尾并没有‘\0’,所以在拷贝时会一直向后找‘\0’,直至访问越界。


3.下面的代码,i 和 j 的值分别是什么?为什么?

static int j;
int k=0;void fun1(void)
{static int i=0;i++;
}void fun2(void)
{j=0;j++;
}int main()
{for(k=0;k<10;k++){fun1();fun2();}return 0;
}

答案:i = 10;j = 1;
原因: i  声明在函数内部是局部变量,保存在内存栈当中。当它被关键字static修饰后,便保存在静态区且它的值只会初始化一次。当局部变量被static修饰后改变为静态变量,它的生存期变长,但作用域变。
    j 声明在所有的函数之外,是全局变量。 当全局变量被static修饰后,变量的作用域变窄(只在当前文件中有效),且生命周期不变 (所以每被调用一次就会被初始化一次)
   注意:函数形参不允许被static声明
   另外,当函数被static修饰后改变的是函数的链接属性使其成为静态函数,函数的作用域仅局限于本文件(所以又称内部函数)。


4.下面代码里,假设在 32 位系统下,各 sizeof 计算的结果分别是多少?

int *p=NULL;
//sizeof(p)的值是       ------>4 p是一个指针变量,占四个字节
//sizeof(*p)的值是      ------>46
// sizeof括号内部的内容不参加运算,所以并不会计算*p是什么,由于p是int*类型的所以返回4
int a[100];
//sizeof(a)的值是       ------->400  数组名单独出现在sizeof中表示整个数组
//sizeof(a[100])的值是  ------->4
//  sizeof括号内的表达式并不进行计算,只是计算类型的空间占用量,(a[])
//sizeof(*(a+100))的值是------->4  因为a是int型数组,这里并不计算a+100指向哪里
//sizeof(&a)的值是      ------->4  地址的内存占用量在32位机器下永远是4个字节(64位下为8字节)
//sizeof(&a[0])的值是   ------->4   同上int b[100];
void fun(int b[100])
{sizeof(b);
}
//sizeof(b)的值是   ------->4函数传参是只是传入一个地址,这里是求的是地址大小

5.下面代码的结果是多少?为什么?

int main()
{char a[1000]; int i;for(i=0; i<1000; i++) a[i] = -1-i;printf("%d",strlen(a)); return 0;
}

   答案:255
   原因:strlen函数统计元素个数的过程相当于一个找‘\0’的过程,只有在找到‘\0’后才会停止++,从而得到元素的个数。所以本题也就是一个在数组中找0的过程(‘\0’的ASCII码值为0)。根据循环初始化数组中的内容应该是:-1,-2,-3…..-128,127,126,125…….2,1,0所以一共有225个元素。对于为什么-128再往后是127可见下图表示:


6.下面的代码里,哪些内容可被改写,哪些不可被改写?

1.const int *p;
2.int const *p;
3.int *const p;
4.const int * const p;

   答案:1.*p不能改,p可以改
         2.*p不能改,p可以改
         3.p不能改,*p可以改
         4.*p和p都不能改
   判别方法: 先忽略类型名,(编译器解析的时候也是忽略类型名),const 离哪个近就修饰谁
1.const int *p;  去掉int —–>  const *p   const 修饰*p,  p 是指针,可变*p 是指针指向的对象,不可变。
2.int const *p;  去掉int —–>  const *p   const 修饰*p,  p 是指针,可变*p 是指针指向的对象,不可变。
3.int *const p;  去掉int —–> *const p   const 修饰 p,p 不可变,p 指向的对象可变。
4.const int * const p;  去掉int —–> const *const p 前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象都不可变。


7.下面的两段代码有什么区别?什么时候需要使用代码(2)

//代码1:
int i = 10;
int j = i;//---------(1)
int k = i;//---------(2)//代码2:
volatile int i=10;
int j = i;//---------(1)
int k = i;//---------(2)

    在代码1后两条语句中,i 没有被用作左值。这时候编译器认为 i 的值没有发生改变,所以在(1)语句时从内存中取出 i 的值赋给 j 之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给 k 赋值。编译器不会生成出汇编代码重新从内存里取 i 的值,这样提高了效率。但要注意:(1)、(2)语句之间 i 没有被用作左值才行。
    volatile 关键字告诉编译器 i 是随时可能发生变化的,每次使用它的时候必须从内存中取出 i,的值,因而编译器生成的汇编代码会重新从 i 的地址处读取数据放在 k 中。
    volatile 可以保证对特殊地址的稳定访问


8. 在 32 位的 x86 系统下,输出的值为多少?

#include<stdio.h>
int main()
{int a[5] = {1,2,3,4,5};int *ptr1 = (int *)(&a + 1); int *ptr2 = (int *)((int )a + 1);printf("%x,%x",ptr1[-1], *ptr2); return 0;
}

答案:5,2000000


9.0x01<<2+3 的值为多少?为什么?

    答案:0x20(十进制32)
    原因:因为加号运算符优先级高于右移运算符,相当于0x01右移五位,即2的5次方。
    需要注意:左移和右移的位数不能超过数据的长度,也不能小于0


10.定义一个函数宏,求x的平方。

    答案: #define SQR(x) ((x)*(x))
   需要注意:宏的本质是字符串的替换,所以为了避免出现优先级问题一定要加括号保证运算的正确性。
   宏函数与函数相比较:

属性 define宏 函数
代码长度 每次使用时,宏代码被插入到程序当中,除非宏内容很短否组会大幅增加代码量 函数代码只保存在一个地方,每次使用都调用那个位置的同一份函数代码
执行速度 更快 存在函数调用返回的额外开销
操作符优先级 宏参数设计时为避免临近操作符因优先级产生的不可预料的结果应该加上括号 函数参数只在调用时求值一次,其结果传递给函数。求值结果更容易预测
参数求值 参数每次用于宏定义时都会重新求值,由于多次求值带有副作用的参数可能会产生不可预料的结果 函数在被调用之前求值一次,多次使用参数也不会导致多种求职过程,带有副作用的参数不会产生特殊影响
参数类型 宏与类型无关,只要对参数的操作是合法的,他可以使用任何参数类型 函数参数与类型有关,如果类型不同就需要使用不同的函数

11.下面的两段代码有什么区别?

//代码1:
struct TestStruct1{char c1; short s; char c2; int i;
};//代码2:
struct TestStruct2{char c1; char c2; short s; int i;
};

答案:二者空间占用量不同。代码1占12byte,代码2占8byte

   考察知识点:结构体内存对齐

   结构体内存对齐规则:

  • 结构体内的第一个成员在在结构体变量偏移量为0的地址处。
  • 其他成员变量要对齐到对齐数的整数倍地址处
    对齐数 = min( 编译器默认对齐数,成员大小 );windows环境下为4,Linux环境下为8
  • 结构体总大小为最大对齐数的整数倍(每个成员都有自己的对齐数)
  • 如果有嵌套结构体的情况,嵌套结构体对齐到自己的最大对齐数的整数倍处,结构体整体的大小就是所有最大对齐数的整数倍(包括被嵌套的结构体)

总结起来:存在内存对齐完全是一种用空间换时间的行为,如果既想要运行效率又要避免内存浪费最好的方法就是尽可能的把空间占用量小的成员放在一起。

offsetof宏函数可用于计算结构体成员相对于结构体的偏移量


12.写代码向内存 0x12ff7c 地址上存入一个整形数 0x100。

  答案:

int *p = (int *)0x12ff7c;
*p = 0x100;
// 或者*(int*)0x12ff7c = 0x100;

  原因:向一块内存中写入一个整型变量就要保证指向这块空间的指针是int型指针,若要写入一个char字符就可以将指针设置为char*类型。


13.下面的代码输出是多少?

main()
{int a[5]={1,2,3,4,5};int *ptr = (int *) (&a + 1); printf(“%d,%d”,*(a+1), *(ptr-1));
}

  答案:(a+1) = 2,(ptr-1) = 5


14.假设 p 的值为 0x100000,如下表达式的值分别为多少?

struct Test
{int Num;char *pcName; short sDate; char cha[2]; short sBa[4];
}*p;
//p+0x1 = ?
//(unsigned long)p+0x1 = ?
//(unsigned int *)p+0x1 = ?

  答案:
  p+0x1 = 00000014
  (unsigned long)p+0x1 = 00000001
  (unsigned int *)p+0x1 = 00000004
  原因:根据内存对齐(同11题)可计算出结构体的内存占用量为20字节,指针变量p是指向整个结构体的,即p的内存偏移量为20,所以p+0x1用16进制表示即为00000014。同理可得unsigned int * 的内存偏移量为4,所以(unsigned int * )p+0x1 = 00000004。(unsigned long)p是将地址 0x100000以无符号的长整型读出,+1就只是在原本值上+1即可,所以(unsigned long)p+0x1 = 00000001。


15.下面代码输出地结果是多少?

#include<stdio.h>
int main(int argc,char * argv[])
{int a[3][2]={(0,1),(2,3),(4,5)}; int *p;p=a[0]; printf("%d",p[0]);
}

  答案:1
  原因:由于二维数组是由一维数组组成的,所以在对二维数组进行初始化时花括号内嵌套的必须是花括号。若是在花括号内用圆括号“( )”进行初始化,且其中含有逗号,编译器会将其按照逗号表达式进行处理。所以数组a中真正定义的内容是int a[3][2]={1,3,5};


16.下面的代码有什么问题?为什么?

void fun(char a[10])
{char c = a[3];
}
int main()
{char b[10] = “abcdefg”; fun(b[10]);return 0;
}

  答案:一维数组传参错误,a[10]表示的是数组的内容(越界)而非char*形参所需的类型。系统虽然传入不会报错但是会有警告。
  分析:C语言中,当一维数组做函数参数时,编译器总是把它解析成一个指向其首元素的指针,所以函数实际传递的数组大小与函数形参指定的数组大小没有关系。
  一维数组传参的两种形式:

  • void fun( char a[] )
  • void fun( char* a )

17.下面的代码有什么问题?为什么?


struct student
{char *name; int score;
}*pstu;
int main()
{pstu = (struct student *)malloc(sizeof(struct student)); strcpy(pstu->name, “Jimy”);pstu->score = 99;free(pstu);return 0;
}

  答案:运行错误:
  error7.exe 中的 0x5b33f689 (msvcr90d.dll) 处未处理的异常: 0xC0000005: 写入位置 0xcdcdcdcd 时发生访问冲突。
  在定义结构体变量 stu 时,对于结构体内部 char *name 成员只是给 name 这个指针变量本身分配了 4 个字节。name 指针并没有指向一个合法的地址,这时候其内部存的只是一些乱码。所以在调用 strcpy 函数时,会将字符串”Jimy”往乱码所指的内存上拷贝,而这块内存 name 指针根本就无权访问,导致出错。解决的办法是为 name 指针 malloc 一块空间。
  在为结构体指针分配内存时是从外向里,即先分配结构体的指针再分配成员指针.释放时从里向外,先释放成员指针再释放结构体指针。

  修改:

int main()
{    pstu = (struct student *)malloc(sizeof(struct student));    pstu->name = (char *)malloc(20);    strcpy(pstu->name,"Jimy");    pstu->score = 99;  free(pstu->name); //free()函数并不能将指针指向空,在free掉之后应该手动将开辟的空间指针指向空。pstu->name = NULL;  free(pstu);    pstu = NULL;     return 0;
}

18.下面的代码输出结果是多少?

void fun(int i)
{if( i > 0 ) fun(i/2);printf("%d\n",i);
}
int main()
{fun(10);return 0;
}

  答案:

  原因:这是一个简单的递归函数,printf函数在递归之后,所以每一层递归返回都会调用一次printf函数。


19.下面的代码有什么问题?为什么?

char c;
c = getchar();
if(EOF == c)
{

}
  答案:由于getchar函数的返回值是int型,如果用char类型进行接收的话可能会发生信息遗漏。
  关于getchar():
  返回值:getchar函数的返回值是用户输入的第一个字符的ASCII码,当出错时返回-1。并且将用户输入的字符回显到屏幕。
  如果用户在按回车之前输入了不止一个字符那么其他字符会保留在键盘缓存区中,等待后续getchar调用读取。(这也是为什么有的main函数中的最后一句会是getchar()的原因)也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后才等待用户按键。


20.请写一个C函数,若当前系统是Big_endian的,则返回0;若是little_endian的,则返回1。

本题的意思即写一个小函数测试数据在当前编译器下是“大端”存储还是“小端”存储

  • 大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中
  • 小端模式:指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,
//方法一
int main()
{int a = 1;int ret = *(char*)&a;printf("%d",ret);//输出为1为小端,0则为大端return 0;}
//方法二
int checkSystem()
{union check{int i;char ch;}c;c.i = 1;return (c.ch == 1);
}//输出为1为小端,0则为大端

以上内容均为自行学习总结内容,如有错误欢迎批评指正。

《C语言深度解剖》20道习题解析相关推荐

  1. C语言深度解剖读书笔记

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  2. c语言深度解剖 pdf,c语言深度解剖(解密).pdf.pdf

    c语言深度解剖(解密).pdf.pdf 还剩 130页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保! 内容要点: * Str ...

  3. C语言深度解剖读书笔记(1.关键字的秘密)

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  4. C语言深度解剖:关键字

    第一个C语言程序 内存 定义与声明 变量是什么 为什么要定义变量 定义变量的本质 定义声明 关键字 - auto 局部与全局变量 作用域 vs 生命周期 auto 关键字 - register 寄存器 ...

  5. 《C语言深度解剖》中的.c/.h 程序模板及函数注释风格

    编程规范和变量命令规范对于代码的可阅读性.可维护性有着很大的影响.编程规范有很多,大公司也会制定自己公司的编程规范,如<华为技术有限公司c语言编程规范>等.对于个人编程来说没必要将自己编写 ...

  6. C语言 | C语言深度解剖 ——章节2 符号

    C语言 | C语言深度解剖 --章节2 符号 C语言基本符号表 注释符号 // /* 几个似非而是的注释问题 y=x/*p 出色注释的基本要求 连接符和转义符 \ 单引号.双引号 花括号 运算符 10 ...

  7. C语言深度解剖 PDF 分享

    链接:https://pan.baidu.com/s/1U-8L7ZY5_sLpk4p0J302Mg           提取码:d8nu 相关推荐 [Objective-c程序设计] 中文编程·学习 ...

  8. 实验 c语言作用域实验,关于《C语言深度解剖》一些例程的实验和感悟——关键字static...

    最经买了一本好书--<C语言深度解剖>,书比较薄,看的比较快,而且也非常地吸引人,对于其中的一些讲解和例子都非常的经典,感觉有一些新的感悟.可能我的想法和水平非常菜,但是我只想是写点东西出 ...

  9. 《C语言深度解剖 》陈正冲

    网上的书评(说该书很好)几乎都是在扯淡. 该书死钻C语言中一些稀奇古怪的东西,不适合入门,书没有吹的那么好. 无聊的时候可以看看,略作消遣. 作者的口头禅是"很遗憾"," ...

最新文章

  1. webp-imageio 如何编译及使用
  2. 关于零拷贝的一点认识
  3. Android studio 关于SQlite数据库导出,创建数据库、表以及如何查看操作
  4. By.css 的级联读取
  5. Ajax — 大事件项目(第三天)
  6. mysql数据库工程师网易微专业_网易MySQL数据库工程师微专业学习笔记(五)
  7. 电子报账系统源码_网上商城系统建设心得,轻松搞定选择困难
  8. C++ 11 nullptr关键字
  9. Vue computed参数与各生命周期关系(主要是异步的时候)
  10. TaskAttempt killed because it ran on unusable node IP:8041 Container released on a *lost* node
  11. html转义成velocity,改造Velocity模板引擎让$[!]{}输出默认进行html转义,并增加$#{}语法支持不转义输出...
  12. 探究 Spring 的定时任务配置
  13. 表单元素值获取方式js及java方式
  14. eclipse中怎么复制错误提示
  15. wordpress创建_如何创建WordPress儿童主题
  16. 寻访x86处理器“实模式”和“保护模式”的前世今生
  17. MySQL回滚日志(undo log)总结
  18. java学生课程设计报告_学生信息管理系统java课程设计报告
  19. c语言城市交通灯优化,城市智能交通灯系统(本科)毕业论文.doc
  20. 描述性统计分析案例题_SPSSAU描述性分析指标如何选择?

热门文章

  1. 你为什么有那么多时间写博客?
  2. HP 6930P 改安装windows XP
  3. 安卓Imageview控件如何获取网络图片
  4. 这个低代码生成器开源了!
  5. The hyperlink for cell A352 references relation rId1, but that didn‘t exist!
  6. 【拆机】触摸感应LED台灯
  7. 李德仁院士:数字孪生助推我国智能交通、数字交通快速发展
  8. 微信网页开发--仿美团、饿了么红包分享
  9. 【First-order Methods】 8 Primal and Dual Projected Subgradient Methods
  10. Android-拍照后使用OpenCV进行图像模糊度检测