攻击asp网站,电商网站模板html,汽车网站flash模板,做网站用别人图片文章会侵权吗C语言##预算符 和#运算符一样#xff0c;##运算符可以用于宏函数的替换部分。这个运算符把两个语言符号组合成单个语言符号。看例子#xff1a;#define XNAME(n) x ## n如果这样使用宏#xff1a;XNAME(8)则会被展开成这样#xff1a;x8看明白了没#xff1f;
##就是个粘合…C语言##预算符 和#运算符一样##运算符可以用于宏函数的替换部分。这个运算符把两个语言符号组合成单个语言符号。看例子#define XNAME(n) x ## n如果这样使用宏XNAME(8)则会被展开成这样x8看明白了没
##就是个粘合剂将前后两部分粘合起来。C语言#运算符 #也是预处理是的你可以这么认为。那怎么用它呢? 别急先看下面例子#define SQR(x) printf(The square of x is %d.\n, ((x)*(x)));如果这样使用宏SQR(8);则输出为The square of x is 64.注意到没有引号中的字符x 被当作普通文本来处理而不是被当作一个可以被替换的语言符号。假如你确实希望在字符串中包含宏参数那我们就可以使用“#”它可以把语言符号转化为字符串。上面的例子改一改#define SQR(x) printf(The square of #x is %d.\n, ((x)*(x)));再使用SQR(8);则输出的是The square of 8 is 64.很简单吧相信你现在已经明白#号的使用方法了。 C语言#pragma预处理 在所有的预处理指令中#pragma 指令可能是最复杂的了它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma 指令对每个编译器给出了一个方法,在保持与C 和C 语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:#pragma para其中para 为参数下面来看一些常用的参数。一、#pragma message message 参数Message 参数是我最喜欢的一个参数它能够在编译信息输出窗口中输出相应的信息这对于源代码信息的控制是非常重要的。其使用方法为#pragma message(“消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候我们自己有可能都会忘记有没有正确的设置这些宏此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86 这个宏可以用下面的方法#ifdef _X86#Pragma message(“_X86 macro activated!”)#endif当我们定义了_X86 这个宏以后应用程序在编译时就会在编译输出窗口里显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。二、#pragma code_seg 另一个使用得比较多的pragma 参数是code_seg。格式如#pragma code_seg( [section-name[,section-class] ] )它能够设置程序中函数代码存放的代码段当我们开发驱动程序的时候就会使用到它。三、#pragma once
#pragma once (比较常用只要在头文件的最开始加入这条指令就能够保证头文件被编译一次这条指令实际上在Visual C6.0 中就已经有了但是考虑到兼容性并没有太多的使用它。四、#pragma hdrstop #pragma hdrstop 表示预编译头文件到此为止后面的头文件不进行预编译。BCB 可以预编译头文件以加快链接的速度但如果所有头文件都进行预编译又可能占太多磁盘空间所以使用这个选项排除一些头文件。有时单元之间有依赖关系比如单元A 依赖单元B所以单元B 要先于单元A 编译。你可以用#pragma startup 指定编译优先级如果使用了#pragma package(smart_init) BCB就会根据优先级的大小先后编译。五、#pragma resource #pragma resource *.dfm表示把*.dfm 文件中的资源加入工程。*.dfm 中包括窗体外观的定义。六、#pragma warning #pragma warning( disable : 4507 34; once : 4385; error : 164 )等价于#pragma warning(disable:4507 34) // 不显示4507 和34 号警告信息#pragma warning(once:4385) // 4385 号警告信息仅报告一次#pragma warning(error:164) // 把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 )在这段代码的最后重新保存所有的警告信息(包括47054706 和4707)。七、#pragma comment #pragma comment(...)该指令将一个注释记录放入一个对象文件或可执行文件中。常用的lib 关键字可以帮我们连入一个库文件。比如#pragma comment(lib, user32.lib)该指令用来将user32.lib 库文件加入到本工程中。linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include 选项来强制包含某个对象,例如:#pragma comment(linker, /include:__mySymbol)八、#pragma pack 这里重点讨论内存对齐的问题和#pragma pack的使用方法。什么是内存对齐先看下面的结构struct TestStruct1{char c1;short s;char c2;int i;};假设这个结构的成员在内存中是紧凑排列的假设c1 的地址是0那么s 的地址就应该是1c2 的地址就是3i 的地址就是4。也就是c1 地址为00000000, s 地址为00000001, c2地址为00000003, i 地址为00000004。可是我们在Visual C6.0 中写一个简单的程序struct TestStruct1 a;printf(c1 %p, s %p, c2 %p, i %p\n,(unsigned int)(void*)a.c1 - (unsigned int)(void*)a,(unsigned int)(void*)a.s - (unsigned int)(void*)a,(unsigned int)(void*)a.c2 - (unsigned int)(void*)a,(unsigned int)(void*)a.i - (unsigned int)(void*)a);运行输出c1 00000000, s 00000002, c2 00000004, i 00000008。为什么会这样这就是内存对齐而导致的问题。1、为什么会有内存对齐字双字和四字在自然边界上不需要在内存中对齐。对字双字和四字来说自然边界分别是偶数地址可以被4 整除的地址和可以被8 整除的地址。无论如何为了提高程序的性能数据结构尤其是栈应该尽可能地在自然边界上对齐。原因在于为了访问未对齐的内存处理器需要作两次内存访问然而对齐的内存访问仅需要一次访问。一个字或双字操作数跨越了4 字节边界或者一个四字操作数跨越了8 字节边界被认为是未对齐的从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的能够在一个总线周期中被访问。某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐这些指令将会产生一个通用保护异常。双四字的自然边界是能够被16 整除的地址。其他的操作双四字的指令允许未对齐的访问不会产生通用保护异常然而需要额外的内存总线周期来访问内存中未对齐的数据。缺省情况下编译器默认将结构、栈中的成员数据进行内存对齐。因此上面的程序输出就变成了c1 00000000, s 00000002, c2 00000004, i 00000008。编译器将未对齐的成员向后移将每一个都成员对齐到自然边界上从而也导致了整个结构的尺寸变大。尽管会牺牲一点空间成员之间有部分内存空闲但提高了性能。也正是这个原因我们不可以断言sizeof(TestStruct1)的结果为8。在这个例子中sizeof(TestStruct1)的结果为12。2、如何避免内存对齐的影响那么能不能既达到提高性能的目的又能节约一点空间呢有一点小技巧可以使用。比如我们可以将上面的结构改成struct TestStruct2{char c1;char c2;short s;int i;};这样一来每个成员都对齐在其自然边界上从而避免了编译器自动对齐。在这个例子中sizeof(TestStruct2)的值为8。这个技巧有一个重要的作用尤其是这个结构作为API的一部分提供给第三方开发使用的时候。第三方开发者可能将编译器的默认对齐选项改变从而造成这个结构在你的发行的DLL 中使用某种对齐方式而在第三方开发者哪里却使用另外一种对齐方式。这将会导致重大问题。比如TestStruct1 结构我们的DLL 使用默认对齐选项对齐为c1 00000000, s 00000002, c2 00000004, i 00000008同时sizeof(TestStruct1)的值为12。而第三方将对齐选项关闭导致c1 00000000, s 00000001, c2 00000003, i 00000004同时sizeof(TestStruct1)的值为8。除此之外我们还可以利用#pragma pack来改变编译器的默认对齐方式当然一般编译器也提供了一些改变对齐方式的选项这里不讨论。使用指令#pragma pack (n)编译器将按照n 个字节对齐。 使用指令#pragma pack ()编译器将取消自定义字节对齐方式。在#pragma pack (n)和#pragma pack ()之间的代码按n 个字节对齐。但是成员对齐有一个重要的条件,即每个成员按自己的方式对齐.也就是说虽然指定了按n 字节对齐,但并不是所有的成员都是以n 字节对齐。其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是n 字节)中较小的一个对齐即min( n, sizeof( item )) 。并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节。看如下例子#pragma pack(8)struct TestStruct4{char a;long b;};struct TestStruct5{char c;TestStruct4 d;long long e;};#pragma pack()问题A)sizeof(TestStruct5) ?B)TestStruct5 的c 后面空了几个字节接着是d?TestStruct4 中,成员a 是1 字节默认按1 字节对齐,指定对齐参数为8,这两个值中取1,a按1 字节对齐;成员b 是4 个字节,默认是按4 字节对齐,这时就按4 字节对齐,所以sizeof(TestStruct4)应该为8;TestStruct5 中,c 和TestStruct4 中的a 一样,按1 字节对齐,而d 是个结构,它是8 个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个, TestStruct4 的就是4.所以,成员d 就是按4 字节对齐.成员e 是8 个字节,它是默认按8字节对齐,和指定的一样,所以它对到8 字节的边界上,这时,已经使用了12 个字节了,所以又添加了4 个字节的空,从第16 个字节开始放置成员e.这时,长度为24,已经可以被8(成员e 按8字节对齐)整除.这样,一共使用了24 个字节.内存布局如下*表示空闲内存1 表示使用内存。单位为1byetea bTestStruct4 的内存布局1***,1111,cTestStruct4.a TestStruct4.b dTestStruct5 的内存布局 1***, 1***, 1111, ****11111111这里有三点很重要首先每个成员分别按自己的方式对齐,并能最小化长度。 其次复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度。 然后对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐。补充一下,对于数组,比如:char a[3];它的对齐方式和分别写3 个char 是一样的.也就是说它还是按1 个字节对齐.如果写: typedef char Array3[3];Array3 这种类型的对齐方式还是按1个字节对齐,而不是按它的长度。但是不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个。另外注意别的#pragma pack 的其他用法#pragma pack(push) //保存当前对其方式到packing stack#pragma pack(push,n) 等效于#pragma pack(push)#pragma pack(n) //n1,2,4,8,16 保存当前对齐方式设置按n 字节对齐#pragma pack(pop) //packing stack 出栈并将对其方式设置为出栈的对齐方 C语言#line预处理 #line 的作用是改变当前行数和文件名称它们是在编译程序中预先定义的标识符命令的基本形式如下#line number[filename]其中[]内的文件名可以省略。例如#line 30 a.h其中文件名a.h 可以省略不写。这条指令可以改变当前的行号和文件名例如上面的这条预处理指令就可以改变当前的行号为30文件名是a.h。初看起来似乎没有什么用不过他还是有点用的那就是用在编译器的编写中我们知道编译器对C 源码编译过程中会产生一些中间文件通过这条指令可以保证文件名是固定的不会被这些中间文件代替有利于进行分析。 C语言#error预处理
#error 预处理指令的作用是编译程序时只要遇到#error 就会生成一个编译错误提示消息并停止编译。其语法格式为#error error-message注意宏串error-message 不用双引号包围。遇到#error 指令时错误信息被显示可能同时还显示编译程序作者预先定义的其他内容。关于系统所支持的error-message 信息请查找相关资料这里不浪费篇幅来做讨论。 C语言文件包含#include 文件包含是预处理的一个重要功能它可用来把多个源文件连接成一个源文件进行编译结果将生成一个目标文件。语言提供#include 命令来实现文件包含的操作它实际是宏替换的延伸有两种格式一、#include filename其中filename 为要包含的文件名称用尖括号括起来也称为头文件表示预处理到系统规定的路径中去获得这个文件即C 编译系统所提供的并存放在指定的子目录下的头文件。找到文件后用文件内容替换该语句。2、#include “filename”其中filename 为要包含的文件名称。双引号表示预处理应在当前目录中查找文件名为filename 的文件若没有找到则按系统指定的路径信息搜索其他目录。找到文件后用文件内容替换该语句。需要强调的一点是#include 是将已存在文件的内容嵌入到当前文件中。另外关于#include 的路径也有点要说明include 支持相对路径格式如trackant(蚁迹寻踪)所写.代表当前目录..代表上层目录。 C语言条件编译#ifdef 条件编译的功能使得我们可以按不同的条件去编译不同的程序部分因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。条件编译有三种形式下面分别介绍第一种形式#ifdef 标识符程序段1#else程序段2#endif它的功能是如果标识符已被#define 命令定义过则对程序段1 进行编译否则对程序段2进行编译。如果没有程序段2(它为空)本格式中的#else 可以没有即可以写为#ifdef 标识符程序段#endif第二种形式#ifndef 标识符程序段1#else程序段2#endif与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是如果标识符未被#define 命令定义过则对程序段1 进行编译否则对程序段2 进行编译。这与第一种形式的功能正相反。第三种形式#if 常量表达式程序段1#else程序段2#endif它的功能是如常量表达式的值为真(非0)则对程序段1 进行编译否则对程序段2 进行编译。因此可以使程序在不同条件下完成不同的功能。C语言宏定义#define
一、数值宏常量
#define 宏定义是个演技非常高超的替身演员但也会经常耍大牌的所以我们用它要慎之又慎。它可以出现在代码的任何地方从本行宏定义开始以后的代码就就都认识这个宏了也可以把任何东西定义成宏。因为编译器会在预编译的时候用真身替换替身而在我们的代码里面却又用常常用替身来帮忙。看例子#define PI 3.141592654在此后的代码中你尽可以使用PI 来代替3.141592654而且你最好就这么做。不然的话如果我要把PI 的精度再提高一些你是否愿意一个一个的去修改这串数呢你能保证不漏不出错而使用PI 的话我们却只需要修改一次。这种情况还不是最要命的我们再看一个例子#define ERROR_POWEROFF -1如果你在代码里不用ERROR_POWEROFF 这个宏而用-1尤其在函数返回错误代码的时候往往一个开发一个系统需要定义很多错误代码。肯怕上帝都无法知道-1 表示的是什么意思吧。这个-1我们一般称为“魔鬼数”上帝遇到它也会发狂的。所以我奉劝你代码里一定不要出现“魔鬼数”。第一章我们详细讨论了const 这个关键字我们知道const 修饰的数据是有类型的而define 宏定义的数据没有类型。为了安全我建议你以后在定义一些宏常数的时候用const代替编译器会给const 修饰的只读变量做类型校验减少错误的可能。但一定要注意const修饰的不是常量而是readonly 的变量const 修饰的只读变量不能用来作为定义数组的维数也不能放在case 关键字后面。二、字符串宏常量 除了定义宏常数之外经常还用来定义字符串尤其是路径A),#define ENG_PATH_1 E:\English\listen_to_this\listen_to_this_3B),#define ENG_PATH_2 “E:\English\listen_to_this\listen_to_this_3”噢到底哪一个正确呢如果路径太长一行写下来比较别扭怎么办用反斜杠接续符啊C), #define ENG_PATH_3 E:\English\listen_to_this\listen\_to_this_3还没发现问题这里用了4 个反斜杠到底哪个是接续符回去看看接续符反斜杠。反斜杠作为接续符时在本行其后面不能再有任何字符空格都不行。所以只有最后一个反斜杠才是接续符。至于A)和B)那要看你怎么用了既然define 宏只是简单的替换那给ENG_PATH_1 加上双引号不就成了“ENG_PATH_1”。但是请注意有的系统里规定路径的要用双反斜杠“\\”,比如#define ENG_PATH_4 E:\\English\\listen_to_this\\listen_to_this_3三、用define 宏定义注释符号 上面对define 的使用都很简单再看看下面的例子#define BSC //#define BMC /*#define EMC */D),BSC my single-line commentE),BMC my multi-line comment EMCD)和E)都错误为什么呢因为注释先于预处理指令被处理,当这两行被展开成//…或/*…*/时,注释已处理完毕,此时再出现//…或/*…*/自然错误.因此,试图用宏开始或结束一段注释是不行的。四、用define 宏定义表达式 这些都好理解下面来点有“技术含量”的定义一年有多少秒#define SEC_A_YEAR 60*60*24*365这个定义没错吧很遗憾很有可能错了至少不可靠。你有没有考虑在16 位系统下把这样一个数赋给整型变量的时候可能会发生溢出一年有多少秒也不可能是负数吧。修改一下#define SEC_A_YEAR 60*60*24*365UL又出现一个问题这里的括号到底需不需要呢继续看一个例子定义一个宏函数求x 的平方#define SQR (x) x * x对不对试试假设x 的值为10SQR (x)被替换后变成10*10。没有问题。再试试假设x 的值是个表达式101SQR (x)被替换后变成101*101。问题来了这并不是我想要得到的。怎么办括号括起来不就完了#define SQR (x) x*x最外层的括号最好也别省了看例子求两个数的和#define SUM (x) xx如果x 的值是个表达式5*3,而代码又写成这样SUM (x)* SUM (x)。替换后变成5*35*3*5*35*3。又错了所以最外层的括号最好也别省了。我说过define 是个演技高超的替身演员但也经常耍大牌。要搞定它其实很简单别吝啬括号就行了。注意这一点宏函数被调用时是以实参代换形参。而不是“值传送”。留四个问题A)上述宏定义中“SUM”、“SQR”是宏吗B)#define EMPTY这样定义行吗C)打印上述宏定义的值printf“SUM (x)”结果是什么D)“#define M 100”是宏定义吗五、宏定义中的空格 另外还有一个问题需要引起注意看下面例子#define SUM x xx这还是定义的宏函数SUMx吗显然不是。编译器认为这是定义了一个宏SUM其代表的是x xx。为什么会这样呢其关键问题还是在于SUM 后面的这个空格。所以在定义宏的时候一定要注意什么时候该用空格什么时候不该用空格。这个空格仅仅在定义的时候有效在使用这个宏函数的时候空格会被编译器忽略掉。也就是说上一节定义好的宏函数SUMx在使用的时候在SUM 和x之间留有空格是没问题的。比如SUM3和SUM 3的意思是一样的。六、#undef #undef 是用来撤销宏定义的用法如下#define PI 3.141592654…// code#undef PI//下面的代码就不能用PI 了它已经被撤销了宏定义。也就是说宏的生命周期从#define 开始到#undef 结束。很简单但是请思考一下这个问题#define X 3#define Y X*2#undef X#define X 2int zY;z 的值为多少 C语言预处理命令有哪些 往往我说今天上课的内容是预处理时便有学生质疑预处理不就是include 和define么这也用得着讲啊。是的非常值得讨论即使是include 和define。但是预处理仅限于此吗远远不止。先看几个个常识性问题A)预处理是C 语言的一部分吗B)包含“#”号的都是预处理吗C)预处理指令后面都不需要加“”号吗不要急着回答先看看ANSI 标准定义的C 语言预处理指令 另外ANSI 标准C 还定义了如下几个宏
_LINE_ 表示正在编译的文件的行号_FILE_ 表示正在编译的文件的名字_DATE_ 表示编译时刻的日期字符串例如 25 Dec 2007_TIME_ 表示编译时刻的时间字符串例如 12:30:55_STDC_ 判断该文件是不是定义成标准C 程序 如果编译器不是标准的则可能仅支持以上宏的一部分或根本不支持。当然编译器也有可能还提供其它预定义的宏名。注意宏名的书写由标识符与两边各二条下划线构成。相信很多初学者甚至一些有经验的程序员都没有完全掌握这些内容下面就一一详细讨论这些预处理指令。