c专家编程阅读笔记

1.警惕不同类型数据的隐式转换,尽量不使用unsigned类型。char short bitfield enum 在表达式中会自动转换为int,float自动转换为double,函数参数也是表达式,所以当参数传递给函数时也会发生类型转换。如果编译器能保证运算结果一致,也可以省略类型提升,常出现在表达式中存在常量操作数的时候。老式的编译器仅仅支持int double 指针三种参数类型,为了简化参数传递,所以常会发生参数类型转换。而ansi c标准支持多种类型参数,而且参数类型相同时不会发生类型转换。

2.对优先级和结合型不确定时,使用括号明确表示。

3.使用typedef 简化类型声明。对于复杂声明,从标识符开始逐层解释。

4.定义分配存储空间,而声明只是表明名称和类型,定义时特殊的声明。编译器为每个变量分配一个地址(左值),并将变量名与地址放入符号表中,该地址在编译时可知,而变量中的值(右值),只有在运行时才知道。

5.数组和指针是不同的,一个直接访问一个间接访问,指针变量值的访问多了一次取地址的操作。一个文件中定义全局变量char c[10]; 另一文件中声明extern char *c;是错误的。因为声明了c为字符指针,即会按照指针的方式去访问,首先从符号表中得到指针变量的地址,然后取出指针变量值(也就是所指向字符变量的地址)进行访问,而c定义为字符数组,取出的值实际为第一个字符,以此作为地址访问就出现错误。同样,若定义全局变量char *p;另一文件中声明extern char p[];进行访问也会出错。

6.静态链接:库函数包含在可执行文件中;动态链接:库函数在运行时被加载到内存,可执行文件中存有链接库的路径名和文件名。动态链接可以避免函数库或操作系统版本更新时重新链接程序,而且可以减小可执行文件大小,节省磁盘和内存,因其可为多个进程共享。

7.动态库名字为libname.so,通过编译时使用-lname 选项告诉编译器连接到libname.so。编译器选项-Lpathname和-Rpathname告诉连接器链接时和运行时分别到哪个目录中查找库文件。动态链接库文件中的所有符号在程序运行时都可见,而静态链接仅仅把用到的符号内容链接到可执行文件中。始终将-l函数库选项放在编译命令行的最右边。

8.小心interpositioning,自定义的标识符会遮蔽系统内部的标识符。

9.可执行文件a.out中包括a.out神奇数字、符号表等其他内容、bss段的大小(并不存储bss段)、数据段(初始化的全局变量和静态变量)、文本段(代码)。程序运行时文本段会被mmap系统调用直接映射到进程地址空间,bss段和数据段合并,统称为数据区,在进入进程地址空间后全部清零。进程地址空间中的最低部分未被映射,数据区和堆栈区中间是共享库的映射区域。

10.函数调用过程:1)实参按顺序压入栈中,2)保存返回地址eip到栈顶,call指令干的,3)保存ebp到栈顶,4)设置ebp的值为esp,5)保存寄存器内容,6)执行函数,7)恢复寄存器值,ebp->esp, pop ebp ,8)pop eip,esp+=参数个数*字长,ret指令干的。

11.cache使用的是虚地址,当进程上下文切换时,需要刷新其内容。cache中每行有行中块儿的虚拟块地址和数据块,块大小一般为32位。

12.动态申请的内存要记得释放,否则会造成内存泄露。malloc(size),free(ptr),calloc(n, size)分配n个size大小的空间并清零,alloca(size)分配的空间不需要手动释放,因为其实在栈中分配的,在返回的指针作用域结束后会被自动回收。

13.现代计算机架构都要求内存对齐,可以是一个原子数据项不会垮cache块或者页的边界。存取未对齐的数据会出现总线错误。而访问未映射内存地址则会出现段错误,释放一个内存块两次、释放非动态分配的内存、或者释放一个无效指针都会导致段错误。系统不支持在信号处理函数中调用库函数。

14.标准io默认每次读取一行字符,也就是需要回车把字符串输入。当需要每次读取一个字符时,可以使用system("stty raw");命令设置终端驱动模式为单字符模式,完毕之后再使用system("stty cooked");恢复终端驱动为行模式。也可以kbhit()非阻塞方法和轮询,kbhit()函数即刻返回当前可读的字符数,在头文件conio.h中声明。curses.h是字符的屏幕显示控制函数库。中断驱动的io,通过设置SIGPOOL信号处理函数,处理每个字符输入事件。记住,在信号处理函数中调用库函数的结果是未定义的。

15.指针和数组在定义和声明的时候不能混用,在表达式中和参数传递时可以通用。数组下标运算被编译器转换成指针运算,并自动调整步长为元素大小,因此处理一维数组时,指针和数组速度相当。下标操作符的两个操作数是可以互换位置的,但是实际很少用6[a]的形式,因为没什么意义。在函数内部不能通过sizeof获得参数数组到长度,因为传递的是指针,得到的是指针类型的大小。

16.数组作为参数时被转换成指针,因此不能获得其长度,需要使用约定来提示数组长度,如参数显式指定或者在传递数组的末尾使用特殊的结束标记。而对于多维数组作为形参,只有最高维的长度可以不给出,其他维的长度必须给出,把实参限制为除最左边一维外所有维都必须与形参匹配的数组。有了各维的长度信息,才可以对数组下标或者指针地址正确的操作,得到准确的地址。

17.c++当中字符常量的类型是char,但在c中它们的类型是int。也就是c++中sizeof ‘a' 结果是1。

你可能感兴趣的:(编程语言)