如何在C++项目(或main.cpp中)使用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++,然后使用C++编译器链接生成的目标文件(以确保正确的支持库被调用)更容易。
然而,如果MSVC编译器说使用MFC的程序必须完全用C++编写(“MFC需要C++编译(使用.cpp后缀)”是报告的错误),那么您可能别无选择,只能确保您的C代码可以编译为C++代码。这意味着您必须对malloc()
等函数的返回值进行强制类型转换,必须关注其他地方是否未使用强制类型转换将void *
转换为其他指针类型,必须关注C中sizeof('a') == 4
而在C++中sizeof('a') == 1
,必须确保每个函数在使用之前都被声明,必须确保您的C代码不使用任何C++关键字(尤其是typename
和class
,有时还有inline
- 但是完整的列表相当大)。
在某些圈子中,您可能需要担心C99中的一些特性,在C++2003或C++2011中不存在,例如灵活数组成员、指定初始化器、复合文字、可变长度数组等等。然而,如果C代码是为MSVC编写的,则可能不会出现问题;这些特性不受MSVC C编译器的支持(它仅支持C89,而不支持C99)。
顺便说一句:我有一个脚本来查找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 Reference 上的一个脚注:
(1)
— 意义在 C++11 中发生了改变从C++示例中提取最小可运行的C代码
从C++调用C函数非常简单:每个C函数只有一个可能的非重载符号,因此不需要额外的工作。
main.cpp
#include <cassert>
#include "c.h"
int main() {
assert(f() == 1);
}
c.h
#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
c.c
#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"
。
从C示例中最小的可运行C++代码
从C++调用有点困难:我们必须手动创建每个要公开的函数的非重载版本。
这里我们演示如何将C++函数重载公开给C。
main.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
C++强调对C源代码的“向后兼容性”,因此可以将C源代码复制到.cpp文件中进行构建。 现在,C++并非完全向后兼容,因此您可能需要在C源代码中进行一些更改,但通常情况下,仅会出现最少量的错误。只需确保包含.c使用的C库(考虑到您的编译器也支持C语言)即可。
#include <stdio.h>
#include <string.h>
//so on
如果你只是使用源代码而不是一些预编译库,在大多数情况下,你可以将 .c 文件重命名为 .cpp 文件。
malloc()
的结果强制转换是有很好的理由的,而C++则强制转换。C具有真正的多维动态数组,而C++没有。好的C代码还有一些C++不允许的东西,这只是我脑海中的两个例子。 - cmaster - reinstate monica
void*
隐式转换为任何其他指针类型。 - Ed S.extern "C"
包含头文件。 - Sergey Kalinichenkoint *i = malloc(sizeof(int) * 10);
在 C++ 中也是非法的。任何使用class
作为变量名或使用 c99 动态数组等代码都是非法的。 - dmckee --- ex-moderator kitten