从C传递bool到C++并返回

51

当设计数据结构以通过连接C和C++代码的C API传递时,是否可以安全地使用bool?也就是说,如果我有一个像这样的struct

struct foo {
  int bar;
  bool baz;
};

在C(其中它是一个_Bool)和C++中,baz的大小、含义以及在foo内的位置是否以相同的方式解释是有保证的吗?

我们正在考虑在单个平台上(在Beaglebone上的Debian 8上使用GCC),通过相同的GCC版本编译C99和C++11代码。欢迎提出一般性意见


12
CиҜӯиЁҖдёӯзҡ„boolдёҺC++иҜӯиЁҖдёӯзҡ„boolжҳҜдёҚеҗҢзҡ„гҖӮCиҜӯиЁҖдёӯзҡ„boolе®һйҷ…дёҠжҳҜC99еј•е…Ҙзҡ„зұ»еһӢ_Boolзҡ„typedefеҲ«еҗҚгҖӮ - martinkunev
8
sizeof(bool)sizeof(_Bool)(C99布尔类型)的大小取决于实现。如果在您的平台上使用一组编译器时它们具有相同的大小,只要您坚持使用这些编译器,就可以认为它是安全的。但是,关于结构布局的填充方式,即使布尔类型的大小相同,可能也会存在其他问题。 - Jabberwocky
2
@sbi 我找到了这个链接:https://dev59.com/inA65IYBdhLWcg3w-z5A,它有用吗? - Pavel
3
在Visual C++ 5.0版本中,将bool的大小从32位改为8位。因此理论上应确保不要混合使用来自不同编译器版本的目标代码。但另一方面,现代编译器几乎不会使用大于8位的bool类型。 - nwellnhof
1
在我看来,“平台”也意味着编译器/设置。因此,“单一平台”和两个不同的编译器/设置(一个用于C,另一个用于C ++)是不正确的。这是两个可能非常相似或非常不同的平台。我会选择使用 int32_tuint8_t - chux - Reinstate Monica
显示剩余11条评论
4个回答

50

C语言和C++语言中的bool类型是不同的,但只要你坚持使用相同的编译器(在你的情况下,gcc),这应该是安全的,因为这是一个合理普遍的情况。

在C++中,bool一直是一个关键字。C语言直到C99才引入了关键字_Bool(因为人们在C89代码中将bool typedef或#define为intchar,所以直接添加bool作为关键字会破坏现有代码);在C语言中,有头文件stdbool.h,其中应该有从_Boolbool的typedef或#define。看一下你的;GCC的实现看起来像这样:

/*
 * ISO C Standard:  7.16  Boolean type and values  <stdbool.h>
 */

#ifndef _STDBOOL_H
#define _STDBOOL_H

#ifndef __cplusplus

#define bool        _Bool
#define true        1
#define false        0

#else /* __cplusplus */

/* Supporting <stdbool.h> in C++ is a GCC extension.  */
#define _Bool        bool
#define bool        bool
#define false        false
#define true        true

#endif /* __cplusplus */

/* Signal that all the definitions are present.  */
#define __bool_true_false_are_defined        1

#endif        /* stdbool.h */

这让我们相信,至少在GCC中,这两种类型是兼容的(在大小和对齐方面),因此结构布局将保持不变。

值得注意的是,Itanium ABI被GCC使用以及其他大多数编译器(除了Visual Studio;如下面评论中的Matthieu M.所述)在许多平台上指定_Boolbool遵循相同的规则。这是一个强有力的保证。第三个提示来自Objective-C参考手册,它说对于Objective-C和Objective-C++,它们分别遵循C和C ++的惯例,bool_Bool是等价的; 因此,虽然标准没有保证,但您可以假设它们是等效的。

编辑:

如果标准不能保证_Boolbool兼容(大小、对齐和填充),那么怎么办呢?
当我们说这些东西是“架构相关”的时候,实际上我们是指它们是“ABI相关”的。每个编译器都实现一个或多个ABI,如果两个编译器(或同一编译器的版本)实现了相同的ABI,则称它们是兼容的。由于从C++调用C代码是普遍存在的,因此所有我听说过的C++ ABI都扩展了本地C ABI。

由于OP询问了有关Beaglebone的问题,我们必须检查ARM ABI,特别是Debian使用的GNU ARM EABI。正如Justin Time在评论中所指出的那样,ARM ABI确实声明C++的ABI扩展了C的ABI,并且_Boolbool是兼容的,两者大小均为1,对齐方式为1,表示机器的无符号字节。因此,在Beaglebone上,是的,_Boolbool是兼容的


26
如果我找到一款同时支持C和C++但对bool_Bool实现不同的编译器,我会追踪其程序员,找到他们并让他们坐下来,解释为什么这是一个巨大的错误。 - paulotorrens
8
只有在将C代码和C++代码都用g++编译成C++后,gcc的实现才提供兼容性。它不支持C代码实际上以C编译和C++代码之间的兼容性。 - Peter
6
我会强调Itanium ABI:这是除了Visual Studio之外所有主要C ++参与者实现的ABI,这意味着兼容性不仅在单个GCC版本中得到保证,而且实际上可以跨编译器和版本进行保证,同时还包括未来的版本。很难找到更好的保证。但是......值得链接到ABI具体部分提到这一点;我发现https://mentorembedded.github.io/cxx-abi/abi.html#pod是您想到的吗? - Matthieu M.
5
为什么他们在#else部分使用#define bool bool#define false false#define true true? - Heshy
13
@Heshy,我认为这是因为一些旧程序会检查 #ifndef bool /**/ typedef int bool; /**/ #endif 或类似的内容。实际上,我已经在野外看到过这种情况。如果 bool 没有被定义为 bool,这样的代码就会失败。 - paulotorrens
显示剩余12条评论

16

语言标准对此没有任何说明(如果我错了,请指出,我找不到任何信息),因此,如果我们仅限于语言标准,这是不安全的。但是,如果你挑剔地支持哪些架构,你可以查找它们的ABI文档以查看是否安全。

例如,amd64 ABI 文档 中有一个脚注,针对 _Bool 类型,其中写道:

在 C++ 中,该类型称为 bool。

我不能用其他方式解释这个问题,除了它将是兼容的。

另外,只是思考一下这个问题。当然会起作用。编译器生成的代码既遵循 ABI,也遵循平台上最大的编译器的行为(如果该行为在 ABI 之外)。C++ 的一个重要特点是它可以链接到用 C 编写的库,而库的一个特点是它们可以由同一平台上的任何编译器编译(这就是为什么我们首先需要 ABI 文档的原因)。可能会发生一些轻微的不兼容性吗?当然可能会,但这是你最好通过向编译器制造商报告错误来解决,而不是在你的代码中进行解决。我怀疑 bool 会是编译器制造商会搞砸的东西。


6

C标准仅对_Bool这个类型做了如下规定:

声明为 _Bool 类型的对象足够大,可以存储值 0 和 1。

这意味着 _Bool 至少要等于 sizeof(char) 或者更大(保证可以存储true/false)。

具体大小取决于实现,正如Michael在评论中所说。最好在相关编译器上执行一些大小测试,如果匹配并且您继续使用同一个编译器,那么就可以认为是安全的。


2
但是 sizeof(char) 被保证为1。每种类型都等于或大于它。 - paulotorrens
1
@paulotorrens 这正是我所说的。 - Hatted Rooster

6
正如Gill Bates所说,C语言中的sizeof(bool)依赖于编译器。无法保证相同的编译器在处理C和C++时会以相同方式处理布尔类型,也不能保证在不同体系结构上,它们的处理方式一致。如果愿意,编译器甚至可以根据C标准将其表示为位域中的单个位。
在使用具有32位ARM核心和32位DSP核心以及可由两者访问的共享内存的TI OMAP-L138处理器上,我亲身经历过这种情况。ARM核心将bool表示为一个int(32位),而DSP将bool表示为char(8位)。为了解决这个问题,我定义了自己的类型bool32_t,用于共享内存接口,知道32位值适用于两个方面。当然,我也可以将其定义为8位值,但是考虑到本机整数大小,我认为这样做可能影响性能。
如果你像我一样这样做,你可以100%保证你的C和C++代码具有二进制兼容性。否则就不行。事实就是这么简单。使用相同的编译器,成功的概率非常高——但是没有保障,更改编译器选项可能会让你意想不到地收到影响。
另外,你的int也应该使用int16_tint32_t或其他已定义大小的整数。(你应该包含stdint.h以获取这些类型定义。)在同一平台上,C和C++使用不同的int极不可能,但是对于固件来说,使用int是一个坏代码习惯。例外情况是在那些真正不关心int长度的地方,但必须清楚的是,接口和结构必须有明确定义。程序员很容易就int的大小做出错误的假设,并且当问题出现时,结果通常是灾难性的——更糟糕的是,他们经常在测试中没有出错,很难找到并修复问题。

如果我没记错的话,C++标准保证 sizeof(bool) == 1 - sbi
@sbi 我会对此感到惊讶,因为在某些处理器上,字节寻址比本机字寻址更慢。这可能对C++来说是正确的,但我不确定。我知道C语言不能保证这一点,并且在32位ARM核心上使用GCC默认生成32位布尔值。 - Graham
@sbi 我的回答并不是关于C++的行为,而是关于C的行为。正如我所说,我个人有GCC将C bool编译为整数大小(在该处理器上为4字节)的经验。为了解决当我遇到这个问题时的问题,我让我的程序打印所有类型的sizeof。bool报告为4。 - Graham
如果你在第一句话中提到了C语言,你可能需要提一下。(就像我在第一条评论中所做的那样。) - sbi
1
@sbi 你的记忆有误:https://en.cppreference.com/w/cpp/language/types#Boolean_type - JDługosz
1
@JDługosz 哎呀,谢谢。 - sbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接