总结序言:

学习大牛们的书籍,每次都是新潮澎湃,热血沸腾,看他们的书心中就有一种信任,有一种无比的膜拜。最初看到这本书的时候心情也是如此。Andy这个大牛我想大家也都如雷贯耳了吧.从图书馆找到他的书籍,我二话不说便细细的品味起来,希望从中能获得意想不到的东西.然而当我真正的深入阅读之后才发现原来一切都是那么常见的问题,一切都来源于实际的编程,并不是什么高深的东西。然而正是这些常见的细微的编程问题扰乱了我们的生活,也许曾多次深夜时分你还在为一个小小的bug而懊恼不已,只身忙碌于电脑旁,不肯入眠。但我相信如果你熟读此书并理解之后把它用于你的实际编程中之后,那么你这样的“懊恼”与“不必要的忙碌”会相应减少很多。愿每位C/C++程序员或爱好者都能熟读此书,你将从中获得意想不到的收获。即使有些东西你早已知道,再次甚至多次提醒一下自己也不为过。因为只要与正确的优雅的用于实际的编程之中还有很长的一段路要走,在这期间我们要做的就是不断的提醒自己与不断的思考改进。

第0章

看到的Andy的第一个C程序,有些失望,竟然是下面这种类型的:

main()

{

}

这中方式在旧的编译器上虽然能成功运行,但他确是大家极不推崇的一种方式。我们应该明确写出正确的函数返回类型.正确的写法应该是这样的:

int main()

{

return 0;

}

int main(int argc,char * argv[])

{

return 0;

}

目前只有这两种方式才是正确的主函数方式.

第二个代码:

int i,a[N];

for (i = 0; i <= N; i++)

{

a[i] = 0;

}

作者说这个程序会出现死循环,在此对此坐下解释:

理解这个结果(“死循环”),我们应该首先理解操作系统内存的分配方式.我们的内存是从高地址开始分配的。所对于变量i和数组a[N].在内存中的关系应该是这样的:

高地址

i

a[N-1]

a[N-2]

…..

……

a[4]

a[3]

a[2]

a[1]

a[0]

低地址

所以对于上面的程序我们遍历数组a[N]的时候,从a[0]开始,知道a[N].而我们的数组有效位置是从a[0]到a[N-1]的。对于a[N]已经不再属于数组的有效范围了。而在内存中a[N]的位置便是i的位置了。而我们每次遍历数组一遍都会将a[N]置0.即令i = 0;故就会陷入无限的死循环了。

而对于上述程序若我们稍作改动便会是另外一种结果了:

int a[N],i;

此时虽然我们改变的仅仅是变量定义的顺序,但是他们在内存中的位置关系就不同了,这样我们访问a[N]的时候就不会再访问到i了.这样就会出现未定义的结果了。不过一般情况下都会出现内存错误,即弹出一个对话框…..

本章习题:

0-1:

这题目开始我看了不禁迷惑起来,作者为啥出这题目呢。直到看了作者的解答我还是没有明白过来,之后深深思考之后才算略有所悟,作者是想通过这个问题让我们每个人都能站在客户的角度思索一下,返修率极高的产品或是bug极多的软件产品是不会受到用户的欢迎的,故我们在写程序的时候也应该时刻保持高质量的程序,这不但便于我们以后的调试,更加影响到最终产品的质量和公司的效益。

0-3:

看到这道题目,我原以为是一道发挥想象力的题目呢,所以就尽情的思索。我的回答是:

我烹饪时失手切伤过自己的手。我设想对菜刀的改进方法有两种:

①:改变刀刃的方向,即令刀刃稍微偏向远离手指的方向.

这种方法不但令菜刀变得难于使用,而且也没有彻底的解决问题,因为有些人还是会因此切伤手指的。

②:我设想在刀刃外面加一个框架,就好似弹簧刀那样的,刀刃可以随我们的控制进出和固定。然后在刀刃上安装一个温度传感器,当刀刃接触到的物体温度为37°C左右的时候,刀刃就自动进入框架。

这个设计必然令一个简单的菜刀繁琐起来,并且还增加了它的成本,不免是一种“画蛇添足”的行为,而且对技术的要求极高。

当看过作者的解答之后我才顿悟过来,原来作者是想通过这道题来提醒我们,有些东西的缺点正是他的优点。若C是一门简洁的语言,易学,但是很多东西都需要我们自己去写,远没有JAVA,C#,Delphi开发来的便宜。可是他简单易用,灵活效率。我们不能为了改变它的缺点,从而去掉他的优点。这反而失去他的本性了。万事万物的存在都有他存在的道理。我们不应该轻易去否定它的价值。

第1章

1.2  & 和 | 不同于 && 和 ||

开始的时候自己根本分不清这两种符号。直到今天才算明白:

& 和 | 是位运算符。

&& 和 || 是逻辑运算符.

如:

int a,b;

a = 2;

b = 3;

a & b; 和 a | b;是对a,b的位操作.

如a的二进制为: 0010 b的二进制为: 0011;

故:

0010

a & b = =  & 0011  = =  0010 = = 2;

0010

a | b = =  |  0011  = =  0011 = = 3;

而,a && b, a || b;都是对a,b的逻辑操作.

即: a && b由于a,b均为非0.故a && b = = 1: a || b = =1;

1.3 词法分析中的”贪心法”

以前只对这种方式有所了解,根本不知道有这个名字。现终于明白啦..O(∩_∩)O

C语言中的某些符号,例如/、*、=,只有一个字符长,称为单字符符号,而C语言中的其他符号,例如/*、==,以及标识符,包括了多个字符,称为多字符符号。当C语言编译器读入一个字符'/'后又跟了一个字符'*',那么编译器就必须做出判断:是将其作为两个分别的符号对待,还是后起来作为一个符号对待。C语言对这个问题的解决方案可以归纳为一个很简单的规则:每一个符号应包含尽可能多的字符。即编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分:如果可能,继续读入下一个字符符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。这个策略有时被称为“贪心法”。

借此趁着这个对这个“贪心法”的理解解释一下习题:

1-3:

n-->0的含义.

首先编译器读进一个字符n,而后接着读进‘-’经判断他是一个运算符,而后接着读下一个,又读进一个‘-’。此时’—‘能够组成一个新的运算符,编译器继续读取字符,又读进一个‘>‘,此时’- ->’便不再是一个运算符了。n-->0便被解释成为n-- > 0;了。

1-4:

a+++++b的含义是什么?

这个按照上面的方法逐个字符的分析便可得知含义为:

a++ + ++b;

第2章

2.1 理解函数声明

这里仅仅理解一下作者给出的一个语句:

(*(void(*)())0)();

我们从最内部开始分析,并逐步扩展:

void(*)();

为了便于分析我们定义一个变量来帮助分析:

typedef void (*PFUN)();

PFUN p;

这个是一个函数指针,它指向返回类型为void,空参数的函数.

(void(*)())0;

根据上一步的变量定义,上面语句就可以转换成:

(p)0;

再进一步扩展:

(*((void)(*)())0)();

这一步是对用函数指针对函数进行调用:

我们用上面的变量进行替换一下就明了了:

(*(p)0)();

这就很清楚明了了。

这里我获得了一个知识点,以前我本以为用函数指针对函数进行调用直接用函数指针加上括号就OK了。如:

typedef void (*PFUN)();

PFUN p;

若有一个这样的函数:

void visit();

p = visit;

我们现在就可以这样调用了.

p();

我原以为就应该这样调用。现在我才明白,其实我们也可以这样调用:

(*p)();并且我原以为的调用p();只是(*p)();的简化调用。

第3章:

3.1指针和数组

int a[12][31];

一个二维数组:

看似很简单的一个二维数组,现在我们分析一下它所包含的知识:

① :a是什么类型?

很多同学对此很迷糊,有的甚至就直接说,他不就是一个二维数组名吗?看似很简单。其实a是一个指向一个维数为31的一维数组指针.类型是这样的:

typedef int (*p)[31];

②:我们若通过指针遍历此二维数组该如何遍历呢?我们该如何定义指针呢?

有两种方法:

1:定义一个int型指针

int *p = &a[0][0];定义一个指针指向二维数组的第一个元素的指针.而后以此遍历即可.

如:

int a[2][3] = {{1,2,3},{4,5,6}};

int *p;

for (p = &a[0][0]; p != &a[1][3]; ++p)

{

printf("%d ",*p);

}

printf("/n");

这里说一下,我代码里用了&a[1][3]做结束标志.很多同学可能对此不解:为什么不用&a[2][0]呢?呵呵,其实这样&a[1][3]和&a[2][0]是一样的。他们指向同一个内存地址.

int (*ap)[31];

此时ap是一个指向有31个元素的一维数组的指针.这就引来了我们的第二种方法。

2:定义一个一维数组指针.

int (*p)[3];

代码如下:

int a[2][3] = {{1,2,3},{4,5,6}};

int (*p)[3];

int i;

p = a;

for (i = 0; i < 2; ++i)

{

int *q;

for (q = p[0]; q != p[0]+3; ++q)

{

printf("%d ",*q);

}

++p;

}

printf("/n");

呵呵,这个相比上面那个代码麻烦了许多。不过也确是一种方法;

第4章

4.3;

这一节只要明白static类型的变量默认为文件内变量,即从其他文件中无法访问的变量。同样函数也是如此。

这一章的学习还让我联想到printf函数的一种是用方式;

printf("hello,world",INT_MAX);

这种方式虽然是无聊的,无用的。但是它却可以正确的运行。

第6章

6.1不能忽视宏定义中的空格

宏定义中的空格往往会引起本质的变化,我们定义的时候千万要小心,不该加空格的地方千万不要加。

如:

#define f (x) ((x)-1)

本来我们可能是想把 f(x) 用((x)-1)替换,现在到弄巧成拙的让f 被(x) ((x)-1)替换了。

6.2宏不是函数

①我们最好在宏定义中把每个参数都用括号括起来、

②整个结果表达式也应该用括号括起来

另宏也不是语句,不是类型,它仅仅是简简单单的替换.

如:#define FOOTYPE struct foo;

这样题目中所有的FOOTYPE 都会被struct foo替换;

如:我们程序中有这样的定义:

FOOTYPE a;

FOOTYPE b,c;

将会被替换成为:

struct foo a;

struct foo b,c;

可以看出没什么问题。好像运行的也很好。我们再看另一种情况:

#define T1 struct foo *

若我们这样定义:

T1 a,b;

将会被替换成:

struct foo *a,b;

这样的结果便是,我们的a变量是一个指针,b变量是一个结构体类型。完全违背了我们的原意。所以说宏不是类型,它仅仅是简单的替换,我们若是想定义类型的话还是应该用typedef.

第7章

移位运算的速度远快于除法运算。

附录A:

简单的格式类型:

%u 输出无符号十进制数

%o 打印八进制数

%x 打印十六进制数,格式项字符用小写字母

%X 打印十六进制数,格式项用大小字母

%s 打印字符串

printf(s);

printf(“%s”,s);

这两个式子的区别:

在打印普通字符串时,两者效果相同,但是第一个若要想打印回车符的话必须再写一句话。

在打印含有格式符的字符串时,第一个将会事与愿违,因为他后面没有参数,产生的字符串中将会产生乱码。

%g,%f ,%e用于打印浮点数。

%g 打印出的数值会去掉该数值尾缀的0,保留6位有效数字。需包含math.h头文件,但是如果一个数的位数大于6位,即绝对值大于999999。%g会按照科学计数法打印这样的数值,科学计数法中的小数部分仍保留6位有效数字。

%f打印出来的额数字总会保留6位小数,不管是否是0.

%e,一律使用科学计数法打印,但是它是小数点后保留6位有效数字。

%E,和%G和%e,%g一样,只不过他们用E替换了e.

修饰符

%% 打印出一个%。

%p 用于打印指针

%n 用于打印已打印的字符.

%#o 打印结果前加0

%#x 打印结果前加 0x

%#X 打印结果前加 0X

%+d 0和整数前加+

m.n

对于 %d,%o,%x,%u,精度修饰符指定了打印数字的最少位数,

如:%.nd

如果待打印的数值并不需要这么多位数的数字来表示,就会在他前面补0,若是整数修饰,

如:%md

输出的宽度应为m列,若是输出的字符比列数小,就左补空格。

对于%e,%E,%f。精度修饰符指定了小数点后应该出现的数字位数。仅当精度大于0时打印的数值中才会实际出现小数。

对于%g,%G.精度修饰符指定了打印数值的有效数字位数。

C陷阱与缺陷学习总结相关推荐

  1. C陷阱与缺陷学习笔记

    导读 程序是由符号(token)序列所组成的,将程序分解成符号的过程,成为"词法分析". 符号构成更大的单元--语句和声明,语法细节最终决定了语义. 词法陷阱 符号(token)指 ...

  2. 《C陷阱与缺陷》学习笔记

    第一章 词法陷阱 笔记本:<C陷阱与缺陷> 创建时间:2018/4/23 22:06:21                                                  ...

  3. 《C陷阱与缺陷》学习笔记(2):作者有话说

    道阻且长,行则将至.埋头苦干,不鸣则已,一鸣惊人!加油,骚年! 1 参考资料 1.书本参考资料 <C陷阱与缺陷>前言: 2.网络参考资料 [维基百科]Andrew Koenig:https ...

  4. 【2018深信服 醒狮计划】《C陷阱与缺陷》学习笔记

    2018深信服"醒狮计划"笔记 先自我介绍一下,湖大研一计算机的菜鸡,本科网络工程的,大学里不务正业一直在做应用,大一自学过一段时间的MFC,Windows网络编程,感觉比控制台好 ...

  5. 《C陷阱与缺陷》和《C专家编程》两本书又翻印了

    今天花了几个小时逛书店,偶然让我发现了<C陷阱与缺陷>和<C专家编程>这两本书.这让我很惊喜,喜欢收集书的我当然不会错过,一口气,两本书都收录了. 其实这两本我都已经下载了电子 ...

  6. 【知识点总结】-《C陷阱与缺陷》

    目录 第一章:词法的陷阱 1.1."="与"=="不同: 1.3词法分析的"贪心法": 1.4整型常量: 1.5字符与字符串: 第二章:语法 ...

  7. C 语言陷阱和缺陷(一)

    CSDN话题挑战赛第2期 参赛话题:学习笔记 文章目录 前言 简介 第一部分:词法缺陷 1. 1 = 不是 == 1.2 多字符记号 1.3 多字符记号一些例外说明 1.4 字符串和字符 第二部分:句 ...

  8. 《C陷阱与缺陷》一导读

    前 言 C陷阱与缺陷 对于经验丰富的行家而言,得心应手的工具在初学时的困难程度往往要超过那些容易上手的工具.刚刚接触飞机驾驶的学员,初航时总是谨小慎微,只敢沿着海岸线来回飞行,等他们稍有经验就会明白这 ...

  9. 《Java解惑》陷阱和缺陷的目录

    陷阱和缺陷的目录 一.词汇问题 1.字母l在许多字体中都与数字1相像. 2.负的十六进制字面常量看起来像是正的. 3.八进制字面常量与十进制字面常量相像. 4.ASCII字符的Unicode转义字符容 ...

最新文章

  1. 标准化工作取得新突破 窄带物联网商用指日可待
  2. 迎接奥运会 里约把机场的IT建设翻新了下
  3. 【原】让H5页面适配移动设备全家 - 设计师篇 - PPT
  4. ”这个动作需要从没有授权的软件源来安装软件包“解决办法
  5. nyoj116士兵杀敌2
  6. OpenCV离散傅立叶变换
  7. 一些SAP德国总部的照片
  8. camel mq_Camel:构建基于消息的应用程序
  9. 【转】C#与C++的发展历程第一 - 由C#3.0起
  10. mysql 计算近30天总金额_MySQL数分实战:咖啡店精细化运营
  11. 宽度自适应实现方法(转)
  12. 一个游戏大量合服代表什么_一个女人哭了代表什么?这几点帮你分析
  13. 免费盈利模式是骗人的吗?
  14. shell编写一键安装mysql.sh
  15. DBCP,C3P0,druid,HiKariCP连接池配置使用
  16. 行编辑器c语言,行编辑器——C语言.doc
  17. 在ASP.NET的服务器端使用message box(Message box Server side in ASP.Net)
  18. 苹果Mac上好用的分屏软件:Magnet
  19. 数据结构编程题及解析c语言版,数据结构习题集答案(C语言版).pdf_c语言数据结构题目,c语言数据结构答案-C/C++文档类资源...
  20. 真正的人工智能能实现吗_如何实现真正的人工智能

热门文章

  1. 一个三线小镇的数字生活
  2. Day3-《青春有你2》选手数据分析
  3. UX设计师 | 该怎么向父母解释自己的工作
  4. Android 手机会中病毒,危险性高 每20部安卓手机就有1部被root
  5. Google透过机器学习 侦测与分类手机装置上的威胁
  6. 七周学会数据分析|良心教程 第三周
  7. OBS远程控制开发记录
  8. sp工具中最疼的是_阴阳师SP酒吞就业面详解 高级PVE新工具人 食发鬼:别说了太难了...
  9. 我们不该是这样的结局
  10. 关于端口被占用的问题(以61440端口为例)