图文讲解知识点-二级C语言-带参数的宏
详解C语言的宏定义
详解C语⾔的宏定义宏定义介绍假设我们有⼀个 C 源⽂件 main.c,那么只需要通过 gcc main.c -o main.exe 即可编译成可执⾏⽂件(如果只写 gcc main.c,那么 Windows 上会默认⽣成 a.exe、Linux 上会默认⽣成 a.out ),但是这⼀步可以拆解成如下步骤:预处理:gcc -E main.c -o main.i,根据 C 源⽂件得到预处理之后的⽂件,这⼀步只是对 main.c 进⾏了预处理:⽐如宏定义展开、头⽂件展开、条件编译等等,同时将代码中的注释删除,注意:这⾥并不会检查语法;编译:gcc -S main.i -o main.s,将预处理后的⽂件进⾏编译、⽣成汇编⽂件,这⼀步会进⾏语法检测、变量的内存分配等等;汇编:gcc -c main.s -o main.o,根据汇编⽂件⽣成⽬标⽂件,当然我们也可以通过 gcc -c main.c -o main.o 直接通过 C 源⽂件得到⽬标⽂件;链接:gcc main.o -o main.exe,程序是需要依赖各种库的,可以是静态库也可以是动态库,因此需要将⽬标⽂件和其引⽤的库链接在⼀起,最终才能构成可执⾏的⼆进制⽂件。
⽽这⾥我们主要来介绍⼀下预处理中的宏定义,相信很多⼈都觉得宏定义⾮常简单,但其实宏定义有很多⾼级⽤法。
我们先来看看简单的宏定义:#include <stdio.h>// 宏定义的⽅式为:#define 标识符常量// 然后会将所有的 PI 替换成 3.14#define PI 3.14int main() {printf("%f\n", PI);}我们⽣成预处理之后的⽂件:gcc -E main.c -o main.i我们看到 PI 被替换成了 3.14,当然除了浮点型之外,也可以是其它的类型:#include <stdio.h>#define NAME "satori"#define AGE 17#define GENDER 'f'int main() {printf("%s %d %c\n", NAME, AGE, GENDER); // satori 17 f}我们再来查看⽣成的预处理⽂件:我们看到确实只是简单替换,除此之外,没有做任何的处理。
比较C语言中带参数的宏
谢谢观看!
窗棂趣分享
例:#define ABS(x) ((x)>0?(x):-(x))
#define ABS (x) ((x)>0?(x):-(x))
与C++中的内联函数、函数模板比较
C++内联函数在程序运行的时候在调用点“就地展开”,而C 语言中的带参数的宏是在编译前“就地展开”。由于两者都 没有像函数调用那样需要入栈和出栈,所以程序运行速度加 快,但是在多次使用后,代码都会占用大量的空间。
比较C语言中带参数的宏与C++中内联函数 和函数模板及一些注意事项
以奇渠
C语言中带参数的宏
1.带参数的宏定义指令
#define 宏名(参数表) 字符串 例:#define ABS(x) ((x)>0?(x):-(x))
这里x是参数,三目运算符表达式为计算参数的绝对值。这里的 ABS(x)既不是函数定义,也不是语句(此处无分号)。y=ABS(-2);
宏不存在类型问题,宏名无类型,它的参数也无类型,只是 一个符号代表,展开时代入指定的字符串即可。宏定义时, 字符串可以是任意类型的数据。而内联函数需要的实参和形 参都要定义类型,而且两者的类型应相同。
函数模板格式: Template <typename 形式数据类型表> 返回类型 函数模板名
例:template <typename T> T Max(T a,T b)
第5章 函数与带参数宏 《C语言程序设计及应用教程》课件-PPT课件
5.2 函数的嵌套与递归调用
函数的嵌套调用
不允许嵌套定义,函数间平行的、独立。 C中的函数:
允许嵌套调用。
main( ) 调用函数a 结束
a函数
调用函数b
b函数
【例5-7】 输入两个整数,求平方和
#include <stdio.h>
int fun1(int x,int y)
int fun1(int x,int y);
斐波那契数列");
for(i=0;i<N;i++)
{
printf("%d\t",iF[i]);
if((i+1)%10==0)
printf("\n");
}
printf("\n");
}
int Sum(int iF[],int n); void main() { int i,Fibonacci[N],m,n; //生成斐波那契数列 CreateFibonacci(Fibonacci); //输出斐波那契数列 PrintFibonacci(Fibonacci); //求数列前n项的和 printf(" 请输入要求和的项数 n(n<%d): ",N); scanf("%d",&n); m=Sum(Fibonacci,n); printf("\n 斐波那契数列前 %d项的和:%4d\n",n,m);}
第5章 函数与带参数宏 ?C语言 程序设计及应用教程?课件
5.1 函数的声明、定义与调用
5.1.1 函数的声明与定义 5.1.2 函数调用 5.1.3 函数参数传递 5.1.4 主函数中的参数
c语言中宏定义
c语言中宏定义宏定义是C语言中一种非常重要的特性,通过宏定义可以简化代码,提高代码的可维护性和可读性。
在C语言中,宏定义是通过#define指令来实现的,可以定义常量、函数宏以及条件编译等。
首先,我们来看一下如何定义常量宏。
在C语言中,我们可以使用#define指令定义常量,例如:#define PI 3.14159。
这样,每次在代码中使用PI时,编译器会将其替换为3.14159。
常量宏的定义格式为#define 宏名值。
除了定义常量,宏定义还可以用来定义函数宏。
函数宏是一种宏定义,可以像函数一样接受参数,并返回一个值。
例如,我们可以定义一个计算平方的函数宏:#define SQUARE(x) ((x) * (x))。
在使用时,可以像函数一样传入参数,编译器会将其替换为对应的表达式。
另外,宏定义还可以用于条件编译,即根据条件编译指令来决定编译哪些代码。
例如,我们可以使用宏定义来控制代码的编译,如下所示:#ifdef DEBUGprintf("Debug mode\n");#endif在这段代码中,如果定义了DEBUG宏,则会打印“Debug mode”,否则不会打印。
条件编译可以根据宏定义的真假来选择性地编译代码,提高代码的灵活性和可移植性。
此外,宏定义还可以用于定义一些特定的编译器指令,如调试信息、优化等。
通过宏定义,我们可以根据不同的编译选项定义不同的宏,以达到不同的编译效果。
总的来说,宏定义是C语言中一种非常有用的特性,可以简化代码、提高代码的可维护性和可读性,同时还可以用于条件编译、函数宏等方面。
合理地使用宏定义,可以使代码更加灵活、高效。
希望以上内容对您有所帮助。
如果有其他问题,欢迎继续提问。
感谢阅读!。
计算机二级知识点总结c语言
计算机二级知识点总结c语言计算机二级知识点总结C语言C语言是一种通用的编程语言,广泛应用于计算机科学和软件开发领域。
对于计算机二级考试来说,掌握C语言的基本知识点是非常重要的。
本文将对C语言的一些重要知识点进行总结,帮助考生更好地准备考试。
一、基本语法1. 字符集:C语言使用ASCII字符集进行编程。
2. 注释:单行注释以“//”开头,多行注释以“/*”开头,以“*/”结尾。
3. 标识符:由字母、数字和下划线组成,必须以字母或下划线开头。
4. 关键字:C语言有一些预定义的关键字,如int、char、if 等。
二、数据类型1. 基本数据类型:包括整型(int)、字符型(char)、浮点型(float)和双精度浮点型(double)。
2. 枚举类型:使用enum关键字定义的一组命名常量。
3. 派生数据类型:由基本数据类型和其他数据类型组合而成的数据类型,如数组、结构体和联合体。
三、运算符和表达式1. 算术运算符:包括加法(+)、减法(-)、乘法(*)、除法(/)和取模(%)。
2. 关系运算符:用于比较两个表达式的结果,如等于(==)、不等于(!=)、大于(>)等。
3. 逻辑运算符:用于连接两个或多个表达式,如与(&&)、或(||)和非(!)。
4. 条件运算符:用于根据条件选择不同的值,形式为“条件 ? 值1 : 值2”。
四、控制语句1. 选择结构:使用if语句进行条件判断,可以使用if-else、if-else if-else等嵌套形式。
2. 循环结构:使用for、while和do-while语句进行循环操作。
3. 跳转语句:使用break和continue语句控制程序的跳转。
五、数组和指针1. 数组:一组相同类型的数据的有序集合,在C语言中以一维或多维数组的形式存在。
2. 指针:用来存储变量的地址,可以通过指针来访问和修改变量的值。
六、函数1. 函数定义:包括返回类型、函数名、参数列表和函数体。
计算机二级C语言程序设计第8讲
整理ppt
9
3.文件包含的优点
一个大程序,通常分为多个模块,并由多个程序员分别编程。有 了文件包含处理功能,就可以将多个模块共用的数据(如符号常量和 数据结构)或函数,集中到一个单独的文件中。这样,凡是要使用其 中数据或调用其中函数的程序员,只要使用文件包含处理功能,将所 需文件包含进来即可,不必再重复定义它们,从而减少重复劳动。
2.使用宏定义的优点 (1)可提高源程序的可维护性 (2)可提高源程序的可移植性 (3)减少源程序中重复书写字符串的工作量
整理ppt
3
[案例8.1] 输入圆的半径,求圆的周长、面积和球的体积。要求使用无参宏定义 圆周率。
/*案例代码文件名:AL8_1.C*/
/*程序功能:输入圆的半径,求圆的周长、面积和球的体积。 */
2.带参宏的调用和宏展开 (1)调用格式:宏名(实参表) (2)宏展开:用宏调用提供的实参字符串,直接置换宏定义命 令行中、相应形参字符串,非形参字符保持不变。 3.说明 (1)定义有参宏时,宏名与左圆括号之间不能留有空格。否则, C编译系统将空格以后的所有字符均作为替代字符串,而将该宏视为 无参宏。 (2)有参宏的展开,只是将实参作为字符串,简单地置换形参 字符串,而不做任何语法检查。在定义有参宏时,在所有形参外和整 个字符串外,均加一对圆括号。
恰当命名的符号常量,除具有宏定义的上述优点外,
还能表达出它所代表常量的实际含义,从而增强程序的 可读性。
#define EOF -1
/*文件尾*/
#define NULL 0
/*空指针*/
[Return]
整理ppt
6
8.1.3 有参宏定义
1.带参宏定义的一般格式 #define 宏名(形参表) 语言符号字符串
宏定义(无参宏定义和带参宏定义),C语言宏定义详解
宏定义(⽆参宏定义和带参宏定义),C 语⾔宏定义详解1、宏定义说明宏定义是⽐较常⽤的预处理指令,即使⽤“标识符”来表⽰“替换列表”中的内容。
标识符称为宏名,在预处理过程中,预处理器会把源程序中所有宏名,替换成宏定义中替换列表中的内容。
常见的宏定义有两种,不带参数的宏定义和带参数的宏定义。
2、⽆参宏定义⽆参数宏定义的格式为:#define 标识符替换列表替换列表可以是数值常量、字符常量、字符串常量等,故可以把宏定义理解为使⽤标识符表⽰⼀常量,或称符号常量。
说明:1. 可以不在⾏⾸,但只允许它前⾯有空格符。
例如:#define PI 3.1416 //正确,该⾏#前允许有空格int a;#define N 5 //错误,该⾏#前不允许有空格外的其他字符2. 标识符和替换列表之间不能加赋值号 =,替换列表后不能加分号#define N =5 //虽语法正确,但预处理器会把N替换成=5int a[N]; //错误,因为宏替换之后为 int a[=5];宏定义不是语句,是预处理指令,故结尾不加分号。
如果不⼩⼼添加了分号,虽然有时该宏定义没问题,但在宏替换时,可能导致 C 语法错误,或得不到预期结果。
例如:#define N 5; //虽语法正确,但会把N替换成5;int a[N]; //语法错误,宏替换后,为int a[5;];错误3. 由于宏定义仅是做简单的⽂本替换,故替换列表中如有表达式,必须把该表达式⽤括号括起来,否则可能会出现逻辑上的“错误”。
例如:#define N 3+2int r = N * N;宏替换后为:int r=3+2*3+2; //r=11如果采⽤如下形式的宏定义:#define N (3+2)int r=N*N;则宏替换后,为:int r=(3+2)*(3+2); //r=254. 当替换列表⼀⾏写不下时,可以使⽤反斜线\作为续⾏符延续到下⼀⾏。
例如:#define USA "The United \States of \America"该宏定义中替换列表为字符串常量,如果该串较长,或为了使替换列表的结构更清晰,可使⽤续⾏符 \ 把该串分若⼲⾏来写,除最后⼀⾏外,每⾏⾏尾都必须加续⾏符 \。
c语言第8章带参宏递归及习题课
如果带参宏的展开与代码中的其他 语句发生冲突,会导致编译错误。
调试技巧
使用调试器
可以使用调试器逐步执行代码, 观察带参宏的展开过程,以便发
现错误。
单步执行
在调试过程中,可以单步执行代 码,观察变量的值和程序的执行
流程。
检查宏定义和调用
在调试过程中,应仔细检查带参 宏的定义和调用,确保参数匹配
在这个示例中, `Factorial`宏接受一个 参数`n`,并使用递归 方式计算`n`的阶乘。 如果`n`等于0,则返 回1;否则,返回`n` 乘以`Factorial(n - 1)` 的结果。
递归调用的注意事项
在使用递归宏时,需要注意避免 无限递归。如果递归没有终止条 件,会导致编译错误或运行时错
下一步学习计划
01
深入理解宏展开机制
为了更好地掌握带参宏的用法,需要深入了解预处理器的工作原理和宏
展开机制。了解预处理器如何处理宏定义和宏调用,以及如何处理参数
替换和文本替换过程。
02
学习其他预处理指令
除了带参宏外,C语言还提供了其他一些有用的预处理指令,如条件编
译、文件包含等。了解这些指令的用法和注意事项,可以帮助更好地利
C语言第8章:带参宏递归及习题 课
contents
目录
• 引言 • 带参宏定义与使用 • 宏的递归调用 • 带参宏的常见错误与调试 • 习题解析与解答 • 总结与回顾
01 引言
课程简介
介绍带参宏递归的基 本概念和原理
介绍带参宏递归的实 现方式和注意事项
讲解带参宏递归在C 语言中的应用场景和 优势
• 计算两个数的和
带参宏使用示例
```c ```
交换两个变量的值
计算机二级C语言最重要知识点
计算机二级C语言最重要知识点第一章C语言的基础知识第一节、对C语言的基础认识1、C语言编写的程序称为源程序,又称为编译单位。
2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。
3、一个C语言程序有且只有一个main函数,是程序运行的起点。
第二节、熟悉vc++1、VC是软件,用来运行写的C语言程序。
2、每个C语言程序写完后,都是先编译,后链接,最后运行。
(.c---à.obj---à.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。
(常考!)第三节、标识符1、标识符(必考内容):合法的要求是由字母,数字,下划线组成。
有其它元素就错了。
并且第一个必须为字母或则是下划线。
第一个为数字就错了2、标识符分为关键字、预定义标识符、用户标识符。
关键字:不可以作为用户标识符号。
main define scanf printf 都不是关键字。
迷惑你的地方If是可以做为用户标识符。
因为If中的第一个字母大写了,所以不是关键字。
预定义标识符:背诵define scanf printf include。
记住预定义标识符可以做为用户标识符。
用户标识符:基本上每年都考,详细请见书上习题。
第四节:进制的转换十进制转换成二进制、八进制、十六进制。
二进制、八进制、十六进制转换成十进制。
第五节:整数与实数1)C语言只有八、十、十六进制,没有二进制。
但是运行时候,所有的进制都要转换成二进制来进行处理。
(考过两次)a、C语言中的八进制规定要以0开头。
018的数值是非法的,八进制是没有8的,逢8进1。
b、C语言中的十六进制规定要以0x开头。
2)小数的合法写法:C语言小数点两边有一个是零的话,可以不用写。
1.0在C语言中可写成1.0.1在C语言中可以写成.1。
3)实型数据的合法形式:a、2.333e-1 就是合法的,且数据是2.333×10-1。
b、考试口诀:e前e后必有数,e后必为整数。
第八章(宏)
(四)宏定义不是C语句,不必在句末加分号;如果加了 分号,将会连分号一起作替换:
(见下页)
#define PI . . . area = PI * r * r ; 经过宏展开后,该语句为:
3.14159 ;
area = 3.14159 ; * r * r ;
(五)#define 命令出现在函数之外,宏名的有效范围为 定义点到源文件结束;也可以用#undef 命令控制宏定义的作 用域。
(二)其优点是提高了C程序的通用性(P153)
二、第二种形式
#ifndef #else 程序段2 #endif 与前一种形式在条件判断上相反,但作用是一致的,即根 据条件编译相应的程序段。 标识符
程序段1
三、第三种形式
#if #else 程序段2 #endif 表达式
程序段1
其作用是:当指定的表达式为真时,编译程序段1,否则 编译程序段2(例8.8 P154)。 返回章节
二带参数的宏定义二带参数的宏定义带参数的宏定义不是进行简单的字符串置换还要进行参数替换这个参数由宏定义中的参数表指定并且该参数包含在字符串中凡是字符串中与参数表中参数同名的字符均作为参数处理相当于函数中的形参宏展开时用宏名中指定的参数作相应的替换替换的规则是
第八章
一、
•主要内容
编译预处理
概 述
本章主要讲述了C编译系统的一个组成部分,即 “编译预处理”。
(六)在进行宏定义时。可以引用已经定义好的宏名,可 以层层置换(例8.2): #define R 3.0 #define PI 3.1415926 #define L 2*PI*R #define S PI*R*R main ( ) { printf ( “ L=%f \n S = %f\n ” , L , S ) ; }
c语言的宏定义
c语⾔的宏定义忘了啥时候记的笔记了@⽬录宏定义宏定义是C提供的三种预处理功能的其中⼀种,这三种预处理包括:宏定义、⽂件包含、条件编译1.不带参数的宏定义:宏定义⼜称为宏代换、宏替换,简称“宏”。
格式: #define 标识符字符串,其中的标识符就是所谓的符号常量,也称为“宏名”。
预处理(预编译)⼯作也叫做宏展开:将宏名替换为字符串。
掌握"宏"概念的关键是“换”。
⼀切以换为前提、做任何事情之前先要换,准确理解之前就要“换”。
即在对相关命令或语句的含义和功能作具体分析之前就要换:例:#define PI 3.1415926 ,把程序中出现的PI全部换成3.1415926说明:(1)宏名⼀般⽤⼤写(2)使⽤宏可提⾼程序的通⽤性和易读性,减少不⼀致性,减少输⼊错误和便于修改。
例如:数组⼤⼩常⽤宏定义(3)预处理是在编译之前的处理,⽽编译⼯作的任务之⼀就是语法检查,预处理不做语法检查。
(4)宏定义末尾不加分号;(5)宏定义写在函数的花括号外边,作⽤域为其后的程序,通常在⽂件的最开头。
(6)可以⽤#undef命令终⽌宏定义的作⽤域(7)宏定义可以嵌套(8)字符串" "中永远不包含宏(9)宏定义不分配内存,变量定义分配内存。
2.带参数的宏定义:除了⼀般的字符串替换,还要做参数代换格式: #define 宏名(参数表)字符串例如:#define S(a,b) a * b ,area=S(3,2);第⼀步被换为area=a*b; ,第⼆步被换为area = 3 * 2;类似于函数调⽤,有⼀个哑实结合的过程:(1)实参如果是表达式容易出问题,#define S ( r ) r * r ,area=S(a+b);第⼀步换为area=r * r;,第⼆步被换为area=a+b * a+b; 正确的宏定义是#define S ( r ) (( r ) * ( r ))(2)宏名和参数的括号间不能有空格(3)宏替换只作替换,不做计算,不做表达式求解(4)函数调⽤在编译后程序运⾏时进⾏,并且分配内存。
C语言符号和参数详解
1..数值宏常量它可以出现在代码的任何地方,从本行宏定义开始,以后的代码就就都认识这个宏了;也可以把任何东西定义成宏。
因为编译器会在预编译的时候用真身替换替身,而在我们的代码里面却又用常常用替身来帮忙。
看例子: #define PI 3.141592654 const修饰的数据是有类型的,而define宏定义的数据没有类型。
为了安全,我建议你以后在定义一些宏常数的时候用const代替,编译器会给const修饰的只读变量做类型校验,减少错误的可能。
但一定要注意const修饰的不是常量而是read only的变量const修饰的只读变量不能用来作为定义数组的维数也不能放在case关键字后面。
2.. 字符串宏常量A),#define ENG_PATH_1 E:\English\listen_to_this\listen_ to_this_3B),#define ENG_PATH_2 “E:\English\listen_to_this\list en_to_this_3”ENG_PATH_1加上双引号就成了:“ENG_PATH_1”。
3..用define宏定义注释符号可以吗#define BSC //#define BMC /*#define EMC */D),BSC my single-linecommentE),BMC my multi-linecomment EMCD)和E)都错误,为什么呢?因为注释先于预处理指令被处理,当这两行被展开成//…或/*…*/时,注释已处理完毕此时再出现//…或/*…*/自然错误.因此,试图用宏开始或结束一段注释是不行的。
(上机调试可知)4.. 用define宏定义表达式求两个数的平方#define SQR(x) x * x假设x的值是个表达式10+1,SQR(x)被替换后变成10+1*10+1这并不是想要得到的怎么办括起来就好了这样 #define SQR(x) ((x)*(x))求两个数的和:#define SUM (x)(x)+(x)而代码又写成这样:SUM (x)* SUM (x)。
C语言中带参数宏的展开次序
C语言中带参数宏的展开次序C语⾔中带参数宏的展开次序最近看QEMU代码的机会⾔较多,发现⾔⾔⾔了⾔量宏定义,有必要学习⾔下。
刚开始主要关注了带参数宏的宏展开次序,发现了之前没有注意到的东西。
⾔般⾔⾔,我们在调⾔⾔个带参数宏时,如果传⾔的参数也是⾔个带参数宏的话,我们期待的是像函数调⾔那样,从内层到外层依次展开,依次计算出结果后,将内层宏展开后的结果作为实参交给更外层的宏进⾔处理,因为我们认为最内层的括号优先级最⾔,应该被优先处理。
⾔多数情况下,这个结论是正确的,但如果外层的宏展开后有 '#' 或 "##" 运算符操作,则会优先从该层展开,这种情况下,在宏展开的过程中往往会报错。
正确的做法是,要么将宏展开后带有 '#' 或"##" 操作的宏放⾔最内层,要么在这样的宏外⾔再包裹⾔层,并且调⾔包裹后的宏。
下⾔介绍⾔个⾔例来说明这个问题:假设有下⾔三个宏:#define A(a) a#define B(a) -a#define C(a,b) a ## b则以下调⾔均会在预处理时出错:int a = C(1, A(B(2)));a = C(A(B(1)), 2);a = A(C(1, B(2)));a = A(C(B(1), 2));a = B(C(1, A(2)));a = B(C(A(1),2));但如果是以下⾔这种⾔式调⾔就不会报错:int a = A(B(C(1,2)));或者是:#define e_5 100#define D(d) _5#define CC(a, b) C(a,b)int a = A(CC(e,D(d)));上述代码中为什么要定义宏e_5,是因为对于gcc⾔⾔,⾔##操作后的结果必须是⾔个已经预定义过的符号。
c语言程序设计--宏
2) #define MU(x,y) x*y a=MU(5,2)=5*2=10 b=6/MU(a+3,a)=6/a+3*a =6/10+3*10=30
#define PR(ar) printf(“ar=%d”,ar) printf(“ar=%d” Main() { int j,a[]={1,3,5,7,9,11,13,15},*p=a+5; for(j=3;j;j--) for(j=3;j;j--) switch(j) { case 1: case 2:PR(*P++);break’; 2:PR(*P++);break’ //printf (“ar=%d”,*p++); (“ar=%d” case 3:PR(*(--P)); 3:PR(*(--P)); // printf(“ar=%d”,*(--p)); printf(“ar=%d”,*(--p)); } } 结果:ar=9 结果:ar=9 ar=9 ar=11
如果不用宏。。。
(1) 程序的可读性(可理解性)变差。 程序员自己会忘记那些数字或字符串是什 么意思,用户则更加不知它们从何处来、 表示什么。 (2) 在程序的很多地方输入同样的数字 或字符串,难保不发生书写错误。 (3) 如果要修改数字或字符串,则会在 很多地方改动,既麻烦又容易出错。
宏使用的注意事项
三、终止宏定义
格式:#undef 格式:#undef 注意后面没有分号 例:#define 例:#define PI 3.14 main() { … #undef PI //在此之后,PI不再代表 //在此之后,PI不再代表 3.14 }
宏的定义
就是——字符替换 就是——字符替换 #defined A B
C 语言宏详解
代码自动生成-宏带来的奇技淫巧Pdf制作:jmpesp@byr Author : Kevin Lynx众多C++书籍都忠告我们C语言宏是万恶之首,但事情总不如我们想象的那么坏,就如同goto 一样。
宏有一个很大的作用,就是自动为我们产生代码。
如果说模板可以为我们产生各种型别的代码(型别替换),那么宏其实可以为我们在符号上产生新的代码(即符号替换、增加)。
关于宏的一些语法问题,可以在google上找到。
相信我,你对于宏的了解绝对没你想象的那么多。
如果你还不知道#和##,也不知道prescan,那么你肯定对宏的了解不够。
我稍微讲解下宏的一些语法问题(说语法问题似乎不妥,macro只与preprocessor有关,跟语义分析又无关):1. 宏可以像函数一样被定义,例如:#define min(x,y) (x<y?x:y) //事实上这个宏存在BUG但是在实际使用时,只有当写上min(),必须加括号,min才会被作为宏展开,否则不做任何处理。
2. 如果宏需要参数,你可以不传,编译器会给你警告(宏参数不够),但是这会导致错误。
如C++书籍中所描述的,编译器(预处理器)对宏的语法检查不够,所以更多的检查性工作得你自己来做。
3. 很多程序员不知道的#和###符号把一个符号直接转换为字符串,例如:#define STRING(x) #xconst char *str = STRING( test_string ); str的内容就是"test_string",也就是说#会把其后的符号直接加上双引号。
##符号会连接两个符号,从而产生新的符号(词法层次),例如:#define SIGN( x ) INT_##xint SIGN( 1 ); 宏被展开后将成为:int INT_1;4. 变参宏,这个比较酷,它使得你可以定义类似的宏:#define LOG( format, ... ) printf( format, __VA_ARGS__ )LOG( "%s %d", str, count );__VA_ARGS__是系统预定义宏,被自动替换为参数列表。