关于C语言编程的四部经典之作

电子说

1.2w人已加入

描述

很多朋友问我学了很久的C语言,可是看一些稍微大一点的代码还是有一些懵圈,自己写一些程序也是漏洞百出,该如何得到提升呢?

“ 有什么好的书籍、资料推荐吗?”

“ 你平时都看哪些关于C语言编程的书籍 ? ”

“ 有一本叫XXX的书怎么样 ? 要不要买一本学习一下 ? ”

。。.。。.

每次遇到这些读者提问,我都不知道从哪个层面跟他谈这个话题,索性今天就从C语言学习这一块开始吧。

对于C语言的学习与进阶,这“四大名著”如果不提及实在是不太专业。它们都一把年纪了,这一方面也说明C语言经久不衰,另一方面证明确实都是经典之作。

重要的是bug菌对这几本书平均都有1~2遍的阅读,更是深有体会,至今留在身边的书籍中就有它们的身影~

第一本经典之作,是还在读书期间导师推荐给我的,《C专家编程》:

目录 · · · · · ·第1章 C:穿越时空的迷雾

1.1 C语言的史前阶段

1.2 C语言的早期体验

1.3 标准I/O库和C预处理器

1.4 K&R C

1.5 今日之ANSI C

1.6 它很棒,但它符合标准吗

1.7 编译限制

1.8 ANSI C标准的结构

1.9 阅读ANSI C标准,寻找乐趣和裨益

1.10 “安静的改变”究竟有多少安静

1.11 轻松一下——由编译器定义的Pragmas效果

第2章 这不是Bug,而是语言特性

2.1 这关语言特性何事,在Fortran里这就是Bug呀

2.2 多做之过

2.3 误做之过

2.4 少做之过

2.5 轻松一下——有些特性确实就是Bug

2.6 参考文献

第3章 分析C语言的声明

3.1 只有编译器才会喜欢的语法

3.2 声明是如何形成的

3.3 优先级规则

3.4 通过图表分析C语言的声明

3.5 typedef可以成为你的朋友

3.6 typedef int x[10]和#define x int[10]的区别

3.7 typedef struct foo{ 。。. foo; }的含义

3.8 理解所有分析过程的代码段

3.9 轻松一下——驱动物理实体的软件

第4章 令人震惊的事实:数组和指针并不相同

4.1 数组并非指针

4.2 我的代码为什么无法运行

4.3 什么是声明,什么是定义

4.4 使声明与定义相匹配

4.5 数组和指针的其他区别

4.6 轻松一下——回文的乐趣

第5章 对链接的思考

5.1 函数库、链接和载入

5.2 动态链接的优点

5.3 函数库链接的5个特殊秘密

5.4 警惕Interpositioning

5.5 产生链接器报告文件

5.6 轻松一下——看看谁在说话:挑战Turing测验

第6章 运动的诗章:运行时数据结构

6.1 a.out及其传说

6.2 段

6.3 操作系统在a.out文件里干了些什么

6.4 C语言运行时系统在a.out里干了些什么

6.5 当函数被调用时发生了什么:过程活动记录

6.6 auto和static关键字

6.7 控制线程

6.8 setjmp和longjmp

6.9 UNIX中的堆栈段

6.10 MS-DOS中的堆栈段

6.11 有用的C语言工具

6.12 轻松一下——卡耐基-梅隆大学的编程难题

6.13 只适用于高级学员阅读的材料

第7章 对内存的思考

7.1 Intel 80x86系列

7.2 Intel 80x86内存模型以及它的工作原理

7.3 虚拟内存

7.4 Cache存储器

7.5 数据段和堆

7.6 内存泄漏

7.7 总线错误

7.8 轻松一下——“Thing King”和“页面游戏”

第8章 为什么程序员无法分清万圣节和圣诞节

8.1 Portzebie度量衡系统

8.2 根据位模式构筑图形

8.3 在等待时类型发生了变化

8.4 原型之痛

8.5 原型在什么地方会失败

8.6 不需要按回车键就能得到一个字符

8.7 用C语言实现有限状态机

8.8 软件比硬件更困难

8.9 如何进行强制类型转换,为何要进行类型强制转换

8.10 轻松一下——国际C语言混乱代码大赛

第9章 再论数组

9.1 什么时候数组与指针相同

9.2 为什么会发生混淆

9.3 为什么C语言把数组形参当作指针

9.4 数组片段的下标

9.5 数组和指针可交换性的总结

9.6 C语言的多维数组

9.7 轻松一下——软件/硬件平衡

第10章 再论指针

10.1 多维数组的内存布局

10.2 指针数组就是Iliffe向量

10.3 在锯齿状数组上使用指针

10.4 向函数传递一个一维数组

10.5 使用指针向函数传递一个多维数组

10.6 使用指针从函数返回一个数组

10.7 使用指针创建和使用动态数组

10.8 轻松一下——程序检验的限制

第11章 你懂得C,所以C++不在话下

11.1 初识OOP

11.2 抽象——取事物的本质特性

11.3 封装——把相关的类型、数据和函数组合在一起

11.4 展示一些类——用户定义类型享有和预定义类型一样的权限

11.5 访问控制

11.6 声明

11.7 如何调用成员函数

11.8 继承——复用已经定义的操作

11.9 多重继承——从两个或更多的基类派生

11.10 重载——作用于不同类型的同一操作具有相同的名字

11.11 C++如何进行操作符重载

11.12 C++的输入/输出(I/O)

11.13 多态——运行时绑定

11.14 解释

11.15 C++如何表现多态

11.16 新奇玩意——多态

11.17 C++的其他要点

11.18 如果我的目标是那里,我不会从这里起步

11.19 它或许过于复杂,但却是惟一可行的方案

11.20 轻松一下——死亡计算机协会

11.21 更多阅读材料

附录A 程序员工作面试的秘密

附录B 术语表

一听名字就感觉这书比较高大上,看完就是C语言“砖家”了,太形象生动,书的目录展示了此书的全貌,你会发现处处都是你感兴趣的,当时买来一本天天啃,越看越觉得自己无知。

看完这本书,你会对C语言不再停留在简单的语法上,会以更深入的视角去欣赏它,回头想想这本书应该完完整整看过两遍吧~

第二本《C和指针》

目录 · · · · · ·第1章 快速上手

1.1 简介

1.1.1 空白和注释

1.1.2 预处理指令

1.1.3 main函数

1.1.4 read_column_numbers函数

1.1.5 rearrange函数

1.2 补充说明

1.3 编译

1.4 总结

1.5 警告的总结

1.6 编程提示的总结

1.7 问题

1.8 编程练习

第2章 基本概念

2.1 环境

2.1.1 翻译

2.1.2 执行

2.2 词法规则

2.2.1 字符

2.2.2 注释

2.2.3 自由形式的源代码

2.2.4 标识符

2.2.5 程序的形式

2.3 程序风格

2.4 总结

2.5 警告的总结

2.6 编程提示的总结

2.7 问题

2.8 编程练习

第3章 数据

第4章 语句

第5章 操作符和表达式

第6章 指针

第7章 函数

第8章 数组

第9章 字符串、字符和字节

第10章 结构和联合

第11章 动态内存分配

第12章 使用结构和指针

第13章 高级指针话题

第14章 预处理器

第15章 输入/输出函数

第16章 标准函数库

第17章 经典抽象数据类型

第18章 运行时环境

附录 部分问题答案

索引

参考文献

与这本书的经历真的是跌宕起伏,最早结实这本书是在刚毕业那家公司的书架上,估计被部门其他大佬翻阅过了无数次,书非常的破,甚至胶装都要掉了,于是在公司有事没事就读着,确实是本好书,后面就自己买了一本新的。当时的我学得非常的认真,在上面做了很多的笔记。不幸的是,就在前年搬家的过程中给弄丢了,这比丢钱包还心痛呀,希望他被同行的朋友捡到,日子过得不要太差就好,千万别进废品站~后来因为这本书的内容也挺熟了,就没有再买新的,实在想翻就拿电子档看看,不过还是比较费眼睛。第三本《C陷阱与缺陷》

目录 · · · · · ·第0章 导读

第1章 词法“陷阱”

1.1 =不同于==

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

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

1.4 整型常量

1.5 字符与字符串

第2章 语法“陷阱”

2.1 理解函数声明

2.2 运算符的优先级问题

2.3 注意作为语句结束标志的分号

2.4 switch语句

2.5 函数调用

2.6 “悬挂”else引发的问题

第3章 语义“陷阱”

3.1 指针与数组

3.2 非数组的指针

3.3 作为参数的数组声明

3.4 避免“举隅法”

3.5 空指针并非空字符串

3.6 边界计算与不对称边界

3.7 求值顺序

3.8 运算符&&、|| 和 !

3.9 整数溢出

3.10 为函数main提供返回值

第4章 连接

4.1 什么是连接器

4.2 声明与定义

4.3 命名冲突与static修饰符

4.4 形参、实参与返回值

4.5 检查外部类型

4.6 头文件

第5章 库函数

5.1 返回整数的getchar函数

5.2 更新顺序文件

5.3 缓冲输出与内存分配

5.4 使用errno检测错误

5.5 库函数signal

第6章 预处理器

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

6.2 宏并不是函数

6.3 宏并不是语句

6.4 宏并不是类型定义

第7章 可移植性缺陷

7.1 应对C语言标准变更

7.2 标识符名称的限制

7.3 整数的大小

7.4 字符是有符号整数还是无符号整数

7.5 移位运算符

7.6 内存位置0

7.7 除法运算时发生的截断

7.8 随机数的大小

7.9 大小写转换

7.10 首先释放,然后重新分配

7.11 可移植性问题的一个例子

第8章 建议与答案

8.1 建议

8.2 答案

附录A PRINTF,VARARGS与STDARG

附录B Koenig和Moo夫妇访谈

bug菌和一些朋友们都叫他“避坑指南”,也是公认的好书,第一次看完基本上你会处于一种不敢写代码的状态,害怕自己稍不留神就写了个bug。不过好在这本书比较轻薄,也不记得看过多少遍,自从接触到这本书以后就开始关注和记录平时学习和开发中容易出问题的编程点,必须一读~最后一本面试经典《你必须知道的495个C语言问题》

目录。。.。。.第1章 声明和初始化

基本类型

1.1 我该如何决定使用哪种整数类型?

1.2 为什么不精确定义标准类型的大小?

1.3 因为C语言没有精确定义类型的大小,所以我一般都用typedef定义int16和int32。然后根据实际的机器环境把它们定义为int、short、long等类型。这样看来,所有的问题都解决了,是吗?

1.4 新的64位机上的64位类型是什么样的?

指针声明

1.5 这样的声明有什么问题?char *p1, p2; 我在使用p2的时候报错了。

1.6 我想声明一个指针,并为它分配一些空间,但却不行。这样的代码有什么问题?char *p; *p=malloc(10);

声明风格

1.7 怎样声明和定义全局变量和函数最好?

1.8 如何在C中实现不透明(抽象)数据类型?

1.9 如何生成“半全局变量”,就是那种只能被部分源文件中的部分函数访问的变量?

存储类型

1.10 同一个静态(static)函数或变量的所有声明都必须包含static存储类型吗?

1.11 extern在函数声明中是什么意思?

1.12 关键字auto到底有什么用途?

类型定义(typedef)

1.13 对于用户定义类型,typedef 和#define有什么区别?

1.14 我似乎不能成功定义一个链表。我试过typedef struct{char *item; NODEPTR next;}* NODEPTR; 但是编译器报了错误信息。难道在C语言中结构不能包含指向自己的指针吗?

1.15 如何定义一对相互引用的结构?

1.16 Struct{ } x1;和typedef struct{ } x2; 这两个声明有什么区别?

1.17 “typedef int(*funcptr)();”是什么意思?

const 限定词

1.18 我有这样一组声明:typedef char *charp; const charp p; 为什么是p而不是它指向的字符为const?

1.19 为什么不能像下面这样在初始式和数组维度值中使用const值?const int n=5; int a[n];

1.20 const char *p、char const *p和char *const p有什么区别?

复杂的声明

1.21 怎样建立和理解非常复杂的声明?例如定义一个包含N个指向返回指向字符的指针的函数的指针的数组?

1.22 如何声明返回指向同类型函数的指针的函数?我在设计一个状态机,用函数表示每种状态,每个函数都会返回一个指向下一个状态的函数的指针。可我找不到任何方法来声明这样的函数——感觉我需要一个返回指针的函数,返回的指针指向的又是返回指针的函数,如此往复,以至无穷。

数组大小

1.23 能否声明和传入数组大小一致的局部数组,或者由其他参数指定大小的参数数组?

1.24 我在一个文件中定义了一个extern数组,然后在另一个文件中使用,为什么sizeof取不到数组的大小?

声明问题

1.25 函数只定义了一次,调用了一次,但编译器提示非法重声明了。

1.26 main的正确定义是什么?void main正确吗?

1.27 我的编译器总在报函数原型不匹配的错误,可我觉得没什么问题。这是为什么?

1.28 文件中的第一个声明就报出奇怪的语法错误,可我看没什么问题。这是为什么?

1.29 为什么我的编译器不允许我定义大数组,如double array[256][256]?

命名空间

1.30 如何判断哪些标识符可以使用,哪些被保留了?

初始化

1.31 对于没有显式初始化的变量的初始值可以作怎样的假定?如果一个全局变量初始值为“零”,它可否作为空指针或浮点零?

1.32 下面的代码为什么不能编译?intf(){char a[]=“Hello, world!”;}

1.33 下面的初始化有什么问题?编译器提示“invalid initializers ”或其他信息。char *p=malloc(10);

1.34 char a[]= “string literal”;和char *p=“string literal”; 初始化有什么区别?当我向p[i] 赋值的时候,我的程序崩溃了。

1.35 char a{[3]}= “abc”; 是否合法?

1.36 我总算弄清楚函数指针的声明方法了,但怎样才能初始化呢?

1.37 能够初始化联合吗?

第2章 结构、联合和枚举

结构声明

2.1 struct x1{ };和typedef struct{ }x2; 有什么不同?

2.2 这样的代码为什么不对?struct x{ }; x thestruct;

2.3 结构可以包含指向自己的指针吗?

2.4 在C语言中用什么方法实现抽象数据类型最好?

2.5 在C语言中是否有模拟继承等面向对象程序设计特性的好方法?

2.6 为什么声明extern f(struct x *p); 给我报了一个晦涩难懂的警告信息?

2.7 我遇到这样声明结构的代码:struct name {int namelen; char namestr[1];};然后又使用一些内存分配技巧使namestr数组用起来好像有多个元素,namelen记录了元素个数。它是怎样工作的?这样是合法的和可移植的吗?

2.8 我听说结构可以赋给变量也可以对函数传入和传出。为什么K&R1却明确说明不能这样做?

2.9 为什么不能用内建的==和!=操作符比较结构?

2.10 结构传递和返回是如何实现的?

2.11 如何向接受结构参数的函数传入常量值?怎样创建无名的中间的常量结构值?

2.12 怎样从/向数据文件读/写结构?

结构填充

2.13 为什么我的编译器在结构中留下了空洞?这导致空间浪费而且无法与外部数据文件进行“二进制”读写。能否关掉填充,或者控制结构域的对齐方式?

2.14 为什么sizeof返回的值大于结构大小的期望值,是不是尾部有填充?

2.15 如何确定域在结构中的字节偏移量?

2.16 怎样在运行时用名字访问结构中的域?

2.17 C语言中有和Pascal的with等价的语句吗?

2.18 既然数组名可以用作数组的基地址,为什么对结构不能这样?

2.19 程序运行正确,但退出时却“core dump ”(核心转储)了,怎么回事?

联合

2.20 结构和联合有什么区别?

2.21 有办法初始化联合吗?

2.22 有没有一种自动方法来跟踪联合的哪个域在使用?

枚举

2.23 枚举和一组预处理的#define有什么不同?

2.24 枚举可移植吗?

2.25 有什么显示枚举值符号的容易方法吗?

位域

2.26 一些结构声明中的这些冒号和数字是什么意思?

2.27 为什么人们那么喜欢用显式的掩码和位操作而不直接声明位域?

第3章 表达式

求值顺序

3.1 为什么这样的代码不行?a[i]= i++;

3.2 使用我的编译器,下面的代码int i= 7; printf(“%d ”, i++ * i++); 打印出49。不管按什么顺序计算,难道不该是56吗?

3.3 对于代码int i=3; i=i++; 不同编译器给出不同的i值,有的为3,有的为4,哪个是正确的?

3.4 有这样一个巧妙的表达式:a^= b^= a^= b; 它不需要临时变量就可以交换a和b的值。

3.5 可否用显式括号来强制执行我所需要的计算顺序并控制相关的副作用?就算括号不行,操作符优先级是否能够控制计算顺序呢?

3.6 可是&&和||操作符呢?我看到过类似while((c = getchar()) != EOF && c != )的代码

3.7 是否可以安全地认为,一旦&&和||左边的表达式已经决定了整个表达式的结果,则右边的表达式不会被求值?

3.8 为什么表达式printf(“%d %d”, f1(), f2()); 先调用了f2?我觉得逗号表达式应该确保从左到右的求值顺序。

3.9 怎样才能理解复杂表达式并避免写出未定义的表达式?“序列点”是什么?

3.10 在a[i] = i++;中,如果不关心a[]的哪一个分量会被写入,这段代码就没有问题,i也的确会增加1,对吗?

3.11 人们总是说i=i++的行为是未定义的。可我刚刚在一个ANSI编译器上尝试过,其结果正如我所期望的。

3.12 我不想学习那些复杂的规则,怎样才能避免这些未定义的求值顺序问题呢?

其他的表达式问题

3.13 ++i和i++有什么区别?

3.14 如果我不使用表达式的值,那我应该用i++还是++i来做自增呢?

3.15 我要检查一个数是不是在另外两个数之间,为什么if(a b c)不行?

3.16 为什么如下的代码不对?int a=1000, b=1000; long int c=a * b;

3.17 为什么下面的代码总是给出0?double degC, degF; degC= 5.0 / 9 * (degF - 32);

3.18 需要根据条件把一个复杂的表达式赋给两个变量中的一个。可以用下面这样的代码吗?((condition) ?a :b)= complicated_expression;

3.19 我有些代码包含这样的表达式。a ?b=c :d 有些编译器可以接受,有些却不能。为什么?

保护规则

3.20 “semantics of‘’change in ANSI C”的警告是什么意思?

3.21 “无符号保护”和“值保护”规则的区别在哪里?

第4章 指针

第5章 空指针

第6章 数组和指针

第7章 内存分配

第8章 字符和字符串

第9章 布尔表达式和变量

第10章 C预处理器

第11章 ANSI/ISO标准C

第12章 标准输入输出库

第13章 库函数

第14章 浮点运算

第15章 可变参数列表

第16 章 奇怪的问题

第17章 风格

第18章 工具和资源

第19章 系统依赖

第20章 杂项

术语表

参考文献

仔细看看这么详细的目录,估计没几个问题可以自信的给出答案吧,没错公司HR要研发部这边出一套软件笔试题目,基本那些C语言题目都会从这本书中抽取。

不过本书不建议大家集中时间认真的去读,而是抽点时间逐个攻破,相信会有不少收获,也更有看下去的欲望~

我手头目前就这几本有关C语言的书籍了,完全足够日后的开发所需,虽然现在市面上又涌现了诸多教材,不过都有很高的相识度,甚至很多都是东拼西凑,知识的连贯性不够~

当然这4本书籍我也不推荐大家马上就去购买,搞技术的嘛,还是要多理性一点,毕竟我觉得不错的,不一定是你适合的。

可以找一些电子档,先阅读一下相应的章节是不是真的感兴趣,书籍作者的风格是不是真的符合你的口味。不然买了不看也是放在家里吃灰占地方,甚至有些朋友天天面对着一大堆还没来得及看的书,会有一种深深的罪恶感,最终失去学习的动力。

“书不在多、新、厚,有神则明”~

来源:最后一个bug,作者:bug菌

版权归原作者所有,如有侵权,请联系删除。

编辑:jq

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分