26

在 C++ 项目中,由于 C 和 C++ 的标准不同,包含 C 源文件的 .h 文件会导致很多错误。
如何在 C++ 项目(或 main.cpp)中使用 C 源文件?

4

4 回答 4

40

为了获得最大的可靠性:

  • 使用 C 编译器编译 C 源代码。
  • 使用 C++ 编译器编译 C++ 源代码
  • 最好用 C++ 编写 main() 函数。
  • 将程序与 C++ 编译器链接。

确保 C 头文件本身知道 C++,或者 C++ 代码在extern "C" { ... }块内包含 C 头文件。

任一(C 头文件cheader.h):

#ifndef CHEADER_H_INCLUDED
#define CHEADER_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

...main contents of header...

#ifdef __cplusplus
}
#endif

#endif /* CHEADER_H_INCLUDED */ 

或(C++ 源代码):

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

现代 C 风格非常接近 C 和 C++ 语言的公共子集。但是,由于很多原因,任意 C 代码都不是 C++ 代码,并且仅调用 C 源文件 C++ 源文件(通过更改扩展名,或仅通过使用 C++ 编译器编译)并不能保证成功. 通常,将 C 编译为 C 并将 C++ 编译为 C++,然后将生成的目标文件与 C++ 编译器链接起来(以确保调用正确的支持库)更容易。

但是,如果 MSVC 编译器说使用 MFC 的程序必须仅用 C++ 编写(MFC 需要 C++ 编译(使用 .cpp 后缀)是报告的错误),那么您可能别无选择,只能确保您的 C 代码可编译为 C++ 代码。这意味着您必须从malloc()et al 转换返回值;您必须担心其他不使用强制转换将 a 转换void *为其他指针类型的地方;sizeof('a') == 4在 C 和sizeof('a') == 1C++中你必须担心;您必须确保在使用每个函数之前都已声明它;您必须确保您的 C 代码不使用任何 C++ 关键字(typename特别class是 ;inline有时也 - 但完整列表非常大)。

在某些圈子中,您不得不担心使用 C99 中不存在于 C++2003 或 C++2011 中的特性,例如灵活的数组成员、指定的初始值设定项、复合文字、可变长度数组等在。但是,如果 C 代码用于 MSVC,那么这可能不会成为问题。MSVC C 编译器不支持这些功能(它只支持 C89,不支持 C99)。

FWIW:我有一个脚本可以搜索 C++ 关键字。它包含以下评论:

# http://en.cppreference.com/w/cpp/keywords
# plus JL annotations
# and                               C (<iso646.h>)
# and_eq                            C (<iso646.h>)
# alignas (C++11 feature)
# alignof (C++11 feature)
# asm                               C (core)
# auto(1)                           C (core)
# bitand                            C (<iso646.h>)
# bitor                             C (<iso646.h>)
# bool                              C99 (<stdbool.h>)
# break                             C (core)
# case                              C (core)
# catch
# char                              C (core)
# char16_t (C++11 feature)
# char32_t (C++11 feature)
# class
# compl                             C (<iso646.h>)
# const                             C (core)
# constexpr (C++11 feature)
# const_cast
# continue                          C (core)
# decltype (C++11 feature)
# default(1)                        C (core)
# delete(1)
# double                            C (core)
# dynamic_cast
# else                              C (core)
# enum                              C (core)
# explicit
# export
# extern                            C (core)
# false                             C99 (<stdbool.h>)
# float                             C (core)
# for                               C (core)
# friend
# goto                              C (core)
# if                                C (core)
# inline                            C (core)
# int                               C (core)
# long                              C (core)
# mutable
# namespace
# new
# noexcept (C++11 feature)
# not                               C (<iso646.h>)
# not_eq                            C (<iso646.h>)
# nullptr (C++11 feature)
# operator
# or                                C (<iso646.h>)
# or_eq                             C (<iso646.h>)
# private
# protected
# public
# register                          C (core)
# reinterpret_cast
# return                            C (core)
# short                             C (core)
# signed                            C (core)
# sizeof                            C (core)
# static                            C (core)
# static_assert (C++11 feature)
# static_cast
# struct                            C (core)
# switch                            C (core)
# template
# this
# thread_local (C++11 feature)
# throw
# true                              C99 (<stdbool.h>)
# try
# typedef                           C (core)
# typeid
# typename
# union                             C (core)
# unsigned                          C (core)
# using(1)
# virtual
# void                              C (core)
# volatile                          C (core)
# wchar_t                           C (core)
# while                             C (core)
# xor                               C (<iso646.h>)
# xor_eq                            C (<iso646.h>)

(1)后缀是 CPP 参考的脚注:

  • (1)— 在 C++11 中的含义发生了变化
于 2012-12-04T01:24:57.333 回答
9

来自 C++ 示例的最小可运行 C

从 C++ 调用 C 非常简单:每个 C 函数只有一个可能的未损坏符号,因此不需要额外的工作。

主文件

#include <cassert>

#include "c.h"

int main() {
    assert(f() == 1);
}

ch

#ifndef C_H
#define C_H

/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif

#endif

抄送

#include "c.h"

int f() { return 1; }

跑:

g++ -c -o main.o -std=c++98 main.cpp
gcc -c -o c.o -std=c89 c.c
g++ -o main.out main.o c.o
./main.out

我在以下位置extern "C"进行了更详细的解释:extern "C" 在 C++ 中的作用是什么?

GitHub 上的示例

C 示例中的最小可运行 C++

从中调用 C++ 有点困难:我们必须手动创建要公开的每个函数的非损坏版本。

在这里,我们说明了如何将 C++ 函数重载暴露给 C。

主程序

#include <assert.h>

#include "cpp.h"

int main(void) {
    assert(f_int(1) == 2);
    assert(f_float(1.0) == 3);
    return 0;
}

cpp.h

#ifndef CPP_H
#define CPP_H

#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif

#endif

cpp.cpp

#include "cpp.h"

int f(int i) {
    return i + 1;
}

int f(float i) {
    return i + 2;
}

int f_int(int i) {
    return f(i);
}

int f_float(float i) {
    return f(i);
}

跑:

gcc -c -o main.o -std=c89 -Wextra main.c
g++ -c -o cpp.o -std=c++98 cpp.cpp
g++ -o main.out main.o cpp.o
./main.out

GitHub 上的示例

于 2018-08-18T21:26:18.583 回答
2

C++ 宣扬对 C 源代码的“向后兼容性”,因此可以选择将 C 源代码复制到 .cpp 文件并构建。现在 C++并不完全向后兼容,因此您可能需要更改 C 源代码中的一些内容,但通常它应该以最少的错误构建。只需确保包含 .c 使用的 C 库(考虑到您的编译器也支持 C)

#include <stdio.h>
#include <string.h>
//so on
于 2012-12-04T01:16:14.207 回答
0

如果您只是使用源代码而不是一些预编译库,在大多数情况下,您只需将 .c 文件重命名为 .cpp 文件

于 2012-12-04T01:16:14.137 回答