extern "C"的使用

124 views
Skip to first unread message

Yili Zhao

unread,
Dec 15, 2011, 9:21:58 AM12/15/11
to pongba
大家好!
自己已经查了一些资料,知道extern
"C"主要是用于C和C++互相调用的链接指示,不过网上的材料说得并不是很清楚。我在读一些开源的C和C++的库源程序的时候,发现一些用C写的库,在头文件有这样的部分:
--------------------------------------------------------------------------------------------------
#ifdef WIN32
#ifdef PROJ_EXPORTS
#define PROJ_EXPORT __declspec(dllexport)
#elif defined(PROJ_STATIC)
#define PROJ_EXPORT
#else
#define PROJ_EXPORT __declspec(dllimport)
#endif
#else
#define PROJ_EXPORT
#endif


#ifdef __cplusplus
extern "C"
{
#endif
PROJ_EXPORT void log_verbosity(int level);
#ifdef __cplusplus
}
#endif
------------------------------------------------------------------------------------------------
我的问题是:
1. 上半部分的#ifdef WIN32应该是定义在Windows下面编译的,那么这个WIN32宏是由谁定义的?
2. 如果在Windows平台下面编译,则PROJ_EXPORT宏会被替换为__declspec(dllexport),这个应该是用于DLL导出函数的;如果在Linux平台下面编译,那么会运行“#else
#define PROJ_EXPORT #endif”部分,这样的话,函数原型声明“PROJ_EXPORT void
log_verbosity(int level);”中的PROJ_EXPORT会被替换成什么?这个宏没有值呀。
3. 为什么一些C库的头文件有如上extern "C"的部分,而一些C库又没有?按照我的理解,这个extern
"C"的部分,不是应该在所有的C库的头文件里面都加上吗?
4. 为什么一些C库的头文件有如上PROJ_EXPROT的部分,而另外一些则没有?没有的库,如果在Windows下面编译,如果编译为DLL,又没有__declspec(dllexport),是不是就没有导出函数了?
5. PROJ_STATIC是由谁定义的?
谢谢!
--
Yili Zhao

Milo Yip

unread,
Dec 15, 2011, 10:15:22 AM12/15/11
to pon...@googlegroups.com
1. Compiler (e.g. VC)
2. PROJ_EXPORT 就是被置換為甚麼都沒有
3. C/C++ Compiler 設定為 C 編譯模式時(或按後綴名決定以C編譯),是*不*可以使用 extern "C"
的部分。extern "C" 是 C++ 模式才能使用,意思是令函數使用C形式的接口。所以若只是供C使用、以C模式編譯,是不會有
extern "C"。
4. 沒有__declspec(dllexport)就不會導出
5. 通常是以編譯器命令行如 -DPROJ_STATIC 去定義,或是IDE (如Visual C++ 中的 project
settings -> preprocessor -> preprocessor defintion)中為某個project
configuration設置的。但也不排除會在之前#include進的頭文件中定義。

2011/12/15 Yili Zhao <pan...@gmail.com>:

--
Milo Yip

http://www.cnblogs.com/miloyip/
http://weibo.com/miloyip/
http://twitter.com/miloyip/

Milo Yip

unread,
Dec 15, 2011, 10:18:22 AM12/15/11
to pon...@googlegroups.com
對於1,補充一下,VC 預定義的macro應該是 _WIN32。其他預定義的macro可參考MSDN:

http://msdn.microsoft.com/en-us/library/b0084kay(v=VS.100).aspx

2011/12/15 Milo Yip <mil...@gmail.com>:

Eben

unread,
Dec 16, 2011, 2:21:24 AM12/16/11
to pon...@googlegroups.com
2. Export is not needed in Linux, so it is defined to be nothing.

2011/12/16 Yili Zhao <pan...@gmail.com>

Jeff Chen

unread,
Dec 16, 2011, 11:16:57 AM12/16/11
to pon...@googlegroups.com
搭车问一下,很多项目里会这样:
 #define DECLARE_DATA  extern

然后extern的时候用DECLARE_DATA 

这样做有什么好处么?


--
My Blog:http://jeffchen.cn

Shuo Chen

unread,
Dec 16, 2011, 8:24:33 PM12/16/11
to TopLanguage
脱了裤子放屁。

On Dec 17, 12:16 am, Jeff Chen <sheismyl...@gmail.com> wrote:
> 搭车问一下,很多项目里会这样:
> #define DECLARE_DATA extern
>
> 然后extern的时候用DECLARE_DATA
>
> 这样做有什么好处么?
>

> >> 如果在Windows平台下面编译,则PROJ_EXPORT宏会被替换为__declspec(dllexport),这个应该是用于DLL导出函数的;如果 在Linux平台下面编译,那么会运行"#else


> >> #define PROJ_EXPORT #endif"部分,这样的话,函数原型声明"PROJ_EXPORT void
> >> log_verbosity(int level);"中的PROJ_EXPORT会被替换成什么?这个宏没有值呀。
> >> 3. 为什么一些C库的头文件有如上extern "C"的部分,而一些C库又没有?按照我的理解,这个extern
> >> "C"的部分,不是应该在所有的C库的头文件里面都加上吗?
> >> 4.

> >> 为什么一些C库的头文件有如上PROJ_EXPROT的部分,而另外一些则没有?没有的库,如果在Windows下面编译,如果编译为DLL,又没有__dec lspec(dllexport),是不是就没有导出函数了?

Ryan Feng

unread,
Dec 16, 2011, 8:28:42 PM12/16/11
to pon...@googlegroups.com
这是第一次看别人的源码还是初学C

Yili Zhao

unread,
Dec 17, 2011, 7:38:53 AM12/17/11
to pon...@googlegroups.com
在Visual Studio 2010中新建一个C++的项目,从项目的属性-》C++》预处理器中可以看到,定义了两个宏:1. WIN32 2. _WINDOWS,这两个宏应该就是Visual Studio 2010编译器预先定义好的吧?
如果一个C库,用C的编译器编译,如果头文件有extern "C"的部分,那么在C++的项目中包含这个C库的头文件,由于extern "C"的存在,应该可以在C++中正常使用这个C的库;那如果这个C库的头文件没有extern "C"的部分,该怎么办?

Bearice Ren

unread,
Dec 17, 2011, 7:53:09 AM12/17/11
to pon...@googlegroups.com
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

sail

unread,
Dec 17, 2011, 9:46:01 AM12/17/11
to pon...@googlegroups.com
额,TL什么时候变成C++课堂了...问google或者老师同学呗。TL并不是C++ 的group,你想知道问题的答案还是需要一番辛苦才好,否则什么事情TL里面一问就知道了以后就懒惰了,而且发这种帖子容易被管理员警告的。

要使用C库需要包含这个C库的头文件,C库由于函数的命名等等原因如果不加extern "C"来包含头文件的话C++是不能用的。这个extern声明是告诉C++编译器,这个extern"C"里面的包含的头文件的函数声明要用C的命名规 范来查找相应的函数实现,这样子C++编译器就按照C的函数命名规范就在这个C库里面找到了这些头文件里面声明的函数的实现。

所以你的问题很令人费解,C库的头文件没有extern "C",你不可以自己加extern"C"来包含这个C库的头文件么?我觉得还是在问问题前自己思考一下好一点。

Yili Zhao

unread,
Dec 17, 2011, 9:15:41 PM12/17/11
to pon...@googlegroups.com
谢谢建议,以后会注意。
我就是觉得C的库应该在头文件里面加上extern "C"的部分,应该是一个规范。

sail

unread,
Dec 17, 2011, 10:17:40 PM12/17/11
to pon...@googlegroups.com
于 2011/12/18 10:15, Yili Zhao 写道:
> 谢谢建议,以后会注意。
> 我就是觉得C的库应该在头文件里面加上extern "C"的部分,应该是一个规范。
额,就一个回答,C为什么要迁就C++?

Shuo Chen

unread,
Dec 17, 2011, 11:35:53 PM12/17/11
to TopLanguage
因为 stdio.h stdlib.h math.h 等等 C 语言的标准库头文件都是这么干的。
Posix 的头文件也是这么干的,包括 pthread、sockets 等等。
通常的 C 语言库也都是这么干的,包括 zlib、libevent、sqlite、pcre、readline、openssl、libxml2
等等。
用 C 语言实现的脚步语言的头文件也是这么干的,包括 python、ruby 等等。(Lua 是个例外,因为它本身也是合法的 C++ 程
序。)

Simon

unread,
Dec 19, 2011, 10:13:58 PM12/19/11
to TopLanguage
你太偏激了,这种用法的好处就是你随时可以

#define DECLARE_DATA

很方便地取消这些符号的extern属性,而不用到处修改代码

Jeff Chen

unread,
Dec 27, 2011, 9:13:59 AM12/27/11
to pon...@googlegroups.com
Thanks
You r right, I just got this answer from others

Shuo Chen

unread,
Dec 27, 2011, 11:25:34 AM12/27/11
to TopLanguage
是非题:"符号的"extern 属性"是由 extern 关键字决定的吗?" 全局的没有 extern 修饰的 int x; 是否能被别的编译
单元看见?
思考题:"去掉了 extern 关键字,多半会出现编译错,为什么?"

Jeff Chen

unread,
Dec 28, 2011, 8:53:16 AM12/28/11
to pon...@googlegroups.com
一个项目,拆分成不同的子项目,由不同的team,不同时间开发,各自测试.

Baiyan Huang

unread,
Jan 2, 2012, 3:50:29 AM1/2/12
to pon...@googlegroups.com
我也没看出来#define DECLARE_DATA extern这种用法的意义在哪里,但是在sqlite的source code中看到:

/*
** Add the ability to override 'extern'
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
#endif

我什么时候需要override extern呢?

-Baiyan


2011/12/28 Shuo Chen <gian...@gmail.com>

Jeff Chen

unread,
Jan 2, 2012, 9:02:41 AM1/2/12
to pon...@googlegroups.com
我上面已经回复了,在哪种情况下有意义



2012/1/2 Baiyan Huang <baiya...@gmail.com>

Baiyan Huang

unread,
Jan 2, 2012, 8:44:11 PM1/2/12
to pon...@googlegroups.com
sorry,之间没看见:
>>一个项目,拆分成不同的子项目,由不同的team,不同时间开发,各自测试.
不怎么理解这么做的好处,如果说这是一个项目,那么这些子项目之间应该会有交互吧,如何各自开发,各自测试? 即使设法这么做了,他们之间的交互、数据共享也应该通过接口,而不是这么tricky的方式吧?

在sqlite-dev邮件组倒是得到一个回复,主要是为了兼容一些非主流的系统 - 这些系统上的linkage可能有些不一样,有些道理:
Try using some more exotic systems where things aren't as simple as that.
 Various less mainstream systems have very different ways of inter-module
linkage and compilation.

但不太清楚那些系统需要这么定义,定义成什么~

-Baiyan


2012/1/2 Jeff Chen <sheis...@gmail.com>

Jeff Chen

unread,
Jan 3, 2012, 2:30:59 AM1/3/12
to pon...@googlegroups.com
different ways of inter-module
linkage and compilation.
===================
不知道这个指的是什么


只是我们项目是从协议上分成三部分,由不同的team开发,各自测试.
有些数据并不通过接口共享。
比如队列的名字, 信号量,socket 等等,还有一些是具体项目相关的变量

2012/1/3 Baiyan Huang <baiya...@gmail.com>

Baiyan Huang

unread,
Jan 3, 2012, 6:38:10 AM1/3/12
to pon...@googlegroups.com
different ways of inter-module
linkage and compilation.
===================
不知道这个指的是什么

我的理解是编译器对标准的支持程度吧,有些平台上的编译器可能与主流平台有所不同,当你需要在不同的编译单元使用同一个变量时,它不是用extern,而是需要一些其他的关键词  ---- 正如我之前说的,我不清楚具体是那个平台,怎么用,但鉴于sqlite的多平台支持性来讲,这种说法还是比较可信的。


只是我们项目是从协议上分成三部分,由不同的team开发,各自测试.
有些数据并不通过接口共享。
比如队列的名字, 信号量,socket 等等,还有一些是具体项目相关的变量
这样做对你们实际开发有带来什么问题吗?如果有问题,有想过用其他方式代替吗?


-Baiyan


2012/1/3 Jeff Chen <sheis...@gmail.com>

chen yang

unread,
Jan 5, 2012, 2:53:16 AM1/5/12
to pon...@googlegroups.com
extern "C" 是为了让 C 语言的程序调用c++写的函数用的。

由于在C++中支持函数重载。例如:

void a( int ){}
void a(){}

在编译过程中,a这个符号就会对应两个函数。link程序则要求每个符号只对应一个函数。为了解决这个问题,c++就使用了name mangle的技术。就是使用函数名+返回类型+参数类型来生成符号名。如果是类的成员函数还要加上类名。

但是c语言的编译器可不知道什么name mangle,它看到a()就直接找一个符号为a的函数。这样一来就找不到啦。

所以 c++就用exten "C" 来告诉编译器说我这个函数要被c调用,你就不要要做name mangle了。当然后果就是这个函数名就不可以重载了。
extern "C" void a( int ){}
extern "C" void a(){}
就会被报错。


2012/1/3 Baiyan Huang <baiya...@gmail.com>
Reply all
Reply to author
Forward
0 new messages