《c和指针》之总结 六
13、高级指针话题
如果声明得当,一个指针变量可以指向一个指针变量。和其他的指针变量一样,一个指向指针的指针在它使用之前必须进行初始化。为了取得目标对象,必须对指针的指针执行双重的间接访问操作。更多层的间接访问也是允许的(比如一个指向整型的指针的指针的指针),但他们与简单的指针相比用的少。你也可以创建指向函数和数组的指针,还可以创建包含这类指针的数组。 指针的指针 相当于二维数组……. 等。
在c语言中,声明是以推论的形式进行分析的。下面这个声明 int *a;
把表达式*a 声明为一个整数。你必须随之推断出a 是一个指向整型的指针。通过推论声明,阅读声明的规则和阅读表达式的规则一样了。
你可以使用函数指针来实现回调函数。一个指向回调函数的指针作为参数传递给另一个函数,后者使用这个指针调用回调函数。使用这种技巧,你可以创建通用型函数,用于执行普通的操作如在一个链表中查找。任何特定问题的某个实例的工作,如在链表中进行值的比较,由客户提供的回调函数执行。转移表也使用函数指针。转移表像switch 语句一样执行选择。转移表由一个函数指针数组组成(这些函数必须具有相同的原型)。函数通过下标选择某个指针,再通过指针调用对应的函数。你必须始终保证下标值处于适当的范围之内,因为在转移表中调试错误是非常困难的。
如果某个执行环境实现了命令行参数,这些参数是通过两个参数传递给main函数的。这两个形参通常称为argc 和argv 。argc是一个整数,用于表示参数的数量。argv 是一个指针,它指向一个序列的字符型指针。该序列中的每个指针指向一个命令行参数。该序列以一个NULL指针作为结束标志。其中第1个参数就是程序的名字。程序可以通过对argv 使用间接访问操作来访问命令行参数。
出现在表达式中的字符串常量的值是一个常量指针,它指向字符串的第1个字符。和数组名一样,你既可以用指针表达式也可以用下标来使用字符串常量。如:
“xyz”+1;表示指针指向字符串第2个字符y。
*“xyz”;表示值为 字符x。
“xyz”【2】;表示值为 字符z。
如果并非必要,避免使用多层间接访问。
把void * 强制转换为其他类型的指针时必须小心。把函数参数类型声明为void *,表示“一个指向未知类型的指针”
14、预处理器
编译一个c程序的第1个步骤就是对它进行处理。预处理共支持5个符号,它们在表14.1中描述
处理器符号
|
符号 |
样例值 |
含义 |
|
_FILE_ |
“name” |
进行编译的源文件名 |
|
_LINE_ |
25 |
文件当前行的行号 |
|
_DATE_ |
“Jan 31 |
文件编译的日期 |
|
_TIME_ |
“18:04: |
文件编译的时间 |
|
_STDC_ |
1 |
如果编译器遵循ANSI C,其值就为1,否则未定义 |
#define 指令把一个符号名与一个任意的字符序列联系在一起。例如,这些字符可能是一个字面值常量、表达式或者程序语句。这个序列到该行的末尾结束。如果该序列较长,可以把它分开数行,但在最后一行之外的每一行末尾加一个反斜杠。宏就是一个被定义的序列,它的参数值将被替换。当一个宏被调用时,它的每一个参数被一个具体的值替换。为了防止可能出现于表达式中的与宏有关的错误,在宏完整定义的两边应该加上括号。同样,在宏定义中每个参数的两边也要加上括号。#define指令可以用于“重写”C语言,使它看上去像是其他语言。
#argument 结构由预处理转换为字符串常量“argument”。##操作符用于把它两边的文本粘贴成同一个标识符。
有些任务既可以用宏也可以用函数实现。但是,宏与类型无关,这是一个优点。宏的执行速度快于函数,因为它不存在函数调用/返回的开销。但是,使用宏通常会增加程序的长度,但函数却不会。同样,具有副作用的参数可能在宏的使用过程中产生不可预料的结果,而函数参数的行为更容易预测。由于这些区别,使用一种命名约定,让程序员很容易地判断一个标识符是函数还是宏是非常重要的。
许多编译器中,符号可以从命名行定义。#undef 指令将导致一个名字的原来定义被忽略。
使用条件编译,你可以从一组单一的源文件创建程序的不同版本。#if 指令根据编译时测试的结果,包含或忽略一个序列的代码。当同时使用#elif 和#else 指令时,你可以从几个序列的代码中选择其中之一进行编译。除了测试常量表达式之外,这些指令还可以测试某个符号是否已被定义。#ifdef和#ifndef 指令也可以执行这个任务。
#include 指令用于实现文件包含。它具有两种形式。如果文件名位于一对尖括号中,编译器将在由编译器定义的标准位置查找这个文件。这种形式通常用于包含函数库头文件时。另一种形式,文件名出现在一对双引号内。不同的编译器可以用不同的方式处理这种形式。但是,如果用于处理本地头文件的任何特殊处理方法无法找到这个头文件,那么编译器接下来就使用标准查找过程来寻找它。这种形式通常用于包含你自己编写的头文件。文件包含可以嵌套,但很少需要进行超过一层或两层的文件包含嵌套。嵌套的包含文件将会增加多次包含同一个文件的危险,而且使我们更难以确定某个特定的源文件依赖的究竟是哪个文件。所以一般用
#ifndef _HEADERNME_H
#define _ HEADERNME_H 1 /*#define _ HEADERNME_H 效果一样虽然是空字符但这个符号仍然被定义*/
…….
#endif
在每个头文件中前后加入这预定义即可用来消除多重包含。
#error 指令在编译时产生一条错误信息,信息中包含的是你所选择的大小。#line 指令允许你告诉编译器下一行输入的行号,如果它加上了可选内容,它还将告诉编译器输入源文件的名字。因编译器而异的#progma 指令允许编译器提供不标准的处理过程,比如向一个函数插入内联的汇编代码。
在那些对表达式求值的宏中,每个宏参数出现的地方都应该加上括号,并且在整个宏定义的两边加上括号。

浙公网安备 33010602011771号