本书封皮如下:
摘抄如下:
p13:可移植的代码
一个严格遵循标准的程序应该是:
只使用已确定的特性
不突破任何由编译器实现的限制
不产生任何依赖由编译器定义或未确定的或未定义的特性的输出
p31:在C语言中,const关键字并不真正表示常量,如
const int two=2; switch(i) { case 1:xxx case two:xxx ... }
报错
p32:case标签后面不加break语句导致的fall through现象,switch语句的缺省行为在97%的情况下都是错误的
p38:当sizeof的操作数是个类型名时,两边必须加上括号,但操作数如果是变量则不必加括号,例如
sizeof a sizeof(int)
p68:可以把typedef看成是一种彻底的封装类型,它和宏的区别体现在两个方面:
1、可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做
#define peach int unsigned peach i; //正确 typedef int banana; unsigned banana i; //错误
2、在连续几个变量的声明中,用typedef定义的类型能够保证声明中所有的变量均为同一种类型,而用#define定义的类型则无法保证
#define int_ptr int * int_ptr chalk,cheese;
变成
int * chalk, cheese; //chalk是一个int的指针,cheese是一个int
使用typedef则没有这个问题
p87:定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化,但不能指望为浮点数之类的常量分配空间
char *p="breadfruit"; //正确 float *pip=3.141; //错误
p93:如果函数库的一份拷贝是可执行文件的物理组成部分,那么我们称为静态链接;如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称为动态链接
p102:Interpositioning就是通过编写与库函数同名的函数来取代该库函数的行为,但很容易发生自己代码中某个符号的定义取代函数中相同符号的意外。不仅你自己所进行的所有对该函数的调用将被自己版本的函数调用所取代,而且所有调用该库函数的系统调用也将用你的函数取而代之。
p127:如果想返回一个指向在函数内部定义的变量的指针时,要把那个变量声明为static,这样就能保证该变量被保存在数据段中而不是堆栈中。
p153: malloc和free--从堆中获得内存以及把内存返回给堆
brk和sbrk--调整数据段的大小至一个绝对值
p158-159:
总线错误:几乎都是由于未对齐的读或写引起的,之所以称为总线错误,是因为出现未对齐的内存访问请求时,被堵塞的组件就是地址总线,例如
union { char a[10]; int i; }u; int *p=(int *)&(u.a[1]); *p=17; //p中未对齐的地址会引起总线错误
段错误:由于内存管理单元的异常所致,该异常通常是由于解除引用一个未初始化或非法值的指针引起的,例如
int *p=0; *p=17; //段错误
p161:通常导致段错误的原因
1、解除引用一个包含非法值的指针
2、解除引用一个空指针
3、在未得到正确的权限时进行访问
4、用完了堆栈或堆空间
常见变成错误
1、坏指针错误:在指针赋值之前就用它来引用内存
2、改写错误:越过数组边界写入数据,在动态存储的内存两端之外写入数据,改写一些堆管理结构
3、指针释放引起的错误:释放同一个内存两次,释放一块未曾使用malloc分配的内存,释放仍在使用的内存,释放一个无效的指针
p200:什么时候数组和指针相同
数组声明:
extern,如extern char a[]; 不能改写成指针的形式
定义,如char a[10]; 不能改写成指针的形式
函数的参数,如func(char a[]); 你可以随自己喜欢,选择数组形式或指针形式
数组在表达式中使用:
如c=a[i];你可以随自己喜欢,选择数组形式或指针形式
p201:什么时候数组和指针是等同的:
1、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针
2、下标总是与指针的偏移量相同
3、在函数参数的声明中,数组名被编译器当作指向该数组的第一个元素的指针
p207:没有办法把数组本身传递给一个函数,因为它总是被自动转换为指向数组的指针
p209:数组和指针的可交换性总结:
1、用a[i]这样的形式对数组进行访问总是被编译器改写或解释成像*(a+i)这样的指针访问
2、指针始终就是指针,它绝不可以改写成数组
3、在特定的上下文中,也就是它作为函数的参数,一个数组的声明可与看作是一个指针。作为函数参数的数组始终会被编译器修改成为指向数组第一个元素的指针
4、当把一个数组定义为函数的参数时,可与选择把它定义为数组,也可以定义指针。不管选择哪种方法,在函数内部事实上获得的都是一个指针
5、在其他所有情况下,定义和声明必须匹配。如果定义了一个数组,在其他文件对它进行声明时也必须把他声明为数组,指针也是如此
p225:数组和指针参数被编译器修改
实参--------------------------------------所匹配的形参
数组的数组 char c[8][10]-----------------char(*)[10] 数组指针
指针数组 char *c[15]-------------------char **c 指针的指针
数组指针 char(*c)[64]-------------------char(*c)[64] 不改变
指针的指针 char **c----------------------char **c 不改变
p227:使用指针向函数传递一个多维数组
my_function(int my_array[10][20]) //限制较大 my_function(int my_array[][20]) //最右边一维长度必须是20 my_function(int (*my_array)[20]) my_function(char **my_array) //只有把二维数组改为一个指向向量的指针数组的前提下才可以这样做
p234:创建动态数组的基本思路使用malloc()库函数来得到一个指向一大块内存的指针,例如:
#include<stdlib.h> #include<stdio.h> int size; char *dynamic; char input[10]; size=atoi(fgets(input,7,stdin)); dynamic=(char *)malloc(size); dynamic[0]='a'; dynamic[size-1]='z';
p264:C++的一个简单子集
尽量使用的C++特性:
1、类
2、构造函数和析构函数,但只限于函数体非常简单的例子
3、重载,包括操作符重载和I/O
4、单重继承和多态
避免使用的C++特性:
1、模板
2、异常
3、虚基类
4、多重继承
p277:库函数调用和系统调用
函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分,系统调用是在操作系统内核发现一个trap或中断后进行的。库函数调用通常比行内展开的代码慢,因为它需要付出函数调用的额外开销,但系统调用比库函数调用还要慢很多,因为它需要上下文环境切换到内核模式。
p279:文件描述符和文件指针
所有操纵文件的系统调用都接受一个文件描述符作为参数,或者把它作为返回值返回
文件描述符就是开放文件的每个进程表的一个偏移量,用于unix系统调用中,用于标识文件
文件指针保存了一个文件结构的地址,文件结构用于表示开放的I/O流,用于ANSI C标准的I/O库调用中,用于标识文件
小结:
这本书从seven那里拿回来已经停留了很久,seven说这本书写的不错。如今总算把这本书看完,确实是有收获。不得不说,最近实习看程序书的时间并不像去年那么多了,估计大多花在看闲书上面了,争取这个所谓的暑假再看点程序书吧,不断学习才有进步。