乐趣区

关于c:C进阶21宏定义与使用分析

Summary

1)#define 预处理器 解决的单元实体之一,在预编译期进行 文本替换

2)#define定义的宏能够呈现在程序的 任意中央,定义之后的代码都能够应用

3)#define能够定义宏常量,实质上是 字面量

4)define能够定义表达式,应用上 相似函数 ;性能 可能更弱小 (类型能够作为参数、求数组大小); 更容易出错(当和其余运算混合在一起时)

5)宏因为是间接文本替换,所以 没有任何的调用开销 ;宏表达式里 不能呈现递归 ;宏被预处理器解决,所以 编译器不晓得宏的存在 ,天然 宏也不会有作用域的概念 作用域是针对变量和函数的

6)罕用的预约义宏

含意 示例
__FILE__ 被编译的文件名 file1.c
__LINE__ 以后行号 25
__DATE__ 编译时的日期 Jan 31 2012
__TIME__ 编译时的工夫 17:01:01
__STDC__ 编译器是否遵循规范 C 标准 1

宏定义与应用剖析

#define 预处理器 解决的单元实体之一
#define 定义的宏能够呈现在程序的 任意地位
#define 定义之后的代码都能够应用这个宏

1、#define 定义的宏常量

#define定义的宏常量能够间接应用,实质为 字面量(不会占用内存,字符串字面量会存在只读存储区)

// test.c 中 上面的宏定义正确么?#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c

int main()
{
    int i = ERROR;
    char* p1 = PATH1;
    char* p2 = PATH2;
    char* p3 = PATH3;
    return 0;
}
单步编译
gcc -E test.c -o test.i    ==> 预编译,生成两头文件.i
gcc -S test.i -o test.s    ==> 编译,生成汇编文件.s

剖析:在第一步预编译阶段,解决 define 并生成.i 文件,这时候不会出错;下一步编译阶段,将两头文件转换为汇编文件时,就会报错。

起因在于:在 预编译的阶段,仅仅是做文本替换,没有语法查看 ;到了 编译阶段,会对替换后的.i 文件进行语法查看,再生成汇编文件,这时候语法查看就出错了。

2、#define 定义的表达式

#define表达式的应用 相似函数调用
#define 表达式能够 比函数更弱小
#define 表达式 比函数更易出错

#define SUM(a, b) (a) + (b)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DIM(a) sizeof(a) / sizeof(*a)

int main()
{
    int a = 1;
    int b = 3;
    int c[2] = {0};

    // expected:4,1,2
    printf("SUM(a, b) = %d\n", SUM(a, b));
    printf("MIN(a, b) = %d\n", MIN(a, b));
    printf("size of array = %d\n", DIM(c));

    // unexpected:3
    printf("unexpected: %d", MIN(++a, b));   // 冀望失去 ++ a 和 b 中的较小的值:2
    return 0;
}

剖析:

  • define 表达式 相似 函数调用,像下面的例子中,printf 中宏的定义就很像是函数调用。
  • define 表达式可能 比函数更弱小 ,如上,DIM 宏能够求数组的大小 然而在 C 语言中无奈通过函数求一个数组的大小 ,因为 当数组作为函数的参数时,会进化成指针 ;再比方,C 语言中 不能把类型作为参数,然而宏就能够。
  • define 表达式 更容易出错 ,如上,MIN(++a, b) 冀望失去的值为 2,理论失去的值却是 3。由单步编译失去的后果能够看出,替换后的表达式为 ((++a) < (b) ? (++b) : (b)),前置的 ++ 被执行了 2 次,所以失去了 3。 预编译器就像是一个传话筒,在传话的过程中,就产生了歧义

宏表达式被 预处理器 解决,编译器并不知道宏的存在;
宏表达式用“实参”齐全代替形参,不进行任何运算
宏表达式 没有任何的调用开销 :(因为间接进行文本替换,不像函数须要参数入栈、返回等开销)
宏表达式中不能呈现递归定义:(因为宏只在预处理期进行 一次 文本替换,后续的符号编译器就不意识了)

#define SUM(n) ((n>0) ? (SUM(n-1) + n) : 0)
int s = SUM(10);    // 编译的时候就会报错,undefined reference SUM

3、宏定义的常量或表达式是否有作用域限度?

上面的程序非法么?void def()
{
    #define PI 3.1415926
    #define AREA(r) r*r*PI
}
double area(double r)
{retrun AREA(r);
}

int main()
{double r = area(10);
    return 0;
}

剖析:编译、运行都不会出错。阐明 宏是没有作用域限度的 。只有定义完,前面的代码都能够用。在定义之前应用就会报错 undefined reference。 作用域只是针对变量和函数的 ,宏在预处理期被开展, 在编译期曾经没有宏这个货色了,所以也就没法给宏限定作用域了。

4、罕用的预约义宏

含意 示例
__FILE__ 被编译的文件名 file1.c
__LINE__ 以后行号 25
__DATE__ 编译时的日期 Jan 31 2012
__TIME__ 编译时的工夫 17:01:01
__STDC__ 编译器是否遵循规范 C 标准 1

本文总结自“狄泰软件学院”唐佐林老师《C 语言进阶课程》。
如有错漏之处,恳请斧正。

退出移动版