代码之家  ›  专栏  ›  技术社区  ›  foobarfuzzbizz

外部“C”声明如何工作?

  •  24
  • foobarfuzzbizz  · 技术社区  · 15 年前

    我在上编程语言课程,我们在讨论 extern "C" 宣言。

    除了“IT接口C和C++”之外,这个声明如何工作在更深层次?这如何影响程序中发生的绑定?

    9 回复  |  直到 9 年前
        1
  •  46
  •   Bertrand Marron    15 年前

    extern "C" 用于确保以下符号不 mangled (装饰)。


    例子:

    假设我们在一个名为 test.cpp :

    extern "C" {
      int foo() {
        return 1;
      }
    }
    
    int bar() {
      return 1;
    }
    

    如果你跑步 gcc -c test.cpp -o test.o

    看看符号名称:

    000000 10吨Z3barv

    亿吨foo

    foo() 保留其名称。

        2
  •  25
  •   Cthutu    15 年前

    让我们来看一个在C和C++中都可以编译的典型函数:

    int Add (int a, int b)
    {
        return a+b;
    }
    

    现在在C语言中,函数在内部被称为“添加”。而C++函数在内部使用一个名为“名字修改”的系统,完全不同。它基本上是一种命名函数的方法,使具有不同参数的同一个函数具有不同的内部名称。

    因此,如果在Addio.c中定义了Ad(),并且在Addio.h中有了原型,如果尝试在C++文件中添加Addio.h,就会遇到问题。因为C++代码正在寻找一个名称与Addio.c中的名称不同的函数,您将得到链接错误。要解决该问题,必须使用以下方法包括add.c:

    extern "C"
    {
    #include "add.h"
    }
    

    现在C++代码将链接到y+Ad,而不是C++名称Mulink版本。

    这是表达式的用法之一。底线,如果你需要编译一个C++程序中严格C的代码(通过一个包含语句或其他方法),你需要用一个外部的“C”{…}声明。

        3
  •  9
  •   Reed Copsey    15 年前

    当您将代码块标记为extern“c”时,您将告诉系统使用c样式的链接。

    这主要影响链接器管理名称的方式。不是使用C++样式名称Mangle(它更复杂地支持运算符重载),而是从链接器中获得标准的C样式命名。

        4
  •  5
  •   gilbertc    15 年前

    在C++中,函数的名称/符号实际上被重命名为其他东西,使得不同的类/命名空间可以具有相同签名的功能。在C语言中,函数都是全局定义的,不需要这样的自定义重命名过程。

    为了使C++和C相互交谈,“ExtEngC:”指示编译器不使用C约定。

        5
  •  5
  •   Johannes Schaub - litb    15 年前

    应该注意的是 extern "C" 还可以修改函数的类型。它不仅在较低的层次上修改内容:

    extern "C" typedef void (*function_ptr_t)();
    
    void foo();
    
    int main() { function_ptr_t fptr = &foo; } // error!
    

    的类型 &foo 不等于typedef指定的类型(尽管代码被某些编译器接受,但并非所有编译器都接受)。

        6
  •  4
  •   quamrana Ryuzaki L    15 年前

    外部C会影响C++编译器的名称修改。它是一种让C++编译器不损坏名字的方法,或者是用C编译器的方式来对它们进行篡改。这是它连接C和C++的方式。

    例如:

    extern "C" void foo(int i);
    

    将允许该函数在C模块中实现,但允许它从C++模块调用。

    当试图让C模块调用C++模块中的C++函数(显然C不能使用C++类)时,问题就出现了。C编译器不喜欢 extern "C" .

    所以你需要使用这个:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void foo(int i);
    
    #ifdef __cplusplus
    }
    #endif
    

    现在,当出现在头文件中时,C和C++编译器都会对声明感到满意,并且现在可以在C或C++模块中定义,并且可以由C和C++代码调用。

        7
  •  3
  •   Harvey    10 年前

    extern“c”表示所附代码使用C样式链接和名称管理。C++使用了更复杂的命名格式。下面是一个例子:

    http://en.wikipedia.org/wiki/Name_mangling

    int example(int alpha, char beta);
    

    在C: _example

    在C++中: __Z7exampleic

    更新:正如gmannick在注释中指出的,名称管理模式依赖于编译器。

        8
  •  0
  •   raman    10 年前

    ExtEnter“C”是用C绑定声明函数的关键字,因为C编译器和C++编译器将在对象文件中将源转换成不同的形式:

    例如,代码段如下:

    int _cdecl func1(void) {return 0}
    int _stdcall func2(int) {return 0}
    int _fastcall func3(void) {return 1}
    

    32位C编译器将按以下格式转换代码:

    _func1
    _func2@4
    @func3@4
    

    在cdecl中,func1将翻译为' 名字

    在stdcall中,func2将转换为' _姓名@X

    在fastcall中,func2将翻译为' @姓名@X

    X '表示参数列表中参数的字节数。

    在Windows上64位约定没有前导下划线

    在C++中,引入了类、模板、命名空间和运算符重载,因为不允许有两个同名函数,C++编译器在符号名中提供类型信息,

    例如,代码段如下:

    int func(void) {return 1;}
    int func(int) {return 0;}
    int func_call(void) {int m=func(), n=func(0);}
    

    C++编译器将翻译代码如下:

    int func_v(void) {return 1;}
    int func_i(int) {return 0;}
    int func_call(void) {int m=_func_v(), n=_func_i(0);}
    

    “_v”和“_i”是“void”和“int”的类型信息

        9
  •  -2
  •   Joe Pitz    15 年前

    这是一份来自msdn的报价单

    “extern关键字声明一个变量或函数并指定它具有外部链接(它的名称在定义它的文件以外的文件中可见)。修改变量时,extern指定变量具有静态持续时间(在程序开始时分配,在程序结束时取消分配)。变量或函数可以在另一个源文件中定义,也可以稍后在同一个文件中定义。默认情况下,文件范围内变量和函数的声明是外部的。”

    http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx

    推荐文章