extern“C” - 在库头包含之前还是之后?

ein*_*ica 5 c c++ cross-language include extern-c

我正在编写一个 C 库,它可能对编写 C++ 的人有用。它有一个标题,如下所示:

#ifndef FOO_H_
#define FOO_H_

#include <bar.h>

#include <stdarg.h>
#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

void foo_func();

#ifdef __cplusplus
}
#endif
#endif 
Run Code Online (Sandbox Code Playgroud)

我想知道 - 我应该在extern "C"包含标头包含指令之前移动该位吗?尤其是在实践中,其中一些标头本身可能有一个extern "C"

wov*_*ano 2

不,一般来说,您不应该移动它以包含标题。

extern "C"用于指示函数正在使用 C 调用约定。声明对变量和 s 没有影响#define,因此不需要包含这些。如果 an#include位于extern "C"块内,则这会有效地修改该头文件内的函数声明!

背景:如果没有extern "C"声明,则在使用 C 编译器编译时,假定函数遵循 C 约定,而在使用 C++ 编译器编译时,假定函数遵循 C++ 约定。如果 C 和 C++ 代码使用相同的头文件,则会出现链接器错误,因为编译后的函数在 C 和 C++ 中具有不同的名称。

虽然可以将所有代码放在 #ifdef 块之间,但我个人不喜欢它,因为它实际上仅用于函数原型,而且我经常看到人们将其复制粘贴到不应该的地方。最简洁的方法是将其保留在应有的位置,即 C/C++ 头文件中的函数原型周围。

因此,为了回答您的问题“我应该extern "C"在包含标头包含指令之前移动该位吗?”,我的答案是:不,您不应该。

但这可能吗?是的,在很多情况下这不会破坏任何东西。有时,如果外部头文件中的函数原型不正确(例如,当它们是 C 函数并且您想从 C++ 调用它们时)并且您无法更改该库,则甚至有必要。

然而,在某些情况下,这样做会破坏构建。这是一个简单的示例,如果使用以下内容包装包含,则无法编译extern "C"

foo.h:

#pragma once

// UNCOMMENTING THIS BREAKS THE BUILD!
//#ifdef __cplusplus
//extern "C" {
//#endif

#include "bar.h"

bar_status_t foo(void);

//#ifdef __cplusplus
//}
//#endif
Run Code Online (Sandbox Code Playgroud)

foo.c:

#include <stdio.h>
#include "foo.h"
#include "bar.h"

bar_status_t foo(void)
{
    printf("In foo. Calling bar wrapper.\n");
    return bar_wrapper();
}
Run Code Online (Sandbox Code Playgroud)

酒吧.h:

#pragma once

typedef enum {
    BAR_OK,
    BAR_GENERIC_ERROR,
    BAR_OUT_OF_BEAR,
    // ...
} bar_status_t;

extern "C" bar_status_t bar_wrapper(void);
bar_status_t bar(void);
Run Code Online (Sandbox Code Playgroud)

酒吧.cpp:

#include <iostream>
#include "bar.h"

extern "C" bar_status_t bar_wrapper(void)
{
    std::cout << "In C/C++ wrapper." << std::endl;
    return bar();
}

bar_status_t bar(void)
{
    std::cout << "In bar. One bear please." << std::endl;
    return BAR_OK;
}
Run Code Online (Sandbox Code Playgroud)

主要.cpp:

#include <stdio.h>
#include <stdlib.h>
#include "foo.h"
#include "bar.h"

int main(void)
{
    bar_status_t status1 = foo();
    bar_status_t status2 = bar();
    return (status1 != BAR_OK) || ((status2 != BAR_OK));    
}
Run Code Online (Sandbox Code Playgroud)

取消注释ah中的块时,出现以下错误:

main2.cpp:(.text+0x18): undefined reference to `bar'
collect2.exe: error: ld returned 1 exit status
Makefile:7: recipe for target 'app2' failed
Run Code Online (Sandbox Code Playgroud)

没有的话,它构建得很好。无论哪种方式,仅从 foo 和 bar 调用 C 函数的 AC main 都可以很好地构建,因为它不受块的影响#ifdef __cplusplus