在 C++20 之前将 malloc 用于 int 未定义行为

ant*_*_rh 97 c++ malloc undefined-behavior c++20

有人告诉我,以下代码在 C++20 之前具有未定义的行为:

int *p = (int*)malloc(sizeof(int));
*p = 10;
Run Code Online (Sandbox Code Playgroud)

真的吗?

论点是int对象的生命周期在为其分配值之前没有开始(P0593R6)。要解决此问题,new应使用放置:

int *p = (int*)malloc(sizeof(int));
new (p) int;
*p = 10;
Run Code Online (Sandbox Code Playgroud)

我们真的必须调用一个微不足道的默认构造函数来启动对象的生命周期吗?

同时,代码在纯 C 中没有未定义的行为。但是,如果我int在 C 代码中分配 an并在 C++ 代码中使用它呢?

// C source code:
int *alloc_int(void)
{
    int *p = (int*)malloc(sizeof(int));
    *p = 10;
    return p;
}

// C++ source code:
extern "C" int *alloc_int(void);

auto p = alloc_int();
*p = 20;
Run Code Online (Sandbox Code Playgroud)

它仍然是未定义的行为吗?

Bar*_*rry 62

这是真的吗?

是的。从技术上讲,不属于:

int *p = (int*)malloc(sizeof(int));
Run Code Online (Sandbox Code Playgroud)

实际上创建了一个 type 的对象int,所以取消引用p是 UB 因为那里没有实际的int

我们真的必须调用默认构造函数来启动对象的生命周期吗?

是否必须按照 C++ 对象模型来避免 C++20 之前的未定义行为?是的。如果您不这样做,任何编译器实际上会造成伤害吗?不是我所知道的。

[...] 它仍然是未定义的行为吗?

是的。在 C++20 之前,您实际上还没有int在任何地方创建对象,所以这是 UB。


Yak*_*ont 43

是的,是UB。列举了一个int可以存在的方式列表,没有一个适用于那里,除非你认为 malloc 是因果关系。

它被广泛认为是标准中的一个缺陷,但不是一个重要的缺陷,因为 C++ 编译器围绕 UB 的特定部分所做的优化不会导致该用例出现问题。

至于第二个问题,C++ 并没有强制要求 C++ 和 C 如何交互。所以所有与 C 的交互都是...... UB,也就是 C++ 标准未定义的行为。

  • @CortAmmon C++20 中对象(任何类型)存在方式的枚举列表位于 [\[intro.object\]](https://eel.is/c++draft/basic#intro .object-1.sentence-2): (1) 根据定义 (2) 通过新表达式 (3) 根据 P0593 中的新规则隐式 (4) 更改联合体的活动成员 (5) 临时的。(3) 是 C++20 中的新增内容,(4) 是 C++17 中的新增内容。 (7认同)
  • 您能否扩展 int 存在方式的枚举列表?我记得问过一个关于基元类型的生命周期的类似问题,并被告知基元可以“存在”,只需说它存在,因为规范没有另外说明。听起来我可能错过了规范中有用的部分!我很想知道我应该仔细阅读哪一部分! (5认同)
  • @Ruslan:实现可以自由定义 ISO C++ 未定义的任何行为。(例如“gcc -fno-strict-aliasing”,或默认情况下的 MSVC)。说“实现定义”将“要求”所有 C++ 实现定义与某些 C 实现互操作的某种方式,因此无论它们是否想要做类似的事情,完全由实现决定是有意义的。 (4认同)
  • @PeterCordes:我想知道为什么这么多人未能认识到 IDB 和 UB 之间的区别,并采用一些奇特的概念,即标准未能强制要求所有实现都有意义地处理构造,这意味着不应期望任何实现都这样做,不这样做的实现决不能因此被视为低劣。 (4认同)
  • C/C++交互真的是UB吗?实现定义而不是未定义会更有意义,否则甚至使用“extern "C"”语法都会很奇怪。 (3认同)
  • 第二个问题:嗯,至少可以用定义的结果调用 C 标准库中的 C 代码:C++ 标准很好地定义了这种交互。这对于通用互操作性来说是个好兆头,即使这不是核心设计范例。 (2认同)
  • @Ruslan 这里的问题是你认为有两种 UB。好吧,有几种:编译器定义了行为的 UB,以及它没有定义的 UB。 (2认同)