C ++中的标头中的C

Posted

技术标签:

【中文标题】C ++中的标头中的C【英文标题】:C out of header in C++ 【发布时间】:2013-06-22 19:57:19 【问题描述】:

如果我在 c++ 环境中使用 C 代码并将所有代码包含在标头中,则一切正常。如果我尝试在标头中声明 C 函数并在 .c 或 .cpp 文件中实现它们,则会收到以下错误:

Undefined symbols for architecture x86_64:
  "vec2_norm(Vec2)", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Vec2.h

#ifndef Physics_Engine_Test_Vec2_h
#define Physics_Engine_Test_Vec2_h

typedef struct

    float x;
    float y;
 Vec2;

inline Vec2 vec2_norm(Vec2 v);

#endif

Vec2.c 或 .cpp

#include "Vec2.h"
#include <math.h>

inline Vec2 vec2_norm(Vec2 v) 
    float len = v.x*v.x + v.y*v.y;
    if (len) 
        len = 1 / sqrtf(len);
        v.x *= len;
        v.y *= len;
    
    return v;

【问题讨论】:

Name mangling... Linker error calling C-Function from Objective-C++的可能重复 我刚刚测试过,它在我使用 g++ 以及当我使用 clang 时编译得很好 如果删除“内联”子句,错误是否仍然存在? 【参考方案1】:

虽然我测试了您的代码示例,并且它使用 g++/gcc 和 clang++/clang 对我来说编译得很好,但是当您想要编译基于 C 的源代码时,在它周围添加 extern "C" 总是一个好主意,这样编译器不对这些函数进行 C++ 名称修改:

#ifdef __cplusplus
extern "C" 
#endif

typedef struct

    float x;
    float y;
 Vec2;

inline Vec2 vec2_norm(Vec2 v);

#ifdef __cplusplus
;
#endif

extern "C" 

inline Vec2 vec2_norm(Vec2 v) 
    float len = v.x*v.x + v.y*v.y;
    if (len) 
        len = 1 / sqrtf(len);
        v.x *= len;
        v.y *= len;
    
    return v;


;

顺便说一句,关于您在代码中使用的inline,即使强制 仅在标头中定义内联函数,强烈建议这样做,所以由于一个定义规则,您不必将内联正文复制到将包含该标题的每个翻译单元。

正如***在topic 上所说:

有些东西,比如类型、模板和外部内联函数,可以在多个翻译单元中定义。对于给定的实体,每个定义必须相同。不同翻译单元中的非外部对象和函数是不同的实体,即使它们的名称和类型相同。

但最终,好主意还是坏主意取决于您的设计选择。

HTH

【讨论】:

实现在.cpp 文件中,对吗?所以不需要在那里测试__cplusplus。此外,正确的测试是#if __cplusplus。一些 C/C++ 编译器在 C 语言模式下运行时会#define __cplusplus 0 虽然我认为 C++ 文件保留#if define __cplusplus 并没有什么坏处,你说得对,但我没有想到可能仍然是真的第二种情况(虽然我不知道哪些编译器会这样做)。 有趣的事情:刚刚得到一个编辑添加#ifdef __cplusplus :-) 标头应使用#if __cplusplus。实现文件只需要无条件extern "C" “所以编译器不会对这些函数进行名称修改” - 相反,编译器会对这些函数使用 C 修改。【参考方案2】:

这里实际上有两个问题:

    如果你声明了一个内联函数,除非在给定的编译单元中需要,否则它不会被编译。所以 Vec2.o 文件将不包含它,并且链接器将无法找到它。内联函数总是必须放在头文件中,因此编译器在每个需要它的编译单元中都可以看到它们的实现。

    正如 H2CO3 所说,C++ 使用名称修饰:它将参数类型编码为函数名称以允许重载。 C 不这样做。所以,如果你编译一个使用你的函数的 c++ 文件,它会想要链接一些奇怪的名字,而不仅仅是“vec2_norm”。为了能够将 C 代码链接到 C++ 代码,您必须告诉编译器它应该使用 C 符号名称。

大多数情况下,人们通过编写这种形式的标题来做到这一点:

#ifdef __cplusplus
    extern "C" 
#endif

Vec2 vec2_norm(Vec2 v);

#ifdef __cplusplus
    
#endif

顺便问一下,有人知道如何正确格式化***中的预处理器指令吗?

【讨论】:

关于你的问题,这不是#的错,而是因为当你在列表之后执行代码块时它会出错。只需在代码块前添加一些文本,就不会乱了。参考我的编辑。

以上是关于C ++中的标头中的C的主要内容,如果未能解决你的问题,请参考以下文章

C ++标头顺序[关闭]

c_cpp 根据标头中的信息计算磁盘上ELF文件的大小

如何在Visual C ++中禁止外部标头中的警告

C ++编译器多次查找标头中定义的值[重复]

WinCE C/C++ 运行时库下如何检测泄漏?

C中的traceroute,响应中的ICMP id错误