宏定义笔记

来源:互联网 发布:淘宝能买到氰化钾吗 编辑:程序博客网 时间:2024/06/05 13:25

宏定义有两种:简单宏定义,带参数的宏定义。
简单宏定义:#define 标识符 替换列表
#define为常量命名的优点:
1. 程序会更易读;
2. 程序更易于修改;
3. 可以帮助避免前后不一致或键盘输入错误;
其他应用:
1. 可以对C语法做小的修改:

#define BEGIN {    // 指令总在第一个换行符结束,除换行符之前加 \#define END      }  // 之间的空格符是可以任意的

甚至可以定义: #define LOOP for( ;; )

2.对类型重命名:#define BOOL int
但是,通常使用typedef 对类型重命名
3.控制条件编译:

带参数的宏定义 #define 标识符(x1,x2,x3,...,xn) 替换列表
如:

#define MAX(x,y) ((x)>(y) ? (x) :(y))//替换列表中的参数都使用()括起来,可避免之间优先级不同而出错

可以包含空的参数列表: #define getchar() getc(stdin)
优点:
程序可能会稍微快些。一个函数调用在执行时通常有些额外开销—–储存上下文信息,复制参数的值等。而宏的调用没有。
宏会更”通用”。 与函数的参数不同,宏的参数类型没有类型。因此,只要预处理后的程序合法,宏可以接收任何类型的参数。
缺点:
编译后代码通常会增加。
宏参数没有类型检查。预处理器不会检查宏参数类型,也不会进行类型转换
无法用一个指针来指向一个宏。宏在预处理的过程中被删除,所以不存在类似的“指向宏的指针”。
宏可能会不止一次地计算它的参数。如果参数有附加作用,多次计算参数的值可能会产生意外的结果。例如:
n = MAX(i++,j);
n = ((i++)>(j)?(i++):(j));
如果i>j, 那么i可能会被计算两次,结果n被赋错误值。
因此,为避免这种情况,最好不使用带有附加作用的参数。

#运算符

将一个宏的参数转换为字符串字量。它仅允许出现在带参数的宏的替换列表中。

例如:
#define PRINT_INT(x) printf(#x " = %d\n", x)
x之前的#通知预处理器根据 PRINT_INT 的参数创建一个字符串字面量。因此,调用 PRINT_INT(i/j);会变成printf("i/j"" = %d\n",i/j); 等价于 printf("i/j = %d\n",i/j);

##运算符

##运算符可以将两个记号连在一起,成为一个记号。例如,#define MK_ID(n) i##n
int MK_ID(1),MK_ID(2),MK_ID(3); 等价于int i1,i2,i3;

##用于同一功能,不同数据类型的函数的宏定义,如:

#define GENERIC_MAX(type)         \type type##_max(type x, type y)   \{                                 \      return x>y?x:y;             \}

GENERIC_MAX(float) 等价于 float float_max(float x,float y) {return x>y?x:y;}

逗号运算符

在创建较长的宏时, 逗号号运算符很有用。可以用逗号运算符使替换列表包含一系列表达式,如:
#define ECHO(s) (gets(s), puts(s))
而,如果想在宏的替换列表中包含一系列语句,可以用do{...}while(0),如:

#define ECHO(s)     \     do {           \       gets(s);     \       puts(s);     \}while(0)

注意:在使用ECHO宏时,一定要加分号:ECHO(str);

预定义宏

名字 描述 _LINE_ 被编译文件行数 _FILE_ 被编译文件名字 _DATE_ 编译的日期(m d y) _TIME_ 编译时间(h:m:s) _STDC_ 如果编译器接受标准C,那么值为1

其中,_LINE__STDC_是整型常量,其他3个宏是字符串字面量。
_DATE__TIME_指明程序编译时间:

printf("Linux (c) 2017 King Software,Inc.\n");printf("Complied on %s at %s\n, _DATE_, _TIME_");

程序开始执行,都会打印编译的日期和时间,可以用来区分同一程序的不同版本。
_LINE__FILE_可以用来找错误。当一个c程序因为被0除(或被0余)而导致终止时,通常没有信息指明哪条除法(或余)导致错误。下面的宏可以帮助查明错误的根源:

#define CHECK_ZERO(divisor)  \   if(divisor == 0)          \   printf("***Attempt to divide by zero on line %d of file %s ***\n", _LINE_, _FILE_);

CHECK_ZERO宏应该在(余)除法运算前被调用:

CHECK_ZERO(j);k = i/j;

如果j = 0,显示错误位置。
C语言库提供了一个通用的、用于错误检测的宏——assert宏。

条件编译

#if & #endif

#define DEBUG 1#if DEBUG....#endif

如果DEBUG设为1,预处理器就将#if 与 #endif 之间的语句保留到程序中,进行编译;如果DEBUG设为0,预处理器就删去此段代码。

对于没有定义过的标识,#if 将它视为0

defined运算符

使用:defined 标识符 如果标识符已经定义为宏返回1,否则返回0。

#define DEBUG //不需要给定值#if defined DEBUG....#endif

#ifdef & #ifndef

#ifdef //相当于#if defined
#ifndef //相当于#if !defined

#elif & #else

#if 标识符#if 表达式1#elif 条件1#elif 条件2....#else#endif

elif可以多次使用,else只能在其中使用一次。

使用条件编译的好处:

编写多台机器或者多个操作系统的可移植程序。

#if defined WINDOWS....#elif defined UNIX....#elif defined OS....#endif

编写可以使用不同编译器编译的程序。

#if _STDC_ //标准c函数原型....#else //经典c函数声明....#endif

为宏提供默认定义。

#ifndef BUFFER_SIZE#define BUFFER_SIZE 256#endif

临时屏蔽包含注释的代码。由于在标准C中不能用注释嵌套

#if 0包含注释的代码行#endif

未完

原创粉丝点击