当前位置: 首页 > 知识库问答 >
问题:

C++标准允许未初始化的bool使程序崩溃吗?

益英逸
2023-03-14
// Zero-filled global buffer of 16 characters
char destBuffer[16];

void Serialize(bool boolValue) {
    // Determine which string to print based on boolValue
    const char* whichString = boolValue ? "true" : "false";

    // Compute the length of the string we selected
    const size_t len = strlen(whichString);

    // Copy string into destination buffer, which is zero-filled (thus already null-terminated)
    memcpy(destBuffer, whichString, len);
}

我已经设置了一个编译器资源管理器示例,显示了反汇编中的问题,这里是完整的示例。注意:为了再现这个问题,我发现工作的组合是使用CLANG5.0.0和-O2优化。

#include <iostream>
#include <cstring>

// Simple struct, with an empty constructor that doesn't initialize anything
struct FStruct {
    bool uninitializedBool;

   __attribute__ ((noinline))  // Note: the constructor must be declared noinline to trigger the problem
   FStruct() {};
};

char destBuffer[16];

// Small utility function that allocates and returns a string "true" or "false" depending on the value of the parameter
void Serialize(bool boolValue) {
    // Determine which string to print depending if 'boolValue' is evaluated as true or false
    const char* whichString = boolValue ? "true" : "false";

    // Compute the length of the string we selected
    size_t len = strlen(whichString);

    memcpy(destBuffer, whichString, len);
}

int main()
{
    // Locally construct an instance of our struct here on the stack. The bool member uninitializedBool is uninitialized.
    FStruct structInstance;

    // Output "true" or "false" to stdout
    Serialize(structInstance.uninitializedBool);
    return 0;
}

问题的产生是因为优化器:它非常聪明地推断出字符串“true”和“false”的长度仅相差1。因此,它不是真正计算长度,而是使用bool本身的值,技术上应该是0或1,如下所示:

const size_t len = strlen(whichString); // original code
const size_t len = 5 - boolValue;       // clang clever optimization

虽然这很“聪明”,但我的问题是:C++标准允许编译器假设bool只能有“0”或“1”的内部数字表示,并以这种方式使用它吗?

或者这是一个实现定义的情况,在这种情况下,实现假设它的所有bools只包含0或1,并且任何其他值都是未定义的行为域?

共有1个答案

孟正志
2023-03-14

启用了优化的__attribute((noinline))构造函数仅从堆栈中加载一个字节以用作uninitializedbool时就会发生冲突。它为main中的对象留出了空间,并使用了push RAX(由于各种原因,它的效率与sub rsp,8相当),因此在main条目中的任何垃圾都是它用于uninitializedbool的值。这就是为什么实际得到的值不仅仅是0

5u-random垃圾可以轻松地包装成一个大的无符号值,导致memcpy进入未映射的内存。目的地在静态存储中,而不是堆栈中,因此您不会覆盖返回地址或其他内容。

其他实现可以做出不同的选择,例如false=0true=any non-zero value。那么clang可能不会为UB的这个特定实例生成崩溃的代码。(但如果它愿意,还是会被允许的。)我不知道有什么实现选择了x86-64为bool做的其他事情,但是C++标准允许很多在硬件上(比如当前的CPU)没有人做或者甚至不想做的事情。

编译时可见的UB总是很危险的,而且很难确定(使用链接时优化)您是否真的对编译器隐藏了UB,从而能够推断它将生成什么样的asm。

不要过于戏剧化;通常,编译器会让您在某些事情上逃脱惩罚,并像您所期望的那样发出代码,即使某些事情是UB的。但是,如果编译器开发人员实现一些优化来获得关于值范围的更多信息(例如,变量是非负的,可能允许它优化符号扩展以释放x86-64上的零扩展),那么将来可能会出现问题。例如,在当前的gcc和clang中,执行tmp=a+int_min不会像始终-false那样优化a<0,只是tmp始终为负数。(因为int_min+a=int_max在这个2的补码目标上是负数,而a不能高于这个值。)

因此gcc/clang当前不会回溯为计算的输入导出范围信息,只会根据基于无符号溢出假设的结果:Godbolt上的示例。不知道这是以用户友好的名义故意“漏掉”优化还是什么?

OTOH,glibc memcpy将执行两个4字节的加载/存储,其重叠取决于长度,因此这确实会使整个事情没有布尔值上的条件分支。请参阅glibc的memcpy/memmove中的l(between_4_7):块。或者至少对memcpy的分支中的任何一个boolean执行相同的方式来选择块大小。

如果内联,可以使用2xmov-immediate+cmov和一个条件偏移量,或者可以将字符串数据留在内存中。

或者,如果对Intel Ice Lake进行调优(具有快速短REP MOV特性),则实际的REP movsb可能是最佳的。glibcmemcpy可能会开始使用repmovsb来处理具有该功能的CPU的小尺寸,从而节省大量分支。

 类似资料:
  • 经过一些调试,我找到了这行代码 导致我的应用程序崩溃,我已经尝试过了 ; 但结果还是一样,有什么帮助吗??

  • 当我初始化的元素应用程序崩溃,如果我不初始化任何元素的应用程序显示的布局只是罚款和做一些基本的功能,我已经添加了使用在XML的onClick属性的方法 我需要从EditText元素中提取数据来进行计算。 java代码: 坠机记录: 致命例外:主进程:com.ajaydubey.fifamobilebulktradeprofitcounter,PID:17627java.lang.运行时异常:无法启

  • 问题内容: 我正在处理Android项目,但遇到了我无法理解的错误: 此处不允许使用数组初始化程序 我试图简化我的代码,这归结为 这里发生了什么? 问题答案: 你应该用 您只能在变量初始化时使用缩写语法。

  • 经过测试,它在非android程序上工作,但在android上初始化后立即崩溃。日志cat也没有生成任何错误消息。我从CircularFifoBuffer找到了这个。 编辑2: 08-05 21:12:48.837:I//system/bin/batteryd(1189):1566474 v_bat 3.678(3.677),i_supply 0.500,i_demand 0.282=>v_bat

  • 我使用CameraX用例在屏幕上显示来自摄像头的图像,同时对其执行对象检测。下面是与CameraX相关的代码,位于我的主要活动的onCreate()函数中。 尽管如此,我在一些(比如说1/5)应用程序初始化时遇到了一个错误,在工作和非工作情况之间没有任何代码或二进制修改: 如果我尝试不访问previewView.display以避免崩溃,我只是得到空裁剪的图像,直到我重新启动应用程序。 它似乎与视

  • 这些是我的分级依赖项: 这是崩溃日志 致命异常:主进程:com.example.myapp,pid:16817 java.lang.nosuchmethoderror:LCOM/mapbox/services/android/telemetry类中没有虚拟方法初始化(Landroid/content/context;LJava/lang/string;LJava/lang/string;LCOM/