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语言带条件的宏定义
c语言带条件的宏定义C语言中带条件的宏定义是一种非常有用的功能。
宏定义是一种预处理指令,它用于在程序中定义一些常用的代码片段。
带条件的宏定义允许根据特定的条件来定义宏,并根据条件的真假来执行不同的代码。
这是一种在程序中进行编译时决策的方式。
在C语言中,使用#define指令来定义宏。
宏定义可以包含条件语句,例如#if、#else和#endif,这样可以根据特定的条件来选择性地定义宏。
使用带条件的宏定义的一个常见场景是根据不同的平台来编写跨平台的代码。
例如,当我们需要在不同的操作系统上编写网络程序时,可以定义一个宏来表示当前的操作系统,然后根据不同的操作系统来选择性地定义不同的函数或变量。
另一个常见的应用是在调试代码时使用条件宏来控制输出。
宏定义可以根据调试模式的开关来选择性地打印调试信息,这样可以方便地在调试和发布版本之间进行切换。
以下是一个简单的示例代码来说明带条件的宏定义的使用:```c#define DEBUG_MODE// 定义一个带条件的宏,表示是否开启调试模式void printDebugInfo(const char* info) {#ifdef DEBUG_MODEprintf("Debug: %s\n", info);#endif}// 定义一个打印调试信息的函数,只在调试模式下打印int main() {printDebugInfo("This is a debug message.");return 0;}```在上面的例子中,我们定义了一个宏DEBUG_MODE来表示是否开启调试模式。
在printDebugInfo函数中,通过#ifdef和#endif来限定只在DEBUG_MODE被定义的情况下才打印调试信息。
如果在编译时没有定义DEBUG_MODE,那么调试信息就不会被打印出来。
带条件的宏定义在C语言中非常灵活,并且可以根据不同的需求来进行灵活的配置。
编译器预定义的宏(可以用来区分使用的是哪种编译器)
一、介绍预定义宏"_MSC_VER"1、_MSC_VER是微软C/C++编译器——cl.exe编译代码时预定义的一个宏。
需要针对cl编写代码时,可以使用该宏进行条件编译。
2、_MSC_VER的值表示cl的版本。
需要针对cl特定版本编写代码时,也可以使用该宏进行条件编译。
3、_MSC_VER的类型是"int",具体版本号定义如下:MS VC++ 9.0 _MSC_VER = 1500MS VC++ 8.0 _MSC_VER = 1400MS VC++ 7.1 _MSC_VER = 1310MS VC++ 7.0 _MSC_VER = 1300MS VC++ 6.0 _MSC_VER = 1200MS VC++ 5.0 _MSC_VER = 1100其中MS VC++ 9.0就是Visual C++ 2008,MS VC++ 8.0就是Visual C++ 2005。
二、介绍预定义宏“__GNUC__”1、__GNUC__ 是gcc编译器编译代码时预定义的一个宏。
需要针对gcc编写代码时,可以使用该宏进行条件编译。
2、__GNUC__ 的值表示gcc的版本。
需要针对gcc特定版本编写代码时,也可以使用该宏进行条件编译。
3、__GNUC__ 的类型是“int”三、预定义宏"__MINGW32__"1、MinGW编译器四、symbian sdk预定义宏:symbian平台,定义"__SYMBIAN32_"3rd MR版及之前的那个3rd版本,定义"__SERIES60_30__"3rd FP1版,定义"__SERIES60_31__"3rd FP2版,定义"__SERIES60_32__"另外,还有一个"__SERIES60_3x__"。
若不需区分具体是哪一个3rd版,则用之。
C语言预处理器和宏定义
C语言预处理器和宏定义C语言作为一种广泛应用于嵌入式系统和系统编程的高级程序设计语言,其预处理器和宏定义功能在编程中起到了重要的作用。
在本文中,我们将深入探讨C语言预处理器和宏定义的概念、用途以及一些常见的应用示例,以帮助读者更好地理解和应用它们。
一、C语言预处理器的概念和用途在编译过程中,预处理器是编译器的一部分,用于在源代码文件被编译之前对代码进行预处理。
预处理器根据预定义的宏和预处理指令,对源代码进行文本替换、条件编译等操作,生成经过预处理后的新代码。
预处理器主要有以下几个用途:1. 宏定义:通过使用#define指令,我们可以在预处理阶段将某个标识符替换为相应的文本。
宏定义可以提高代码的可读性和可维护性,减少代码冗余,以及实现一些简单的代码自动生成功能。
2. 文件包含:使用#include指令,可以将其他源代码文件的内容包含到当前的源代码文件中。
通过文件包含,我们可以将项目的代码分割成多个模块,提高代码的组织性和可维护性。
3. 条件编译:通过使用#ifdef、#ifndef、#ifdef等条件预处理指令,我们可以根据不同的条件在编译过程中选择性地包含或排除一些代码。
条件编译在调试代码、处理不同平台的差异以及实现程序的灵活性方面非常有用。
二、宏定义的概念和应用示例宏定义是预处理器中最常见、最基础的功能之一,它可以将一段代码片段以某个标识符的形式定义起来,并在代码中多次使用。
以下是一些常见的宏定义示例:1. 数学计算宏:通过定义一些数学计算的宏,我们可以方便地在代码中使用这些计算。
例如,可以定义一个计算圆面积的宏: #define AREA(r) ((r) * (r) * 3.14)在代码中使用该宏可以极大地简化计算过程,提高代码的可读性和简洁性。
2. 条件编译宏:在使用条件编译时,宏定义非常有用。
例如,我们可以使用宏定义来判断当前编译的平台类型,并进行不同的处理: #ifdef WIN32// 在Windows平台下的代码#else// 在其他平台下的代码#endif通过使用宏定义,我们可以在编译过程中根据不同的平台选择性地编译不同的代码,实现跨平台的程序编写。
C中的预编译宏定义
printf("line: %d, file: %s ", __LINE__, __FILE__);
显示:
line: 34, file: 1.c
line: 100, file: haha
line: 101, file: haha
4, #ifdef, #ifndef, defined.
#ifdef, #ifndef, defined用来测试某个宏是否被定义
#ifdef name 或 #ifndef name
它们经常用于避免头文件的重复引用:
#ifndef __FILE_H__
#define __FILE_H__
---若-DMAX=1, 则正确编译.
---若-DMAX的值被指定为不为1的值, 那么gcc会给出MAX宏被重定义的警告, MAX的值仍为1.
注意: 若在调用gcc的命令行中不显示地给出对象宏的值, 那么gcc赋予该宏默认值(1), 如: -DVAL == -DVAL=1
(3) #define所定义的宏的作用域
关于定义宏的另外一些问题
(1) 宏可以被多次定义, 前提是这些定义必须是相同的. 这里的"相同"要求先后定义中空白符出现的位置相同, 但具体的空白符类型或数量可不同, 比如原先的空格可替换为多个其他类型的空白符: 可为tab, 注释...
e.g.
#define NULL 0
#define NULL /* null pointer */ 0
可参考cpp 手册进一步了解#include_next
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++标准预定义宏2009-08-22 14:40/sevencat/archive/2004/06/10/14872.html一、标准预定义宏The standard predefined macros are specified by the relevant language standards, so the are available with all compilers that implement those standards. Older compilers may no provide all of them. Their names all start with double underscores.__FILE__This macro expands to the name of the current input file, in the form of a C string constant This is the path by which the preprocessor opened the file, not the short name specifie in #include or as the input file name argument. For example,"/usr/local/include/myheader.h" is a possible expansion of this macro.__LINE__This macro expands to the current input line number, in the form of a decimal integer constant. While we call it a predefined macro, it's a pretty strange macro, since its "definition" changes with each new line of source code.__FILE__ and __LINE__ are useful in generating an error message to report an inconsistenc detected by the program; the message can state the source line at which the inconsistenc was detected. For example,fprintf (stderr, "Internal error: ""negative string length ""%d at %s, line %d.",length, __FILE__, __LINE__);An #include directive changes the expansions of __FILE__ and __LINE__ to correspond t the included file. At the end of that file, when processing resumes on the input file tha contained the #include directive, the expansions of __FILE__ and __LINE__ revert to th values they had before the #include (but __LINE__ is then incremented by one as processin moves to the line after the #include).A #line directive changes __LINE__, and may change __FILE__ as well. See Line ControlC99 introduces __func__, and GCC has provided __FUNCTION__ for a long time. Both of thes are strings containing the name of the current function (there are slight semantic differences; see the GCC manual). Neither of them is a macro; the preprocessor does noknow the name of the current function. They tend to be useful in conjunction with __FILE_ and __LINE__, though.__DATE__This macro expands to a string constant that describes the date on which the preprocesso is being run. The string constant contains eleven characters and looks like "Feb 12 1996" If the day of the month is less than 10, it is padded with a space on the left.If GCC cannot determine the current date, it will emit a warning message (once per compilation) and __DATE__ will expand to "??? ?? ????".__TIME__This macro expands to a string constant that describes the time at which the preprocesso is being run. The string constant contains eight characters and looks like "23:59:01" If GCC cannot determine the current time, it will emit a warning message (once per compilation) and __TIME__ will expand to "??:??:??".__STDC__In normal operation, this macro expands to the constant 1, to signify that this compile conforms to ISO Standard C. If GNU CPP is used with a compiler other than GCC, this i not necessarily true; however, the preprocessor always conforms to the standard unles the -traditional-cpp option is used.This macro is not defined if the -traditional-cpp option is used.On some hosts, the system compiler uses a different convention, where __STDC__ is normall 0, but is 1 if the user specifies strict conformance to the C Standard. CPP follows th host convention when processing system header files, but when processing user files __STDC__ is always 1. This has been reported to cause problems; for instance, some version of Solaris provide X Windows headers that expect __STDC__ to be either undefined or 1 See Invocation.__STDC_VERSION__This macro expands to the C Standard's version number, a long integer constant of the for yyyymmL where yyyy and mm are the year and month of the Standard version. This signifie which version of the C Standard the compiler conforms to. Like __STDC__, this is not necessarily accurate for the entire implementation, unless GNU CPP is being used with GCCThe value 199409L signifies the 1989 C standard as amended in 1994, which is the curren default; the value 199901L signifies the 1999 revision of the C standard. Support for th 1999 revision is not yet complete.This macro is not defined if the -traditional-cpp option is used, nor when compiling C+ or Objective-C.__STDC_HOSTED__This macro is defined, with value 1, if the compiler's target is a hosted environment A hosted environment has the complete facilities of the standard C library available.__cplusplusThis macro is defined when the C++ compiler is in use. You can use __cplusplus to tes whether a header is compiled by a C compiler or a C++ compiler. This macro is similar t __STDC_VERSION__, in that it expands to a version number. A fully conforming implementatio of the 1998 C++ standard will define this macro to 199711L. The GNU C++ compiler is no yet fully conforming, so it uses 1 instead. We hope to complete our implementation in th near future.__OBJC__This macro is defined, with value 1, when the Objective-C compiler is in use. You can us __OBJC__ to test whether a header is compiled by a C compiler or a Objective-C compiler__ASSEMBLER__This macro is defined with value 1 when preprocessing assembly language.C标准中的一些预定义宏昨天写代码时需要在代码获取当前编译时间,从而可动态地作为版本信息,因此用到了C标准中的一些预定义的宏。
C语言预处理命令总结大全:宏定义
C语⾔预处理命令总结⼤全:宏定义C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。
虽然它们实际上不是C语⾔的⼀部分,但却扩展了C程序设计的环境。
本节将介绍如何应⽤预处理程序和注释简化程序开发过程,并提⾼程序的可读性。
ANSI标准定义的C语⾔预处理程序包括下列命令:#define,#error,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。
⾮常明显,所有预处理命令均以符号#开头,下⾯分别加以介绍。
⼀ #define命令#define定义了⼀个标识符及⼀个串。
在源程序中每次遇到该标识符时,均以定义的串代换它。
ANSI标准将标识符定义为宏名,将替换过程称为宏替换。
命令的⼀般形式为:#define ID string注意:1该语句没有分号。
在标识符和串之间可以有任意个空格,串⼀旦开始,仅由⼀新⾏结束。
2宏名定义后,即可成为其它宏名定义中的⼀部分。
3 宏替换仅仅是以⽂本串代替宏标识符,前提是宏标识符必须独⽴的识别出来,否则不进⾏替换。
例如:#define XYZ this is a tes使⽤宏printf("XYZ");//该段不打印"this is a test"⽽打印"XYZ"。
因为预编译器识别出的是"XYZ"4如果串长于⼀⾏,可以在该⾏末尾⽤⼀反斜杠' \'续⾏。
#defineLONG_STRING"this is a very long\string that is used as an example"5 C语⾔程序普遍使⽤⼤写字母定义标识符。
6 ⽤宏代换代替实在的函数的⼀⼤好处是宏替换增加了代码的速度,因为不存在函数调⽤的开销。
但增加速度也有代价:由于重复编码⽽增加了程序长度。
C语言宏定义全解析
C语言宏定义全解析C语言宏定义是C语言中一种非常重要的特性,它可以将一段代码或者一种特定的操作封装成一个宏,方便在程序中多次使用。
宏定义在代码的编写过程中起到了简化代码、提高代码的可读性和可维护性的作用。
本文将详细解析C语言宏定义的相关知识,包括宏定义的基本语法、宏定义的特点、宏定义的使用技巧和注意事项等。
一、宏定义的基本语法C语言宏定义的基本语法为:#define 宏名字符串其中,宏名是宏定义的标识符,字符串是需要定义的内容。
在宏定义中,为了增加可读性和减少错误,通常要求宏名全大写。
字符串可以是单个代码行,也可以是多行代码,多行代码需要使用反斜杠(\)进行换行。
示例:#define MAX_VALUE 100#define SQUARE(x) ((x) * (x))#define PRINT_INT(x) printf("%d\n", x)二、宏定义的特点1. 宏定义是在预处理阶段进行的,不占用运行时的内存空间。
2. 宏定义可以定义常量、表达式或函数。
3. 宏定义不会进行类型检查,只是简单的文本替换。
4. 宏定义可以嵌套使用,宏会被递归地展开。
5. 宏定义的作用域为定义后的位置至文件结束或宏重新定义前。
三、宏定义的使用技巧1. 定义宏时需要遵守一定的规范,充分考虑到宏展开后的语法正确性。
2. 在宏定义中使用括号,尤其是在表达式中使用,可以避免优先级问题带来的错误。
3. 在宏定义中使用do-while(0)结构,可以解决宏在某些特定使用场景下的问题。
4. 使用宏时要注意参数的类型,宏不会进行类型检查,可能会导致意想不到的错误。
5. 避免定义过多的宏,过多的宏定义会增加代码的复杂性和维护的难度。
四、宏定义的注意事项1. 宏定义不要滥用,只有在确实有必要的情况下才进行宏定义。
2. 注意宏展开后的代码风格,避免出现过长的宏替换代码。
3. 避免使用“#ifdef”、“#else”、“#endif”等预处理指令来控制宏的定义和使用。
C语言中的预编译宏定义
C语言中的预编译宏定义C语言中的预编译宏定义导语:C初学者可能对预处理器没什么概念,这是情有可原,下面是C中的预编译宏定义,一起来学习下吧:(一) 预处理命令简介预处理命令由#(hash字符)开头, 它独占一行, #之前只能是空白符. 以#开头的语句就是预处理命令, 不以#开头的语句为C中的代码行. 常用的预处理命令如下:#define 定义一个预处理宏#undef 取消宏的定义#include 包含文件命令#include_next 与#include相似, 但它有着特殊的用途#if 编译预处理中的条件命令, 相当于C语法中的if语句#ifdef 判断某个宏是否被定义, 若已定义, 执行随后的语句#ifndef 与#ifdef相反, 判断某个宏是否未被定义#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif 之后的语句, 相当于C语法中的else-if#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else 之后的语句, 相当于C语法中的else#endif #if, #ifdef, #ifndef这些条件命令的结束标志.defined 与#if, #elif配合使用, 判断某个宏是否被定义#line 标志该语句所在的行号# 将宏参数替代为以参数值为内容的字符窜常量## 将两个相邻的标记(token)连接为一个单独的标记#pragma 说明编译器信息#warning 显示编译警告信息#error 显示编译错误信息(二) 预处理的文法预处理并不分析整个源代码文件, 它只是将源代码分割成一些标记(token), 识别语句中哪些是C语句, 哪些是预处理语句. 预处理器能够识别C标记, 文件名, 空白符, 文件结尾标志.预处理语句格式: #command name(...) token(s)1, command预处理命令的名称, 它之前以#开头, #之后紧随预处理命令, 标准C允许#两边可以有空白符, 但比较老的编译器可能不允许这样. 若某行中只包含#(以及空白符), 那么在标准C中该行被理解为空白. 整个预处理语句之后只能有空白符或者注释, 不能有其它内容.2, name代表宏名称, 它可带参数. 参数可以是可变参数列表(C99).3, 语句中可以利用""来换行.e.g.# define ONE 1 /* ONE == 1 */等价于: #define ONE 1#define err(flag, msg) if(flag)printf(msg)等价于: #define err(flag, msg) if(flag) printf(msg)(三) 预处理命令详述1, #define#define命令定义一个宏:#define MACRO_NAME(args) tokens(opt)之后出现的MACRO_NAME将被替代为所定义的标记(tokens). 宏可带参数, 而后面的标记也是可选的.对象宏不带参数的宏被称为"对象宏(objectlike macro)"#define经常用来定义常量, 此时的宏名称一般为大写的字符串. 这样利于修改这些常量.e.g.#define MAX 100int a[MAX];#ifndef __FILE_H__#define __FILE_H__#include "file.h"#endif#define __FILE_H__ 中的宏就不带任何参数, 也不扩展为任何标记. 这经常用于包含头文件.要调用该宏, 只需在代码中指定宏名称, 该宏将被替代为它被定义的内容.函数宏带参数的宏也被称为"函数宏". 利用宏可以提高代码的运行效率: 子程序的调用需要压栈出栈, 这一过程如果过于频繁会耗费掉大量的CPU运算资源. 所以一些代码量小但运行频繁的代码如果采用带参数宏来实现会提高代码的运行效率.函数宏的参数是固定的情况函数宏的定义采用这样的方式: #define name( args ) tokens其中的args和tokens都是可选的. 它和对象宏定义上的区别在于宏名称之后不带括号.注意, name之后的左括号(必须紧跟name, 之间不能有空格, 否则这就定义了一个对象宏, 它将被替换为以(开始的字符串. 但在调用函数宏时, name与(之间可以有空格.e.g.#define mul(x,y) ((x)*(y))注意, 函数宏之后的参数要用括号括起来, 看看这个例子:e.g.#define mul(x,y) x*y"mul(1, 2+2);" 将被扩展为: 1*2 + 2同样, 整个标记串也应该用括号引用起来:e.g.#define mul(x,y) (x)*(y)sizeof mul(1,2.0) 将被扩展为 sizeof 1 * 2.0调用函数宏时候, 传递给它的参数可以是函数的返回值, 也可以是任何有意义的语句:e.g.mul (f(a,b), g(c,d));e.g.#define (stmt) stmt( a=1; b=2;) 相当于在代码中加入 a=1; b=2 .( a=1, b=2;) 就有问题了: 预处理器会提示出错: 函数宏的参数个数不匹配. 预处理器把","视为参数间的分隔符.((a=1, b=2;)) 可解决上述问题.在定义和调用函数宏时候, 要注意一些问题:1, 我们经常用{}来引用函数宏被定义的内容, 这就要注意调用这个函数宏时的";"问题.example_3.7:#define swap(x,y) { unsigned long _temp=x; x=y; y=_tmp}如果这样调用它: "swap(1,2);" 将被扩展为: { unsigned long _temp=1; 1=2; 2=_tmp};明显后面的;是多余的, 我们应该这样调用: swap(1,2)虽然这样的调用是正确的, 但它和C语法相悖, 可采用下面的方法来处理被{}括起来的内容:#define swap(x,y)do { unsigned long _temp=x; x=y; y=_tmp} while (0)swap(1,2); 将被替换为:do { unsigned long _temp=1; 1=2; 2=_tmp} while (0);在Linux内核源代码中对这种do-while(0)语句有这广泛的应用.2, 有的函数宏是无法用do-while(0)来实现的, 所以在调用时不能带上";", 最好在调用后添加注释说明.eg_3.8:#define incr(v, low, high)for ((v) = (low),; (v) <= (high); (v)++)只能以这样的形式被调用: incr(a, 1, 10) /* increase a form 1 to 10 */函数宏中的参数包括可变参数列表的情况C99标准中新增了可变参数列表的内容. 不光是函数, 函数宏中也可以使用可变参数列表.#define name(args, ...) tokens#define name(...) tokens"..."代表可变参数列表, 如果它不是仅有的'参数, 那么它只能出现在参数列表的最后. 调用这样的函数宏时, 传递给它的参数个数要不少于参数列表中参数的个数(多余的参数被丢弃).通过__VA_ARGS__来替换函数宏中的可变参数列表. 注意__VA_ARGS__只能用于函数宏中参数中包含有"..."的情况.e.g.#ifdef DEBUG#define my_printf(...) fprintf(stderr, __VA_ARGS__)#else#define my_printf(...) printf(__VA_ARGS__)#endiftokens中的__VA_ARGS__被替换为函数宏定义中的"..."可变参数列表.注意在使用#define时候的一些常见错误:#define MAX = 100#define MAX 100;=, ; 的使用要值得注意. 再就是调用函数宏是要注意, 不要多给出";".注意: 函数宏对参数类型是不敏感的, 你不必考虑将何种数据类型传递给宏. 那么, 如何构建对参数类型敏感的宏呢? 参考本章的第九部分, 关于"##"的介绍.关于定义宏的另外一些问题(1) 宏可以被多次定义, 前提是这些定义必须是相同的. 这里的"相同"要求先后定义中空白符出现的位置相同, 但具体的空白符类型或数量可不同, 比如原先的空格可替换为多个其他类型的空白符: 可为tab, 注释...e.g.#define NULL 0#define NULL /* null pointer */ 0上面的重定义是相同的, 但下面的重定义不同:#define fun(x) x+1#define fun(x) x + 1 或: #define fun(y) y+1如果多次定义时, 再次定义的宏内容是不同的, gcc会给出"NAME redefined"警告信息.应该避免重新定义函数宏, 不管是在预处理命令中还是C语句中, 最好对某个对象只有单一的定义. 在gcc中, 若宏出现了重定义, gcc会给出警告.(2) 在gcc中, 可在命令行中指定对象宏的定义:e.g.$ gcc -Wall -DMAX=100 -o tmp tmp.c相当于在tmp.c中添加" #define MAX 100".那么, 如果原先tmp.c中含有MAX宏的定义, 那么再在gcc调用命令中使用-DMAX, 会出现什么情况呢?---若-DMAX=1, 则正确编译.---若-DMAX的值被指定为不为1的值, 那么gcc会给出MAX宏被重定义的警告, MAX的值仍为1.注意: 若在调用gcc的命令行中不显示地给出对象宏的值, 那么gcc 赋予该宏默认值(1), 如: -DVAL == -DVAL=1(3) #define所定义的宏的作用域宏在定义之后才生效, 若宏定义被#undef取消, 则#undef之后该宏无效. 并且字符串中的宏不会被识别e.g.#define ONE 1sum = ONE + TWO /* sum = 1 + TWO */#define TWO 2sum = ONE + TWO /* sum = 1 + 2 */#undef ONEsum = ONE + TWO /* sum = ONE + 2 */char c[] = "TWO" /* c[] = "TWO", NOT "2"! */(4) 宏的替换可以是递归的, 所以可以嵌套定义宏.e.g.# define ONE NUMBER_1# define NUMBER_1 1int a = ONE /* a = 1 */2, #undef#undef用来取消宏定义, 它与#define对立:#undef name如够被取消的宏实际上没有被#define所定义, 针对它的#undef 并不会产生错误.当一个宏定义被取消后, 可以再度定义它.3, #if, #elif, #else, #endif#if, #elif, #else, #endif用于条件编译:#if 常量表达式1语句...#elif 常量表达式2语句...#elif 常量表达式3语句......#else语句...#endif#if和#else分别相当于C语句中的if, else. 它们根据常量表达式的值来判别是否执行后面的语句. #elif相当于C中的else-if. 使用这些条件编译命令可以方便地实现对源代码内容的控制.else之后不带常量表达式, 但若包含了常量表达式, gcc只是给出警告信息.使用它们可以提升代码的可移植性---针对不同的平台使用执行不同的语句. 也经常用于大段代码注释.e.g.#if 0{一大段代码;}#endif常量表达式可以是包含宏, 算术运算, 逻辑运算等等的合法C常量表达式, 如果常量表达式为一个未定义的宏, 那么它的值被视为0.#if MACRO_NON_DEFINED == #if 0在判断某个宏是否被定义时, 应当避免使用#if, 因为该宏的值可能就是被定义为0. 而应当使用下面介绍的#ifdef或#ifndef.注意: #if, #elif, #else之后的宏只能是对象宏. 如果name为名的宏未定义, 或者该宏是函数宏. 那么在gcc中使用"-Wundef"选项会显示宏未定义的警告信息.4, #ifdef, #ifndef, defined.#ifdef, #ifndef, defined用来测试某个宏是否被定义#ifdef name 或 #ifndef name它们经常用于避免头文件的重复引用:#ifndef __FILE_H__#define __FILE_H__#include "file.h"#endifdefined(name): 若宏被定义,则返回1, 否则返回0.它与#if, #elif, #else结合使用来判断宏是否被定义, 乍一看好像它显得多余, 因为已经有了#ifdef和#ifndef. defined用于在一条判断语句中声明多个判别条件:#if defined(VAX) && defined(UNIX) && !defined(DEBUG)和#if, #elif, #else不同, #indef, #ifndef, defined测试的宏可以是对象宏, 也可以是函数宏. 在gcc中使用"-Wundef"选项不会显示宏未定义的警告信息.5, #include , #include_next#include用于文件包含. 在#include 命令所在的行不能含有除注释和空白符之外的其他任何内容.#include "headfile"#include#include 预处理标记前面两种形式大家都很熟悉, "#include 预处理标记"中, 预处理标记会被预处理器进行替换, 替换的结果必须符合前两种形式中的某一种.实际上, 真正被添加的头文件并不一定就是#include中所指定的文件. #include"headfile"包含的头文件当然是同一个文件, 但#include 包包含的"系统头文件"可能是另外的文件. 但这不值得被注意. 感兴趣的话可以查看宏扩展后到底引入了哪些系统头文件.关于#include "headfile"和#include 的区别以及如何在gcc中包含头文件的详细信息, 参考本blog的GCC笔记.相对于#include, 我们对#include_next不太熟悉. #include_next 仅用于特殊的场合. 它被用于头文件中(#include既可用于头文件中, 又可用于.c文件中)来包含其他的头文件. 而且包含头文件的路径比较特殊: 从当前头文件所在目录之后的目录来搜索头文件.比如: 头文件的搜索路径一次为A,B,C,D,E. #include_next所在的当前头文件位于B目录, 那么#include_next使得预处理器从C,D,E目录来搜索#include_next所指定的头文件.可参考cpp手册进一步了解#include_next6, 预定义宏标准C中定义了一些对象宏, 这些宏的名称以"__"开头和结尾, 并且都是大写字符. 这些预定义宏可以被#undef, 也可以被重定义.下面列出一些标准C中常见的预定义对象宏(其中也包含gcc自己定义的一些预定义宏:__LINE__ 当前语句所在的行号, 以10进制整数标注.__FILE__ 当前源文件的文件名, 以字符串常量标注.__DATE__ 程序被编译的日期, 以"Mmm dd yyyy"格式的字符串标注.__TIME__ 程序被编译的时间, 以"hh:mm:ss"格式的字符串标注, 该时间由asctime返回.__STDC__ 如果当前编译器符合ISO标准, 那么该宏的值为1__STDC_VERSION__ 如果当前编译器符合C89, 那么它被定义为199409L, 如果符合C99, 那么被定义为199901L.我用gcc, 如果不指定-std=c99, 其他情况都给出__STDC_VERSION__未定义的错误信息, 咋回事呢?__STDC_HOSTED__ 如果当前系统是"本地系统(hosted)", 那么它被定义为1. 本地系统表示当前系统拥有完整的标准C库.gcc定义的预定义宏:__OPTMIZE__ 如果编译过程中使用了优化, 那么该宏被定义为1.__OPTMIZE_SIZE__ 同上, 但仅在优化是针对代码大小而非速度时才被定义为1.__VERSION__ 显示所用gcc的版本号.可参考"GCC the complete reference".要想看到gcc所定义的所有预定义宏, 可以运行: $ cpp -dM /dev/null7, #line#line用来修改__LINE__和__FILE__.e.g.printf("line: %d, file: %s ", __LINE__, __FILE__);#line 100 "haha"printf("line: %d, file: %s ", __LINE__, __FILE__);printf("line: %d, file: %s ", __LINE__, __FILE__);显示:line: 34, file: 1.cline: 100, file: hahaline: 101, file: haha8, #pragma, _Pragma#pragma用编译器用来添加新的预处理功能或者显示一些编译信息. #pragma的格式是各编译器特定的, gcc的如下:#pragma GCC name token(s)#pragma之后有两个部分: GCC和特定的pragma name. 下面分别介绍gcc中常用的.(1) #pragma GCC dependencydependency测试当前文件(既该语句所在的程序代码)与指定文件(既#pragma语句最后列出的文件)的时间戳. 如果指定文件比当前文件新, 则给出警告信息.e.g.在demo.c中给出这样一句:#pragma GCC dependency "temp-file"然后在demo.c所在的目录新建一个更新的文件: $ touch temp-file, 编译: $ gcc demo.c 会给出这样的警告信息: warning: current file is older than temp-file如果当前文件比指定的文件新, 则不给出任何警告信息.还可以在在#pragma中给添加自定义的警告信息.e.g.#pragma GCC dependency "temp-file" "demo.c needs to be updated!"1.c:27:38: warning: extra tokens at end of #pragma directive1.c:27:38: warning: current file is older than temp-file注意: 后面新增的警告信息要用""引用起来, 否则gcc将给出警告信息.(2) #pragma GCC poison token(s)若源代码中出现了#pragma中给出的token(s), 则编译时显示警告信息. 它一般用于在调用你不想使用的函数时候给出出错信息.e.g.#pragma GCC poison scanfscanf("%d", &a);warning: extra tokens at end of #pragma directiveerror: attempt to use poisoned "scanf"注意, 如果调用了poison中给出的标记, 那么编译器会给出的是出错信息. 关于第一条警告, 我还不知道怎么避免, 用""将token(s)引用起来也不行.(3) #pragma GCC system_header从#pragma GCC system_header直到文件结束之间的代码会被编译器视为系统头文件之中的代码. 系统头文件中的代码往往不能完全遵循C标准, 所以头文件之中的警告信息往往不显示. (除非用#warning显式指明).(这条#pragma语句还没发现用什么大的用处)由于#pragma不能用于宏扩展, 所以gcc还提供了_Pragma:e.g.#define PRAGMA_DEP #pragma GCC dependency "temp-file"由于预处理之进行一次宏扩展, 采用上面的方法会在编译时引发错误, 要将#pragma语句定义成一个宏扩展, 应该使用下面的_Pragma语句:#define PRAGMA_DEP _Pragma("GCC dependency "temp-file"")注意, ()中包含的""引用之前引该加上转义字符.9, #, ###和##用于对字符串的预处理操作, 所以他们也经常用于printf, puts之类的字符串显示函数中.#用于在宏扩展之后将tokens转换为以tokens为内容的字符串常量.e.g.#define TEST(a,b) printf( #a "<" #b "=%d ", (a)<(b));注意: #只针对紧随其后的token有效!##用于将它前后的两个token组合在一起转换成以这两个token 为内容的字符串常量. 注意##前后必须要有token.e.g.#define TYPE(type, n) type n之后调用:TYPE(int, a) = 1;TYPE(long, b) = 1999;将被替换为:int a = 1;long b = 1999;(10) #warning, #error#warning, #error分别用于在编译时显示警告和错误信息, 格式如下:#warning tokens#error tokense.g.#warning "some warning"注意, #error和#warning后的token要用""引用起来!(在gcc中, 如果给出了warning, 编译继续进行, 但若给出了error, 则编译停止. 若在命令行中指定了 -Werror, 即使只有警告信息, 也不编译.【C语言中的预编译宏定义】。
宏定义(无参宏定义和带参宏定义),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-常用预定义宏
C-常⽤预定义宏预定义宏 __DATE__ 字符串, 进⾏预处理的⽇期("Mmm dd yyyy", 如May 27 2006) __TIME__ 字符串, 源⽂件的编译时间("hh:mm:ss", 如09:11:10) __FILE__ 字符串, 代表当前源代码⽂件名(包含详细路径, 如F:/a.c) __LINE__ 整数值, 代表当前源代码⽂件中的⾏号 __STDC__ 布尔值, 表⽰该实现严格遵循ANSIC标准 __STDC_VERSION__长整型值, 表⽰编译器所遵循的C标准的版本号(yyyymmL,如199101L) __func__ 字符串, 当前所在函数名(C99标准) __FUNCTION__ 字符串, 同__func__, 不建议使⽤ __PRETTY_FUNCTION__在C中, 同__func__; ⽽在C++中, 则记录了当前函数的头信息 __VA_ARGS__ 保存了可变参数列表 "..." __cplusplus 长整型值, 表⽰了C++的版本号(yyyymmL, 如199711L)/* 对于__DATE__, __TIME__, 可以获取编译时间 */char *creationDate = __DATE__","__TIME__;sscanf(creationDate, "$s %d %d,, %d:%d:%d", month, &day, &year, &hour, &min, &sec);OS Macro Windows: _WIN32, WIN32, _WIN64 Unix: unix, __unix, __unix__ Linux: linux, __linux, __linux__, __gnu_linux__ SunOS/Solaris: __SVR4, __svr4__, sun, __sun, __sun__, sparc, __sparc, __sparc__ Hpux: __hppa, __hppa__, __hpux, __hpux__, _HUPX_SOURCE AIX: _AIX, _AIX32, _AIX41, _AIX43, _AIX51, _AIX52 CPU: __x84_64, __x86_64__, __amd64, __amd64__, sparc, __sparc, __sparc__ Comiler Macro __STDC__: 布尔值, 值为1时, 说明兼容ANSIC标准 __GNUC__: 整数值, gcc编译器预定义的⼀个宏, 表⽰gcc的主版本号 __GNUC_MINOR__: 整数值, gcc的次版本号 __GNUC_PATHLEVEL__: 整数值, gcc的修订号 __GLIBC__: 整数值, glib的主版本号 __GLIBC_MINOR__: 整数值, glib的次版本号 ⽐如gcc3.4.5版本, 则__GNUC__==3, __GNUC_MINOR__==4, __GNUC_PATHLEVEL__==6。
C语言预处理器宏定义和条件编译
C语言预处理器宏定义和条件编译C语言预处理器宏定义和条件编译是C语言中重要的概念和机制。
通过宏定义和条件编译,我们可以在编译阶段对代码进行宏展开和条件判断,以实现代码的重用和编译选项的控制。
本文将介绍C语言预处理器宏定义和条件编译的基本概念、语法和用法,帮助读者更好地理解和应用这两个功能。
一、宏定义宏定义是C语言预处理器提供的一个功能,可以用来声明宏,并将一段代码或表达式替换为所定义的宏。
宏定义的基本语法格式为:#define 宏名替换文本其中,宏名是一个标识符,替换文本可以是任意合法的代码片段。
在预处理阶段,编译器会将代码中出现的宏名替换为所指定的替换文本。
宏定义通常放在头文件中或在代码的开头部分声明,以便在整个程序中都可以使用。
通过宏定义,我们可以简化代码、提高代码的可读性,还可以实现一些常量的定义和函数的替换,提高代码的灵活性和可维护性。
二、参数化宏定义在宏定义中,我们可以通过使用参数来使宏更加通用和灵活。
参数化宏定义的语法格式为:#define 宏名(参数列表) 替换文本其中,参数列表是用逗号分隔的形式参数,形式参数在宏定义中相当于占位符,当宏被调用时,实际参数会替换宏定义中的形式参数。
通过参数化宏定义,我们可以实现类似于函数的功能,将一段代码封装为宏,并传入不同的参数进行替换。
参数化宏定义的优势在于展开的宏在编译时期完成,避免了函数调用的开销,适用于一些简单的代码片段。
三、条件编译条件编译是一种编译预处理技术,通过条件判断来选择性地编译代码,以实现不同平台、不同条件下的编译。
条件编译的语法格式为:#ifdef 宏名代码片段1#else代码片段2#endif其中,宏名是一个宏定义的标识符,如果该宏已经定义,则编译代码片段1;否则,编译代码片段2。
条件编译主要用于处理不同平台的适配、实现不同编译选项的控制和条件代码的编译。
在实际开发中,我们可以使用条件编译来定义特定平台的常量、开启或关闭调试信息输出等。
C语言预处理命令--宏定义
C语⾔预处理命令--宏定义⼀、宏讲解1、宏定义宏(Macro),是⼀种的称谓。
⾥的宏是⼀种(Abstraction),它根据⼀系列预定义的规则替换⼀定的⽂本模式。
或在遇到宏时会⾃动进⾏这⼀模式替换。
2、C语⾔宏定义的常规⽤法1) 定义符号常量#define PI 3.1415926#define MAX_N 100002) 定义傻⽠表达式(注意,定义的这种表达式⼀不⼩⼼很容易出现bug,下⽂会讲)#define S(a, b) a * b#define MAX(a, b) (a) > (b) ? (a) : (b)3) 定义代码段#define P(a) {\ printf("%d\n", a);\} ps:编译器对于宏的解析是很严谨的,只能⽀持⼀⾏解析,\是起连接作⽤,表⽰当⾏的宏代码与下⼀⾏宏连接在⼀起,使得编译器当成⼀⾏看待。
3、编译器预定义的宏在C语⾔中,我们有很多预定义的宏,就是C语⾔帮程序员预先定义好的宏,可以让我们使⽤。
宏说明__DATE__ ⽇期:Mmm dd yyyy__TIME__ 时间:hh:mm:ss__LINE__ 当前源⽂件的代码⾏号__FILE__ ⽂件名__func__ 函数名/⾮标准__FUNC__ 函数名/⾮标准__PRETTY_FUNCTION__ 更详细的函数信息/⾮标准4、预定义命令-条件式编译函数说明#ifdef DEBUG 是否定义了DEBUG宏#ifndef DEBUG 是否没有定义DEBUG宏#if MAX_N == 5 宏MAX_N是否等于5#elif MAX_N == 4 否则宏MAX_N是否等于4#else#endif5、预定义命令从上图可以看到: 预编译 将.c ⽂件转化成 .i⽂件 使⽤的gcc命令是:gcc –E 对应于预处理命令cpp 编译 将.c/.h⽂件转换成.s⽂件 使⽤的gcc命令是:gcc –S 对应于编译命令 cc –S 汇编 将.s ⽂件转化成 .o⽂件 使⽤的gcc 命令是:gcc –c 对应于汇编命令是 as 链接 将.o⽂件转化成可执⾏程序 使⽤的gcc 命令是: gcc 对应于链接命令是 ld 总结起来编译过程就上⾯的四个过程:预编译、编译、汇编、链接。
C语言预定义宏用法
C语言预定义宏用法C语言预定义宏用法引导语;预定义的宏不采用任何参数,并且不能重新定义。
以下是店铺分享给大家的.C语言预定义宏用法,欢迎阅读!预定义宏__DATE__进行预处理的日期(“Mmm dd yyyy”形式的字符串文字)__FILE__代表当前源代码文件名的字符串文字__BASE_FILE__获取正在编译的源文件名__LINE__代表当前源代码文件中的行号的整数常量__TIME__源文件编译时间,格式为“hh: mm: ss”__STDC__设置为 1时,表示该实现遵循 C标准__STDC_HOSTED__为本机环境设置为 1,否则设为 0__STDC_VERSION__为C99时设置为199901L__FUNCTION__或者 __func__ 获取所在的函数名(预定义标识符,而非预定义宏)#includeint main (void){printf ("The file is %s\n", __FILE__);printf ("The base_file is %s\n", __BASE_FILE__);printf ("The line is %d\n", __LINE__);printf ("The function is %s\n", __FUNCTION__);printf ("The func is %s\n", __func__);printf ("The date is %s\n", __DATE__);printf ("The time is %s\n", __TIME__);return 0;}输出结果:The file is part.cThe base_file is part.c The line is 6The function is main The func is mainThe date is Nov 22 2016 The time is 15:46:30。
c编译器预定义宏(转载)
c编译器预定义宏(转载)为了⽅便处理⼀些有⽤的信息,预处理器定义了⼀些预处理标识符,也就是预定义宏。
预定义宏的名称都是以“__”(两条下划线)开头和结尾的,如果宏名是由两个单词组成,那么中间以“_”(⼀条下划线)进⾏连接。
并且,宏名称⼀般都由⼤写字符组成。
在⽇常项⽬编程中,预定义宏尤其对多⽬标平台代码的编写通常具有重⼤意义。
通过预定义宏,程序员使⽤“#ifdef”与“#endif”等预处理指令,就可使平台相关代码只在适合于当前平台的代码上编译,从⽽在同⼀套代码中完成对多平台的⽀持。
从这个意义上讲,平台信息相关的宏越丰富,代码的多平台⽀持越准确。
标准 C 语⾔提供的⼀些标准预定义宏如表 1 所⽰。
表 1 常⽤的标准预定义宏宏描述__DATE__丐前源⽂件的编泽⼝期,⽤ “Mmm dd yyy”形式的字符串常量表⽰__FILE__当前源⽂件的名称,⽤字符串常量表⽰__LINE__当前源义件中的⾏号,⽤⼗进制整数常量表⽰,它可以随#line指令改变__TIME__当前源⽂件的最新编译吋间,⽤“hh:mm:ss”形式的宁符串常量表⽰__STDC__如果今前编泽器符合ISO标准,那么该宏的值为1,否则未定义__STDC_VERSION__如果当前编译器符合C89,那么它被定义为199409L;如果符合C99,那么它被定义为199901L:在其他情况下,该宏为宋定义__STDC_HOSTED__(C99)如果当前是宿主系统,则该宏的值为1;如果当前是独⽴系统,则该宏的值为0__STDC_IEC_559_(C99)如果浮点数的实现符合IEC 60559标准时,则该宏的值为1,否则为未定义__STDC_IEC_559_COMPLEX__(C99)如果复数运算实现符合IEC60559标准时,则该宏的伉为1,否则为未定义__STDC_ISO_10646__(C99 )定义为长整型常量,yyyymmL表⽰wchai_t值遵循ISO 10646标准及其指定年⽉的修订补充,否则该宏为未定义除标准 C 语⾔提供的标准宏之外,各种编译器也都提供了⾃⼰的⾃定义预定义宏。
VC中预处理指令与宏定义详解
VC中预处理指令与宏定义详解刚接触到MFC编程的⼈往往会被MFC 向导⽣成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义⼜是C语⾔的⼀个强⼤⼯具。
使⽤它们可以进⾏简单的源代码控制,版本控制,预警或者完成⼀些特殊的功能。
⼀个经典的例⼦ 使⽤预处理与宏定义最经典的例⼦莫过于加在⼀个头⽂件中以避免头⽂件被两次编译。
试想这种的情况,有⼀个⽂件headerfile.h 它被包含在headerfile1.h中,同时在headerfile2.h 中也被包含了,现在有⼀个CPP⽂件,implement.cpp 包含了headerfile1.h 和headerfile2.h:#include “headerfile1.h”#include “headerfile2.h” 假设headerfile.h 中定义了⼀个全局变量 iglobal 。
int iglobal; 在编译的时候编译器两次编译headerfile,也就会发现iglobal被定义了两次,这时就会发⽣变量重定义的编译错误。
传统的解决办法是使⽤#ifdef 以及#endif 来避免头⽂件的重复编译,在上⾯的例⼦中,只需要加上这么⼏⾏:#ifndef smartnose_2002_6_21_headerfile_h#define smartnose_2002_6_21_headerfile_hint iglobal;#endif 仔细的考虑上⾯的宏定义,会发现当编译器编译过⼀次headerfile.h以后,smartnose_2002_6_21_headerfile_h 这个宏就被定义了,以后对headerfile.h的编译都会跳过int iglobal 这⼀⾏。
当然smartnose_2002_6_21_headerfile_h 这个宏是可以任意定义的,但是这个宏本⾝不能和其它⽂件中定义的宏重复,所以MFC在⾃动⽣成的⽂件中总是使⽤⼀个随机产⽣的长度⾮常长的宏,但我觉得这没有必要,我建议在这个宏中加⼊⼀些有意义的信息,⽐⽅作者,⽂件名,⽂件创建时间等等,因为我们有时候会忘记在注释中加⼊这些信息。
ANSIC 预定义的六种宏
ANSIC标准定义了以下6种可供C语言使用的预定义宏:__LINE__ 在源代码中插入当前源代码行号__FILE__ 在源代码中插入当前源代码文件名__DA TE__ 在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕__TIME__ 在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕__STDC__ 当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
__cplusplus标识符__LINE__和__FILE__通常用来调试程序;标识符__DA TE__和__TIME__通常用来在编译后的程序中加入一个时间标志,以区分程序的不同版本;当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1;当用C++编译程序编译时,标识符__cplusplus就会被定义。
__STDC__用来指示是否符合标准C规范。
#ifdef __STDC__/* use standard C future */#else/* use extend C future */#endif在你写dll的时候,因为对于C和C++,编译器会有不同的名字解析规则,所以可以这样用#ifndef __STDC__extern"C" void function();#elsevoid function();#endif【分享】【原创】利用__FILE__, __LINE__, __FUNCTION__等宏跟踪调试程序的运行利用__FILE__, __LINE__, __FUNCTION__跟踪调试程序作为一个Linux系统下的C程序员,你可能发现调试程序是个比较麻烦的工作,虽然已经有gdb,kgdb等专业的调试软件,但如果对这些软件运用不熟练是根本达不到调试程序找出bug的目的的。
又或者你对gdb已经很熟了,但运行gdb开始调试后在哪里设置断点成了你头痛的问题?当然,你可以从程序开始就以单步运行step by step来调试程序,但这会耗去你很多时间。
C语言宏定义与预编译详解
C语言宏定义与预编译详解/findaway123/article/details/6994203 20111.宏定义和函数的区别-------------------------------------------------------------------------------------------------------------------宏:宏定义是C提供的三种预处理功能的其中一种,这三种预处理包括:(1)宏定义(2)文件包含(3)条件编译1.不带参数的宏定义:格式:#define 标识符字符串标示符就是可以替换字符串的宏名称,编译器在进行预处理过程中对使用宏替换的地方展开,用“字符串”替换宏名称,这样做的好处就是可以对数字或者随机数值指定一个有代表意义的名称,提高程序的可读性和通用性,在后期维护中可以方便的修改宏定义中字符串的数值大小或者其他数值类型,从而可以控制整个代码中所有引用宏名的地方。
从占用资源的角度看,编译器对宏替换不会占用或者分配内存,和函数比较,调用子函数需要分配内存,增加系统开销,但子函数可以把程序结构模块化,提高程序的可读性和聚合度,对比之下,宏也可以有参数,如果在程序中为了不调用子函数而减小开销,那么所有过程都写在一个函数中,并且没有自注释的名称,程序的可读性就会降低,毕竟代码是处理现实世界中事务数据关系的一种抽象,但不是一个人的,应该是像一首简洁,优美的诗,描述了美好的事务,所以折中之下,宏替换是个不错的选择。
虽然宏替换占用了编译器的时间,所谓“有得必有失”,减小了程序执行的资源消耗,这未尝不是一种平衡。
宏的一些特点(引用):(1)宏名一般用大写(2)使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改。
例如:数组大小常用宏定义,可以理解数组大小代表具体含义,便于二次维护。
(3)预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
c 宏 预编译
c 宏预编译
C语言中的宏定义是一种预编译指令,用于将一些代码片段或值在程序编译前替换为另一些代码或值。
通过宏定义,我们可以减少代码的重复使用,提高程序的可读性和可维护性。
宏定义在程序中有着广泛的应用,常用于定义常量、函数、条件编译等。
比如,我们可以通过以下方式定义一个常量:
#define PI 3.1415926
这样在程序中就可以直接使用PI来代替3.1415926了。
还可以定义一个简单的函数,比如:
#define SQUARE(x) ((x)*(x))
这样在程序中就可以用SQUARE(x)来代替(x)*(x)了,这在计算中很常见。
在编写程序时,宏定义的使用需要注意一些规则,比如:
1.宏定义的名称必须是合法的标识符,不能与系统关键字冲突。
2.宏定义的值需要用括号括起来,以避免运算优先级可能带来的问题。
3.宏定义会将所有出现的代码片段替换为其定义的值,因此需要注意定义名称和值的语义一致性。
总的来说,宏定义是C语言中非常重要的预编译特性,可以帮助我们提高程序的可读性和可维护性。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
作用:对于__FILE__,__LINE__,__func__这样的宏,在调试程序时是很有用的,因为你可以很容易的知道程序运行到了哪个文件的那一行,是哪个函数。
下面一个例子是打印上面这些预定义的宏的。
__DATE__,__FILE__,__LINE__,__TIME__,__FUNCTION__ C标准中指定了一些预定义的宏,对于编程经常会用到。
下面这个表中就是一些常常用到的预定义宏。
__DATE_ %s_
进行预处理的日期(“Mmm dd yyyy”形式的字符串文字)
__FILE__ %s
代表当前源代码文件名的字符串文字
__LINE__ %d
代表当前源代码中的行号的整数常量
__TIME__ %s
源文件编译时间,格式微“hh:mm:ss”
__FUNCTION__(__fucn__ %s
当前所在函数名
#include
#include
void why_me(;
int main(
{
printf( "The file is %s.\n", __FILE__ ;
printf( "The date is %s.\n", __DATE__ ;
printf( "The time is %s.\n", __TIME__ ;
printf( "This is line %d.\n", __LINE__ ;
printf( "This function is %s.\n", __FUNCTION__ ; why_me(;
return 0;
}
void why_me(
{
printf( "This function is %s\n", __func__ ;
printf( "The file is %s.\n", __FILE__ ;
printf( "This is line %d.\n", __LINE__ ; }。