从 C 到 C++ 再返回 bool

Getting bool from C to C++ and back

提问人:sbi 提问时间:10/13/2016 最后编辑:sbi 更新时间:8/10/2020 访问量:8482

问:

在设计要通过连接 C 和 C++ 代码的 C API 传递的数据结构时,使用安全吗?也就是说,如果我有这样的:boolstruct

struct foo {
  int bar;
  bool baz;
};

是否保证 C(它是 )和 C++ 以相同的方式解释 的大小和含义以及它在其中的位置?bazfoo_Bool

我们正在考虑在单个平台上执行此操作(Beaglebone 上的 Debian 8 的 GCC),C 和 C++ 代码由相同的 GCC 版本(分别为 C99 和 C++11)编译。不过,也欢迎一般性评论

C++ C

评论

12赞 martinkunev 10/13/2016
C 中的 bool 与 C++ 中的 bool 不同。C 中的 bool 实际上是 C99 中引入的 _Bool 类型的 typedef
1赞 sbi 10/13/2016
@martinkunev嗯,我们当然知道这一点。Lemme 添加一个声明来明确这一点......
8赞 Jabberwocky 10/13/2016
sizeof(bool)和 (C99 布尔值) 取决于实现。如果它们在你的平台上与一组给定的编译器的大小相同,我认为只要你坚持使用该编译器就是安全的。关于结构布局的填充,即使布尔类型的大小相同,这也可能是另一回事。sizeof(_Bool)
2赞 Pavel 10/13/2016
@sbi stackoverflow.com/questions/3529831/......我找到了这个链接,有用吗?
3赞 nwellnhof 10/13/2016
Visual C++ 在 5.0 版中bool 的大小从 32 位更改为 8 位。因此,从理论上讲,您应该确保永远不要混合来自不同编译器版本的目标代码。OTOH,任何现代编译器都不太可能使用大于 8 位的任何内容。

答:

50赞 paulotorrens 10/13/2016 #1

C 和 C++ 的类型不同,但是,只要您坚持使用相同的编译器(在您的例子中是 gcc),它应该是安全的,因为这是一个合理的常见场景。bool

在 C++ 中,一直是一个关键字。C 直到 C99 才有一个,在那里他们引入了关键字(因为人们习惯于在 C89 代码中键入 def 或 #define as 或,因此直接添加为关键字会破坏现有代码);在 C 中,标头应该具有 typedef 或 #define from to 。看看你的;GCC 的实现如下所示:bool_Boolboolintcharboolstdbool.h_Boolbool

/*
 * 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 中,这两种类型是兼容的(在大小和对齐方式上,因此结构布局将保持不变)。

另外值得注意的是,GCC 和大多数其他编译器(Visual Studio 除外;Matthieu M. 在下面的评论中指出)在许多平台上使用的 Itanium ABI 指定了这一点并遵循相同的规则。这是一个强有力的保证。我们可以得到的第三个提示是从 Objective-C 的参考手册中,它说 Objective-C 和 Objective-C++ 分别尊重 C 和 C++ 的约定,并且是等效的;所以我几乎可以说,虽然标准不能保证这一点,但你可以假设是的,它们是等价的。_Boolboolbool_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,并且 _Boolbool 是兼容的,两者都是大小为 1,对齐方式为 1,代表机器的无符号字节。所以这个问题的答案,在 Beaglebone 上,是的,_Boolbool 是兼容的。

评论

26赞 paulotorrens 10/13/2016
如果我找到一个同时支持 C 和 C++ 但实现方式不同的编译器,我会追捕它的程序员,我会找到他们,我会让他们坐下来向他们解释为什么这是一个巨大的错误。bool_Bool
8赞 Peter 10/13/2016
只有当 C 代码和 C++ 代码都编译为 C++(使用 g++)时,gcc 的实现才提供兼容性。它不提供实际编译为 C 的 C 代码和 C++ 代码之间的兼容性。
6赞 Matthieu M. 10/13/2016
我会更强调 Itanium ABI:这是除 Visual Studio 之外的所有主要 C++ 播放器实现的 ABI,这意味着兼容性不仅在单个版本的 GCC 中得到保证,而且实际上在编译器和版本之间得到保证,它还包括未来的版本。很难找到更好的保证。然而。。。值得链接到 ABI 中提到这一点的具体部分;我发现 mentorembedded.github.io/cxx-abi/abi.html#pod 这就是你在想的吗?
5赞 Heshy 10/13/2016
他们为什么要为 #define bool bool #define false false #define true true(在 #else 部分)而烦恼?
13赞 paulotorrens 10/14/2016
@Heshy,我认为这是因为一些遗留程序会检查或类似的东西。实际上,我已经在野外看起来已经这样了。如果不是 #defined,这样的代码就会失败。#ifndef bool /**/ typedef int bool; /**/ #endifboolbool
16赞 Art 10/13/2016 #2

语言标准对此只字未提(我很高兴被证明是错的,我找不到任何东西),所以如果我们只是将自己限制在语言标准上,那就不安全了。但是,如果您对支持的架构很挑剔,您可以找到他们的 ABI 文档,看看它是否安全。

例如,amd64 ABI 文档的类型有一个脚注,内容如下:_Bool

此类型在 C++ 中称为 bool。

我无法以任何其他方式解释它,只能说它是兼容的。

另外,只是沉思一下。当然会起作用。编译器生成的代码既遵循 ABI,又遵循平台最大编译器的行为(如果该行为在 ABI 之外)。C++ 的一大优点是它可以链接到用 C 编写的库,关于库的一件事是它们可以由同一平台上的任何编译器编译(这就是为什么我们首先有 ABI 文档)。在某些时候会有一些轻微的不兼容吗?当然可以,但最好通过向编译器制造商报告错误来解决此问题,而不是在代码中解决此问题。我怀疑 bool 会是编译器制造商会搞砸的东西。

6赞 Hatted Rooster 10/13/2016 #3

C标准唯一说的是:_Bool

声明为 type 的对象足够大,可以存储值 0 和 1._Bool

这意味着至少或更大(因此/保证是可存储的)。_Boolsizeof(char)truefalse

确切的大小是Michael在评论中所说的所有实现。你最好只是在相关的编译器上对它们的大小执行一些测试,如果这些测试匹配并且你坚持使用同一个编译器,我认为它是安全的。

评论

2赞 paulotorrens 10/13/2016
但保证是 1。每种类型都等于或大于此值。sizeof(char)
1赞 Hatted Rooster 10/13/2016
@paulotorrens不过,我就是这么说的。
6赞 Graham 10/13/2016 #4

正如 Gill Bates 上面所说,在 C 语言中,你确实遇到了一个依赖于编译器的问题。不能保证同一个编译器在 C 和 C++ 中会相同地对待它,或者它们在不同的架构上会相同。编译器甚至有权(根据 C 标准)将其表示为位域中的单个位,如果它愿意的话。sizeof(bool)

我个人在使用 TI OMAP-L138 处理器时体验过这种情况,该处理器在同一器件上结合了 32 位 ARM 内核和 32 位 DSP 内核,两者都可以访问一些共享内存。ARM 内核表示为(此处为 32 位),而 DSP 表示为(8 位)。为了解决这个问题,我定义了自己的类型以用于共享内存接口,因为我知道 32 位值对双方都有效。当然,我可以将其定义为 8 位值,但我认为如果我将其保留为本机整数大小,则不太可能影响性能。boolintboolcharbool32_t

如果你和我做同样的事情,那么你可以100%保证你的C和C++代码之间的二进制兼容性。如果你不这样做,那么你就不能。就这么简单。使用相同的编译器,您的几率非常高 - 但无法保证,并且更改编译器选项很容易以意想不到的方式搞砸您。

在相关主题上,您还应该使用 ,或其他定义大小的整数。(应包括这些类型定义。在同一平台上,C和C++不太可能有所不同,但是对于固件来说,这是代码的味道。例外情况是,在你真的不在乎 an 有多长的地方,但应该清楚的是,接口和结构必须有明确的定义。对于程序员来说,对它的大小做出假设(通常是不正确的!)太容易了,而且当它出错时,结果通常是灾难性的——更糟糕的是,它们在测试中通常不会出错,因为你可以很容易地找到并修复它们。intint16_tint32_tstdint.hintint

评论

0赞 sbi 2/4/2020
IIRC,C++ 标准保证 .sizeof(bool) == 1
0赞 Graham 2/4/2020
@sbi我会对此感到惊讶,因为在某些处理器上,字节寻址比本机字寻址慢。对于C++来说可能是真的,我不知道。我确实知道 C 不能保证这一点,并且默认情况下将 GCC 用于 32 位 ARM 内核会产生 32 位布尔值。
0赞 Graham 7/13/2020
@sbi 我的回答不是关于C++行为,而是关于C行为。正如我所说,我有 GCC 将 C bool 编译为整数大小(该处理器上为 4 个字节)的个人经验。为了弄清楚我遇到这个问题时的问题,我让程序打印了所有类型的大小。Bool 报告了 4.
1赞 JDługosz 8/30/2021
@sbi 你记不清了:en.cppreference.com/w/cpp/language/types#Boolean_type
1赞 sbi 9/14/2021
@JD ługosz 呃。谢谢。