C与指针 13-15章

来源:互联网 发布:数字签名软件 编辑:程序博客网 时间:2024/06/06 01:12

第10章  高级指针话题

 

只有当确实需要时,才应该使用多层间接访问

13.2 高级声明

int f()[] 这个声明是非法的,函数只能返回标量值,不能返回数组

int f[]() 这个声明也是非法的,因为数组元素必须具有相同的长度,但不同的函数可能具有不同的长度

int (*f[])()  f肯定是一个数组,数组元素的类型是函数指针,它所指向的函数的返回值是一个整型值

int  *(*f[])()  这个声明创建了一个指针数组,指针所指向的类型是返回值为整数指针的函数

 

尽管原型增加了声明的复杂度,但我们还是应该提倡这种风格,因为它向编辑器提供了一些额外的信息

13.3 函数指针

最常用的两个用途是转换表和作为参数传递给另一个函数

 

警告:简单声明一个函数并不意味着它马上就可以使用。和其他函数一样,对函数指针执行间接访问之前必须把它初始化为指向某个函数

       

13.4 命令行参数

argv是一个指针,它指向一个序列的字符型指针,该序列中每个指针指向一个命令行参数。该序列以一个NUL指针作为结束标志

 

13.5字符串常量

当一个字符串出现在表达式中,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针

当数组名用于表达式中,它们的值也是指针常量

xyz+1  它的结果是个指针,指向字符串中第二个元素:

*(“xyz”+4) 偏移量4超出了这个字符串的范围,所以这个表达式的结果是一个不可预测的字符

 

如果程序的可读性大幅度下降,对于因此获得的执行速度的略微提高是得不偿失的

当你使用一种不寻常的语句或技巧时,确保增加一条注释,描述它的工作原理

 

13.6 总结

可以用指针表达式也可以用下标来表示字符串常量

如果并非必要,避免使用多层间接访问

 

 

第11章 预处理器

C预处理器在源代码编译之前对其进行一些文本性质的操作

主要任务包括删除注释,插入被include指令包含的文件的内容,定义和替换由#define指令定义的符号以及确定代码的部分内容是否应该根据一些条件编译指令进行执行

14.2 #define

#define name stuff

如果定义的stuff特别长,可以分成几行,除了最后一行以外,每行的末尾都要加一个反斜杠

 

可以使用#define指令把一系列语句插入到程序中

提示:不要滥用这种技巧,如果相同的代码需要出现在程序的几个地方,通常更好的方法是把它实现为一个函数

提示:所有用于对数值表达式进行求值的宏定义都应该加上括号,避免在使用宏时,由于参数中的操作符或临近的操作符之间不可预料的相互关系

 

宏参数和#define可以包含其他#define包含的符号,但是宏不可以出现递归

14.2.2.3宏与函数

例如:#define  MAX(a,b) ((a>b)?(a):(b))

函数的参数必须声明为一种特定的类型,所以它只能在类型合适的表达式上使用。反之,上面这个宏可以用于整型,浮点型,字符型及其他任何可以用>比较大小的类型。换句话说,宏是和类型无关的。

使用宏的不利在于,每次使用宏时,一份宏定义代码的拷贝都将插入到源程序。除非宏非常短,否则宏会大幅增加程序的长度

 

还有一些任务无法用函数实现,比如:

#define MALLOC(n,type) ((type*)malloc(n*sizeof(type)))

 

P=MALLOC(5,int);可以转换为

P=(int *)malloc(5*sizeof(int));

14.3.带副作用的宏参数

例如;#define  MAX(a,b) ((a>b)?(a):(b))

x=5,y=8;

z=MAX(x++,y++);

printf(“%d %d %d”,x,y,z);

结果为61010

因为z=(x++>y++)?x++:y++;

 

#undef用于移除一个宏定义

14.3 条件编译

 

14.4文件包含

编译器支持两种不同类型的#include文件包含:函数库文件和本地文件

如果可能,应避免多重包含,不管它是否由于嵌套的#include所致

 

14.5 其他指令

无效指令就是以一个#开头,后面不跟任何内容

#error在编译时产生一条错误信息,信息中包含的是你所选择的文本

因编译器而异的#progma指令允许编译器提供不标准的处理过程

 

14.6总结

由于宏与参数的区别,使用一种命名约定,让程序员很容易判断一个标识符是宏还是函数是非常重要的

文件包含可以嵌套,但很少需要进行超过一层或两层的文件包含嵌套

 

第12章 输入输出函数

15.1错误信息

perror简化向用户报告这些特定错误的过程

void perror(char *const message)

如果mssage不是NUL并且指向一个非空的字符串,函数就打印这个字符串,后面跟一个分号和空格;然后打印出解释当前错误代码的信息

 

15.2终止执行

exit函数结束时,程序已经结束,所以它无处可返

15.4

读取和写入实际是从一块被称为缓冲区的内存区域中来回复制数据

流分为两种类型:文本流和二进制流

应该始终检查fopen函数的返回值。如果函数失败,它将返回NUL

对于输出流,fclose 在文件关闭前刷新缓冲区。如果执行成功,返回零值,如果失败,返回EOF

15.12刷新和定位函数

fflush迫使一个输出流的缓冲区里的数据进行物理写入,不管它是否已经写满

ftell返回流的当前位置

fseek允许你在一个流中定位

15.13改变缓冲方式

setbuf设置另一个数组,用于对流进行缓冲

15.17总结

ferror,cleareer函数和流的错误状态有关

在可能出现错误的场合,检查并报告错误

使用scanf限定符提高可移植性

操纵文本行而无须顾及它们的外在表现形式有助于提高程序的可移植性

原创粉丝点击