《C专家编程》这本书是自己认真读完的一本IT类的书籍,书中详细介绍了关于C语言的缺陷和优点,对于C语言的初学者有很大的帮助;并且本书的语言活泼生动,读起来没有技术类书籍的那种枯燥和艰涩;书中还提供了一些编程实例,供读者实践;确实是一本好书,看过的人都知道,呵呵。
现将书中对自己有帮助的内容总结如下:
1.理解C语言声明的优先级规则(本书第64页)
A 声明从它的名字开始读取,然后按照优先级顺序依次读取。
B 优先级从高到低依次是:
B1 声明中被括号括起来的那部分;
B2 后缀操作符:括号()表示这是一个函数,而方括号[]表示这是一个数组;
B3 前缀操作符:星号*表示“指向...的指针”
C 如果const和(或)volatile关键字的后面紧跟类型说明符(如int,long等),那么它作用于类型说明符,在其他情况下,const和(或)volatile关键字作用于 它左边紧邻的指针星号。
譬如,用优先级规则分析下面的C语言声明:
char * const *(*next)();
适用规则 | 解 释 |
A | 首先,看变量名“next”,并注意到它直接被括号括住 |
B1 | 所以先把括号里的东西作为一个整体,得出“next”是一个指向...的指针 |
B | 然后考虑括号外面的东西,在星号前缀和括号后缀之间作出选择 |
B2 | B2规则告诉我们优先级较高的是右边的函数括号,所以得出“next是一个函数指针,指向一个返回...的函数” |
B3 | 然后,处理前缀“*”,得出指针所指的内容 |
C | 最后,把“char * const”解释为指向字符的常量指针 |
把上述结果概括如下:这个声明表示“next是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为char的常量指针”。
2.数组和指针是如何访问的(本书第83页)
我们首先要注意“地址y”和“地址y的内容”之间的区别。这是一个相当微妙支出,因为在大多数编程语言中我们用同一个符号来表示这两样东西,由编译器根据上下文环境判断它的具体含义。以一个简单的赋值为例:
x = y;
在这个上下文环境里,符号相当含义是x所代表的地址 | 在这个上下文环境里,符号y的含义是y所代表的地址的内容 |
x被称为左值 | y被成为右值 |
左值在编译时可知,左值表示存储结果的地方 | 右值知道运行时才知。如无特别说明,右值表示“y的内容” |
实际上编译器为每个变量分配一个地址(左值) 。这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量并将它存于寄存器中。这里的关键之处在于每个符号的地址在编译时可知。所以,如果编译器需要一个地址(可能还需要加上偏移量)来执行某种操作,它就可以直接进行操作,并不需要增加指令首先取得具体的地址。相反,对于指针,必须首先在运行时取得它的当前值,然后才能对它进行解除引用操作(作为以后进行查找的步骤之一)。
譬如下述代码对数组下表的引用:
char a[9] = "abcd"; ... ... char c = a[i];
编译器符号表具有一个地址9980(实际上就是数组的首地址)
运行时步骤1:取i的值,将它与9980相加;
运行时步骤2:取地址(9980+i)的内容;
再譬如下述代码对于指针的引用:
char *p; ... ... c = *p;
编译器符号表有一个符号p,它的地址是4624
运行时步骤1:取地址4624的内容,就是“5081”
运行时步骤2:取地址5081的内容。
3.动态链接库与静态链接库的不同(本书第100页)
在动态连接中,所有的符号进入输出文件的虚拟地址空间中,所有的符号对于链接在一起的所有文件都是可见的。相反,对于静态链接,在处理archive(静态库)时,它只是在静态库中查找载入器当时所知道的未定义符号。
简而言之,在编译器命令行中各个静态链接库出现的顺序是非常重要的,因为符号是通过从左向右的顺序进行解析的。譬如相同的符号在两个不同的函数库中有不同的定义,静态库出现的顺序不同,其结果就有可能不同。另外,如果在自己的代码之前引入静态库,又会带来另一个问题。因为此时尚未出现未定义的符号,所以它不会从函数库中提取任何符号。接着,当目标文件被链接器处理时,它所有的对函数库的引用都是未实现的!这个问题最常出现于链接math库的时候。譬如,像下面这样进行静态链接:
cc -lm main.c
则会得到一条错误信息;为了能从math库中提取所需的符号,首先需要让文件包含未解析的引用,如下面所示:
cc main.c -lm
【备注】:始终将 -l 函数库选项放在编译命令的最右边是一个很好的习惯。
未完待续......