C语言--预处理
文章目录
- 一、宏定义
- 1、宏定义
- 2、有参宏和无参宏
- 3、预定义宏:C语言预处理器提供了一些内置的宏,用于获取编译时的信息。
- 二、文件包含
- 1、标准方式:
- 2、通用方式:
- 3、包含保护
- 三、条件编译
- 1、如果标识符被定义过
- 2、如果标识符未被定义过
- 3、根据表达式判定
- 4、实例:
- 四、编译器配置
- 1、Pragma 指令格式
- 2、Pragma 指令常用参数
- #pragma message
- #pragma code_seg
- #pragma once
- #pragma hdrstop
- #pragma resource
- #pragma warning
- #pragma disable
- #pragma data_seg
C语言预处理是C语言编译过程中的一个重要阶段,它在编译器对源代码进行编译之前,由预处理器对源代码进行一系列文本处理操作。预处理指令以井号(#)开头,它们不是C语言的语句,因此不需要以分号(;)结尾。
一、宏定义
宏定义的作用就是在程序中某段代码的一个别名,宏定义主要是为程序调试、移植等提供便利是一个非常实用的功能。
1、宏定义
所有的宏命令行都以符号“#define”开头,并且结尾不用分号,
例: #define PI 3.14
宏定义的作用是在编译处理时,将源程序中所有标识替换成语名序列,宏定义分为有参宏和无参宏。
需要注意的是,宏名一般用大写字母,以便于与变量名的区别,在编译与处理时,宏名与字符串进行替换时,不做语法检查,只是简单的字符替换,只有在编译时才对已展开的宏名的源程序进行语法检查,宏名的有效范围是从定义位置到文件结束,如果需要终止红定义的作用域可以用 “#undef”命令,
例如:
#define PI 3.14
……
#undef PI
2、有参宏和无参宏
无参宏指的是执行单一的替换功能的宏定义,在使用无参红时要注意以下两个问题,
1.宏定义时可以引用已经定义的宏名如以下所示:
#define R 2.0
#define PI 3.14
#define L 2RPI
2.对程序中用双引号括起来的字符的字符串内的字符,不进行宏定义的替换操作。
有参宏可以让我们在宏定义时,还可以带参数扩大宏的应用范围,有参宏的格式:
#define 标识符(参数表) 字符串
它的作用是在编译预处理时,将原程序中所有的标识符替换成字符串,并且将字符串中的参数用实际使用的参数替换,
例如:
#define S(a,b) (a*b)/2
需要注意的是,在宏定义时,宏名和参数之间不能有空格,否则空格后面的所有字符序列都作为替换的字符串,带参数的宏展开时,只做简单的字符和参数替换,不进行任何计算操作,所以一般在宏定义时字符串中的形式参数外面加一个小括号
例如:
#define L( r ) 2*PI*r
如果输入 L(2+3) 则编译后变为 2*3.14*2+3 不是2*3.14*5
解决方法:
#define L( r ) 2*PI*( r )
3、预定义宏:C语言预处理器提供了一些内置的宏,用于获取编译时的信息。
宏 描述
__FILE__ 当前源文件名(字符串)
__LINE__ 当前行号(整数)
__DATE__ 编译日期(字符串)
__TIME__ 编译时间(字符串)
__STDC__ 如果编译器遵循ANSI C标准,则定义为1。
二、文件包含
文件包含的功能是将指定的文件内容嵌入到一个原文件中,文件包含共有以下两种格式:
1、标准方式:
只按照标准方式在C语言编译器的C函数库头文件中查找要包含的文件。
#include <> 一般用于包含系统头文件,诸如 stdlib.h 、stdio.h 、iostream等;
#include <xxxx.x>
2、通用方式:
先在原文件所在的目录中查找要包含的文件,若未能找到,则在按照标准方式查找。
#include " " 一般用于包含自定义头文件,如test.h、declare.h等
#include “xxxx.x”
3、包含保护
为了避免头文件被重复包含,通常会在头文件中使用包含保护:
#ifndef MY_HEADER_H#define MY_HEADER_H// 头文件内容#endif
三、条件编译
#ifdef #ifndef #if #else #endif
条件编译一般用于某种条件判断是否要编译某一段代码,常用的格式如下:
1、如果标识符被定义过
#ifdef 标识符/*程序段1*/#else/*程序段2*/#endif
2、如果标识符未被定义过
#ifndef 标识符/*程序段1*/#else/*程序段2*/#endif
3、根据表达式判定
#if 表达式/*程序段1*/#else/*程序段2*/#endif
4、实例:
#include <stdlib.h>#include <stdio.h>#define ABC 123456int main(){#ifdef ABC printf("定义了ABC !\n");#elseprintf(L"未定义ABC !\n");#endif#ifndef CBAprintf("未定义CBA!\n");#endifsystem("pause");return 0;}
四、编译器配置
#Pragma 指令的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。
#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
1、Pragma 指令格式
格式一般为:
#pragma Para //其中Para 为参数。
2、Pragma 指令常用参数
#pragma message
Message 参数能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。
其使用方法为:
#pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
#pragma code_seg
设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
格式:
#pragma code_seg([“section-name”[,“section-class”]])
#pragma once
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。
#pragma hdrstop
#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。
#pragma resource
#pragma resource ".dfm"表示把.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。
#pragma warning
#pragma warning(disable:450734;once:4385;error:164)
等价于:
#pragma warning(disable:450734)//不显示4507和34号警告信息
#pragma warning(once:4385)//4385号警告信息仅报告一次
#pragma warning(error:164)//把164号警告信息作为一个错误。
#pragma disable
在函数前声明,只对一个函数有效。该函数调用过程中将不可被中断。一般在C51中使用较多。
#pragma data_seg
用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:
#pragma data_seg(“shareddata”)
HWND sharedwnd=NULL;//共享数据
#pragma data_seg()
1、#pragma data_seg()一般用于DLL中。也就是说,在DLL中定义一个共享的有名字的数据段。
最关键的是:这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量。
2、共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。