【C&C++ Tip 01】#define, __cplusplus, extern "C"

来源:互联网 发布:浙江省noip知乎 编辑:程序博客网 时间:2024/05/17 03:31

注:基础知识学习积累所用,参考网上资料,后续扩展。


【定义】#define是预处理指令,在编译预处理时进行简单的文本替换,不做类型检查。
【格式】#define KEY value, 编译会把所有KEY文本替换为value.
【目的】1. #ifndef和 #define组合,一般用于头文件中,用以实现防止多个文件对此同一个头文件的重复引用。
#ifndef <标识>
#define <标识>
……
// include or define sth.
……
#endif

2.#ifdef和 #define组合,可以实现加入自己需要的模块(源文件).
#ifdef MYSELF_H
#include “myself.c”
#endif

3.#define可以进行宏定义常量
可以对一些常见的变量,字符串等,进行宏定义,系统在编译期间,就会自动替换。


1. #define只带一个参数(例#define JDK_1.4)

定义宏,并在预处理过程中将其替换为空字符串(即删除),表示已定义JDK_1.4, 通过#ifdef判断返回true。
这样做主要是为了标记某些内容,使程序阅读者能够清楚标记表明的意义,同时又不影响被编译的源代码。
通常这些标记能被条件编译的预处理命令#ifdef、#ifndef检测到, 可在别处引用该文件进,用此空的宏作条件判断。

2. #define Conn(x,y) x##y

x##y, 表示x连接y,举例说:
int n = Conn(123, 456); 结果就是n=123456;
char* str = Conn(“asdf”, “adf”)结果就是 str = “asdfadf”;
如#define A(x) T_##x == A(1)——>T_1

3. #define ToChar(x) #@x

#@x, 其实就是给x加上单引号,结果返回是一个const char。举例说:
char a = ToChar(1);结果就是a=’1’;
做个越界试验char a = ToChar(123);结果是a=’3’;
但是如果你的参数超过四个字符,编译器就给给你报错了!error C2015: too many characters in constant

4. #define ToString(x) #x

#x, 是给x加双引号。举例说:
char* str = ToString(123132);就成了str=”123132”;

5. Linux中min宏定义:
#define min(x, y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })

(void) (&_x == &_y); 用于判断输入的两个值的类型是否是一致的。如果不一致,那么编译器就会做出如下警告:warning: comparison of distinct pointer types lacks a cast。我们此处想要实现的目的是,在计算两个数的最小值之前,希望去判断一下两个值的类型是否一 致,而由于C语言本身不支持我们去做类似于这样的操作typeof(_x)==typeof(_y),所以在此,通过故意判断他们2个的地址指针是否相 等,而显然&_x,即x的地址,是不可能等于&_y的,但是这句话(void) (&_x == &_y);使得,如果_x和_y的类型不一样,其指针类型也会不一样,2个不一样的指针类型进行比较操作,则会引起编译器产生一个编译警告,提示 你这两个值的类型不同。其中的void,表示将表达式(&_x == &_y); 所得到的结果(此处肯定是逻辑上的假,值为0)忽略掉。通过void显式丢弃一个表达式的值,否则有些编译器会就此给出警告信息提示你这行代码是无意义的,没人用到。
如此定义,那么对于一些特殊的值传入此宏之后,就会产生一些副作用,产生的结果,就不是我们想要的了,比如:
  min(++a,++b) ==> ((++a)<(++b))?(++a) : (++b)
就使得,a++和b++分别执行了2次,而且min的结果,也不对了。而用上面那个复杂的定义,多加了局部变量_x和_y,就可以避免此类问题了。

__cplusplus宏
ANSI标准说明了五个预定义的宏名。它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__
C++中还定义了 __cplusplus, 可用于判断下面是C++代码格式。
如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。
__LINE____FILE__ 宏指示,#line指令可以改变它的值,简单的讲,编译时,它们包含程序的当前行数和文件名。
__DATE__ 宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
__TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为:分:秒
__STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受不包含任何非标准扩展的标准C/C++代码。如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。

extern “C”含义
C和C++对函数的处理方式是不同的.extern “C”是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern “C”来说明。
extern “C”常以以下方式出现:
#ifdef__cplusplus
extern “C” {
#endif
…….
//一段代码
……
#ifdef__cplusplus
}
#endif
使用extern “C”,还得从cpp中对函数的重载处理开始说起。在C++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的.
明白了加入与不加入extern “C”之后对函数名称产生的影响,C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern “C”就是其中的一个策略。
如果一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,就会出现链接错误.
extern “C” {
#include “func.h”
}

extern “C”
{
extern void func();
}
为了在C++代码中调用用C写成的库文件,就需要用extern “C”来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。

0 0