1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
| 首先三种形式的命令:宏定义,文件包含,条件编译命令。 1、宏定义主要是:#define,#undef 如下: #define PI 3.1415926 #define Max(a,b) a>b?a:b 说明:宏定义在C语言与C++语言中是相通的。 下面举例说明定义宏FAILED用于检测数据的正确性。 ①#define的应用: #define FAILED(Status) ((Status)<0) #include "stdio.h" void main() { int d; printf ("Please input a integer number(n>0)/n"); do { scanf("%d" ,&d); }while(FAILED(d)); } 其中while(FAILED(d))在编译之前被无条件替换为while(d<0)。 宏定义和调用在形式与函数比较相似,但是原理是不同。 ②#undef的应用: #include "stdio.h" void Test(); int main(int argc, char* argv[]) { #define CONST_NAME1 "CONST_NAME1" printf("%s/n",CONST_NAME1); #undef CONST_NAME1 printf("%s/n",CONST_NAME1); { #define CONST_NAME2 "CONST_NAME2" printf("%s/n",CONST_NAME2); } printf("%s/n",CONST_NAME2); return 0; } void Test() { printf("%s/n",CONST_NAME2); } 在程序的编译的时候,系统提示如下信息 error C2065: 'CONST_NAME1' : undeclared identifier 出现上述编译错误的原因是,在第二次应用符号常量CONST_NAME1时,此符号常量已经被取消定义。 2、文件包含: #include <文件名> ,这种属于标准方式,用于编译系统指定的文件。 #include “文件名”,这种属于用户方式,查找用户当前工作的文件夹中的文件,如果不存在则再按照标准方式查询。 3、条件编译(常见的三种形式): ①第一种形式: #if defined(或者是ifdef)<标识符> <程序段1> [#else <程序段2>] #endif ②第二种形式: #if !defined(或者是ifndef)<标识符> <程序段1> [#else <程序段2>] #endif ③第三种形式常用与C++编译器中。 #ifdef … [#elif … ] [#elif …] #else … #endif 4、其他条件编译命令: ①#error: 语法格式如下: #error token-sequence 其主要的作用是在编译的时候输出编译错误信息token-sequence,从方便程序员检查程序中出现的错误。例如下面的程序 #include "stdio.h" int main(int argc, char* argv[]) { #define CONST_NAME1 "CONST_NAME1" printf("%s/n",CONST_NAME1); #undef CONST_NAME1 #ifndef CONST_NAME1 #error No defined Constant Symbol CONST_NAME1 #endif { #define CONST_NAME2 "CONST_NAME2" printf("%s/n",CONST_NAME2); } printf("%s/n",CONST_NAME2); return 0; } 在编译的时候输出如编译信息 fatal error C1189: #error : No defined Constant Symbol CONST_NAME1 ②# pragma 在编写程序的时候,我们经常要用到#pragma 指令来设定编译器的状态或者是指示编译器完成一些特定的动作. 下面介绍了一下该指令的一些常用参数,希望对大家有所帮助! 一般格式:#pragma para 一、 message 参数。 message 它能够在编译信息输出窗 口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为: #pragma message(“消息文本”) 当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条 指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法 #ifdef _X86 #pragma message(“_X86 macro activated!”) #endif 当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_ X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了 二、 另一个使用得比较多的#pragma参数是code_seg,驱动开发使用比较多,应用用不到。格式如: #pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] ) 该指令用来指定函数在.obj文件中存放的节,观察OBJ文件可以使用VC自带的dumpbin命令行程序,函数在.obj文件中默认的存放节 为.text节 如果code_seg没有带参数的话,则函数存放在.text节中 push (可选参数) 将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名 pop(可选参数) 将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名 identifier (可选参数) 当使用push指令时,为压入堆栈的记录指派的一个标识符,当该标识符被删除的时候和其相关的堆栈中的记录将被弹出堆栈 "segment-name" (可选参数) 表示函数存放的节名 例如:
void func1() { }
#pragma code_seg(".my_data1") void func2() { }
#pragma code_seg(push, r1, ".my_data2") void func3() { } int main() { } 三、 #pragma once (比较常用) 这是一个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次 四、 #pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。 BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。 有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级, 如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。 五、 #pragma warning指令 该指令允许有选择性的修改编译器的警告消息的行为 指令格式如下: #pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list...] #pragma warning( push[ ,n ] ) #pragma warning( pop ) 主要用到的警告表示有如下几个: once:只显示一次(警告/错误等)消息 default:重置编译器的警告行为到默认状态 1,2,3,4:四个警告级别 disable:禁止指定的警告信息 error:将指定的警告信息作为错误报告 如果大家对上面的解释不是很理解,可以参考一下下面的例子及说明 #pragma warning( disable : 4507 34; once : 4385; error : 164 ) 等价于: #pragma warning(disable:4507 34) #pragma warning(once:4385) #pragma warning(error:164) 同时这个pragma warning 也支持如下格式: #pragma warning( push [ ,n ] ) #pragma warning( pop ) 这里n代表一个警告等级(1---4)。 #pragma warning( push )保存所有警告信息的现有的警告状态。 #pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告 等级设定为n。 #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的 一切改动取消。例如: #pragma warning( push ) #pragma warning( disable : 4705 ) #pragma warning( disable : 4706 ) #pragma warning( disable : 4707 ) #pragma warning( pop ) 在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707) 在使用标准C++进行编程的时候经常会得到很多的警告信息,而这些警告信息都是不必要的提示, 所以我们可以使用#pragma warning(disable:4786)来禁止该类型的警告 在vc中使用ADO的时候也会得到不必要的警告信息,这个时候我们可以通过 #pragma warning(disable:4146)来消除该类型的警告信息 六、 pragma comment(...) 该指令的格式为 #pragma comment( "comment-type" [, commentstring] ) 该指令将一个注释记录放入一个对象文件或可执行文件中, comment-type(注释类型):可以指定为五种预定义的标识符的其中一种 五种预定义的标识符为: compiler:将编译器的版本号和名称放入目标文件中,本条注释记录将被编译器忽略 如果你为该记录类型提供了commentstring参数,编译器将会产生一个警告 例如:#pragma comment( compiler ) exestr:将commentstring参数放入目标文件中,在链接的时候这个字符串将被放入到可执行文件中, 当操作系统加载可执行文件的时候,该参数字符串不会被加载到内存中.但是,该字符串可以被 dumpbin之类的程序查找出并打印出来,你可以用这个标识符将版本号码之类的信息嵌入到可 执行文件中! lib:这是一个非常常用的关键字,用来将一个库文件链接到目标文件中 常用的lib关键字,可以帮我们连入一个库文件。 例如: #pragma comment(lib, "user32.lib") 该指令用来将user32.lib库文件加入到本工程中 linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中 设置的链接选项,你可以指定/include选项来强制包含某个对象,例如: #pragma comment(linker, "/include:__mySymbol") 你可以在程序中设置下列链接选项 /DEFAULTLIB /EXPORT /INCLUDE /MERGE /SECTION 这些选项在这里就不一一说明了,详细信息请看msdn! user:将一般的注释信息放入目标文件中commentstring参数包含注释的文本信息,这个注释记录将被链接器忽略 例如: #pragma comment( user, "Compiled on " __DATE__ " at " __TIME__ ) 补充一个 #pragma pack(n) 控制对齐 如 #pragma pack(push) #pragma pack(1) struct s_1{ char szname[1]; int a; }; #pragma pack(pop) struct s_2{ char szname[1]; int a; }; 则 printf("s_1 size : %d/n", sizeof(struct s_1)); printf("s_2 size : %d/n", sizeof(struct s_2)); 得到5,8。 (5) #pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体 外观的定义。 件。 (8) progma pack(n) 指定结构体对齐方式!#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏 移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条 件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数; 否则必须为n的倍数。下面举例说明其用法。 #pragma pack(push) #pragma pack(4) struct test { char m1; double m4; int m3; }; #pragma pack(pop) 为测试该功能,可以使用sizeof()测试结构体的长度! ③#line 此命令主要是为强制编译器按指定的行号,开始对源程序的代码重新编号,在调试的时候,可以按此规定输出错误代码的准确位置。 形式1 语法格式如下: # line constant “filename”:其作用是使得其后的源代码从指定的行号constant重新开始编号,并将当前文件的名命名为filename。例如下面的程序如下: #include "stdio.h" void Test(); #line 10 "Hello.c" int main(int argc, char* argv[]) { #define CONST_NAME1 "CONST_NAME1" printf("%s/n",CONST_NAME1); #undef CONST_NAME1 printf("%s/n",CONST_NAME1); { #define CONST_NAME2 "CONST_NAME2" printf("%s/n",CONST_NAME2); } printf("%s/n",CONST_NAME2); return 0; } void Test() { printf("%s/n",CONST_NAME2); } 提示如下的编译信息:Hello.c(15) : error C2065: 'CONST_NAME1' : undeclared identifier 表示当前文件的名称被认为是Hello.c, #line 10 "Hello.c"所在的行被认为是第10行,因此提示第15行出错。 形式2语法格式如下: # line constant:其作用在于编译的时候,准确输出出错代码所在的位置(行号),而在源程序中并不出现行号,从而方便程序员准确定位。 ④运算符#和## #@ ‘’ 在ANSI C中为预编译指令定义了两个运算符——#和##。# 的作用是实现文本替换,例如 #define HI(x) printf("Hi,"#x"/n"); void main() { HI(John); } 程序的运行结果:Hi,John 在预编译处理的时候, "#x"的作用是将x替换为所代表的字符序列。在本程序中x为John,所以构建新串“Hi,John”。 ##的作用是串连接。例如 #define CONNECT(x,y) x##y void main() { int a1,a2,a3; CONNECT(a,1)=0; CONNECT(a,2)=12; a3=4; printf("a1=%d/ta2=%d/ta3=%d",a1,a2,a3); } 程序的运行结果为:a1=0 a2=12 a3=4 在编译之前, CONNECT(a,1)被翻译为a1, CONNECT(a,2)被翻译为a2。 ⑤预定义常量 __LINE__:当前源代码的行号,为整型常量 __FILE__:当前编译程序文件的名称,为字符串 __DATE__:编译程序文件日期,为字符串(”MM DD YYYY"形式,如” Jan 19 1993”) __TIME__:编译程序文件时间,为字符串("hh:mm:ss"形式,如”08:30:23”) __STDC__:ANSI C标志,整型常量1,说明此程序兼容ANSI C标准。
.2 #line等 #error指令将使编译器显示一条错误信息,然后停止编译。 #line指令改变_LINE_与_FILE_的内容,它们是在编译程序中预先定义的标识符。 #pragma指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。
#line 100 #include<stdio.h> int main() { printf("Hello World!\n"); printf("%d",__LINE__); return 0; }
|