1.前言想象这样一个工作场景:你正在为一个项目写一个函数库,别人调用库中提供的函数,然后你在中找到这个函数库A是多余的。剧情完美的你,只想把这个函数A废弃掉,这个时候千万不能直接删掉,因为你不知道别人有多少地方调用了这个函数。如何更好地应对这种情况?这篇简短的文章将讨论这个问题。二、运行过程1、第一版只有3个库测试文件:api.h、api.c和main.capi.h和api.c:库文件,编译得到libapi.so;main.c:要生成可执行程序,使用上面生成的库libapi.so;api.h。api.h文件的内容:声明了2个函数。api.c文件的内容:定义了2个函数。编译得到库文件libapi.so。编译指令:gcc-fPIC-sharedapi.c-olibapi.somain.c文件内容:编译后的可执行文件:gccmain.c-omain-L./-Wl,-rpath=./-lapi上面代码的简单性,等价到地狱世界。2.库的第二个版本现在,如果你觉得init函数是多余的,想去掉它,你可以这样修改。在api.c文件中,删除init()函数。api.h文件内容修改如下:关键代码是这一行:#defineinit()(1)API_DEPRECATED由于api.c文件已经删除了这个函数,但是main.c文件又调用了这个函数,所以宏定义的形式提供了符号init。也就是说:在第一个版本中,main.c文件中的init是一个函数,由编译器处理,在链接阶段从libapi.so库中找到这个函数的地址;在第二个版本中,init被定义为一个宏,并在预处理阶段被以下(1)API_DEPRECATED取代。(1)是宏替换时的表达式。因为这个函数可能用在if条件判断中,所以需要有返回值。API_DEPRECATED是另一个宏定义,它被扩展为允许编译器在编译可执行程序时打印一条消息。在编译一个可执行文件时,编译器输出如下一段话:gccmain.c-omain-L./-Wl,-rpath=./-lapi这样就达到了初衷!即提醒用户:该功能已弃用,最好不要使用!三_Prama其他用法_Pragma类似于微软特有的__pragma关键字,但它是标准的一部分。它是在C99中为C引入的。对于C++,它是在C++11中引入的。它允许将指令放入宏定义中。1、处理头文件中重复包含头文件。为了防止重复包含,一般有三种处理方式:(1)第一种处理方式:#ifdefMY_API#defineMY_API//头文件内容#endif(2)第二种两种处理方式#pragmaonce//头文件内容以上两种方法可以防止同一个头文件被重复包含,但是还是有一些区别的。第一种方法:预处理器仍然需要搜索文件,然后打开文件,读取文件内容后,检查是否定义了MY_API。第二种方式:可以加快编译速度,因为这是一种高端机制;编译器会自动比较文件名,不需要判断头文件中的#ifndef和#endif,省去了中间的查找、打开和读取操作。(3)第三种处理方式_Pragma("once")与第二种方式的区别在于:#pragma:是预处理指令,用来向编译器传递一些语言标准以外的信息,不能使用在宏中;_Pragma:是属于语言标准的操作符,所以可以嵌套在宏中,就像上面的例子;#pragma是编译器的扩展,也就是说它是由编译器实现的确定的,可能A编译器支持,B编译器不一定支持,虽然这种可能性比较小。_Pragma运算符是语言级别的标准。既然是标准,编译器就得按照标准来,所以也推荐这种方式。记得侯捷老师在C++视频课上说过:我们写代码的时候,不仅要保证功能的正确性,还要把代码写得堂堂正正!我觉得使用_Pragma可能比#ifndef更慷慨。2.输出编译信息#pragmamessage("the#pragmaway")_Pragma("message(\"the_Pragmaway\")")上面两行内容输出信息是一样的,需要注意嵌套的双引号需要反斜杠才能转义。
