C和指针读书笔记(第十四章)

1. #define的用法
#define指令把一个符号名与任意的一个字符序列联系在一起

#define name stuff;
//每当有符号name出现在这条指令之后,预处理器就会把它替换成stuff
#define reg register;
//为关键字register声明一个简短的别名
#define do_forever for( ; ; );
//声明一个更具描述性的符号来代替无限循环的for语句类型
#define CASE break;case
//用于switch中,自动把一个break放在每个case之前
//如果定义的stuff很长,可以分成几行,出最后一行外,每一行的末尾都要有"/"
#define DEBUG_PRINT printf(..., \
                            ..., \
                            ...)

2.
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者定义宏

#define SQUARE1(x) x * x;
SQUARE1(5)  //被替换为5*5,结果为25
a = 5;
SQUARE1(a + 1)  //被替换为a+1*a+1,结果是11
#define SQUARE2(x) (x)*(x);
SQUARE2(a + 1);  //被替换为(a+1)*(a+1),结果是36
#define DOUBLE1(x) (x) + (x);
a = 5;
10 * DOUBLE1(5);  //被替换为10*(a)+(a),结果是55
#define DOUBLE2(x) ((x) + (x));  //被替换为10*((a)+(a)),结果是100

3. 宏不可以出现递归
##结构把位于它两边的符号连接成一个符号,并允许宏定义从分离的文本片段创建标识符
假设程序中已经定义了这样一个带参数的宏:

#define paster(n) 
printf("token"#n" = %d", token##n)   
//同时又定义了一个整形变量:   
int token9 = 9; 
//现在在主程序中以下面的方式调用这个宏:   
paster(9);   
//那么在编译时,上面的这句话被扩展为:   
printf("token" "9" " = %d", token9);   

注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9,而#n也被”9”所替代。
可想而知,上面程序运行的结果就是在屏幕上打印出token9=9


4. 副作用就是表达式求值时出现的永久性效果

x + 1;  //可以重复执行,每次执行的结果都相同
x++;  //每次x的值都会增加1,重复执行结果不同
getchar();  //也有副作用,这个函数被调用将会取走一个字符,后续再调用得到的是不同的字符

5. 宏和函数的区别
在使用宏和函数时语法相同,为了区别开来,采用命名约定,把宏的名字全部大写。
宏和函数的不同之处
宏和函数的不同之处


6.
#undef 移除一个宏定义
#undef name 如果一个现存的名字需要重新定义,那么它的旧定义首先必须要用#undef移除。


7. 使用条件表已可以选择代码的一部分被正常编译还是完全忽略。用于支持条件编译的基本结构是#if指令和与其匹配的#endif指令。

#if constant-expression
    statements;
#endif

constant-expression(常量表达式)由预处理器进行求值,如果它的值是非零值(真),那么statements就被正常编译。

所谓常量表达式,就是说它或者是字面值常量或者是一个#define定义的符号。如果变量在执行期无法获得它的值,那么它们如果出现在常量表达式中就是非法的,因为其值在编译时不可预测。

if DEBUG
    printf("x = %d, y = %d\n", x, y);
#endif
如果想编译 #define DEBUG 1
//如果要忽略,将DEBUG定义为0

条件编译的另一个用途是在编译时选择不同的代码部分。为了支持这个功能,#if指令还具有可选的#elif和#else子句。

#if constant-expression
    statements
#elif constant-expression  //出现次数不限
    other statements
#else 
    other statements
#endif

测试一个符号是否被定义(四种方法)

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

嵌套指令

#if defined(OS_UNIX)
    #ifdef OPTION1
           unix_option1;
    #endif
    #ifdef OPTION2
           unix_option2;
    #endif
#elif defined(OS_MSDOS)
    #ifdef OPTION2
           msdos_option2;
    #endif
#endif
//为了阅读方便,可以为#endif添加注释标签,如下
#endif /* OPTION1 */

8. #include指令使另一个文件的内同被编译,就像它实际出现于#include指令出现的位置一样,这种替换执行的方式很简单:预处理器删除这条指令并用包文件的内容取而代之。这样,一个头文件如果被包含到10个源文件中,它实际被编译了10次。
当头文件被包含时,位于头文件内的所有内容都要背编译。这意味着每个头文件只应该包含一组函数或数据的声明,和把一个程序需要的所有声明都放入一个巨大的头文件相比,使用几个头文件,每个头文件包含用于某个特定函数或模块的声明的做法更好一些。
程序设计和模块化的原则也支持这种方法,只把必要的声明包含于一个文件中这种做法更好一些,这样文件中的语句就不会意外的访问应该属于私有的函数或变量。


9. 函数库文件包含
函数库头文件包含
编译器在由编译器定义的标准位置下搜索查找这个文件

#include <filename>

本地文件包含
先在源文件所在的当前目录下搜索,再像查找函数库头文件一样在标准位置查找本地头文件

#include "filename"

还可以使用绝对路径:UNIX中使用斜杠,MS_DOS中使用反斜杠
home/fh/proj/dec.h(UNIX)
嵌套文件包含
在一个将被其他文件包含的文件中使用#include指令是正常的。
例如函数的原型被放入头文件中,并用#include指令包含到需要使用这些函数的源文件中,但是每个使用I/O函数的文件必须同时包含stdio.h,因此包含函数原型的头文件中也可能包含一条#include

#include "a.h"
#include "b.h"
//若a.h和b.h中都出现了c.h,那么事实上出现了头文件的重复引入

要解决这个问题可以使用条件编,头文件可以如下编写

#ifdef HEADERNAME_H
#define HEADERNAME_H 1
//也可以写作#define HEADERNAME_H ,尽管现在是空字符串而不是1,但这个符号仍被定义
/*
**头文件中的具体代码
*/
#endif

那么多重半酣即被消除,当头文件第1次被包含时,它被正常处理,符号HEADERNAME_H被定义为1,如果头文件再次被包含,通过条件编译,它的所有内容被忽略。符号HEADERNAME_H按照被包含文件的文件名进行取名,以避免由于其他头文件使用相同的符号因此冲突。


2016.10.16

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值