C++ 编译器优化了我的 bool 检查函数

c++ compiler optimized out my bool check function

我在做JNI开发的时候发现了一些有线的结果。

这是我的测试代码:

#include <stdint.h>
#include <iostream>
#define JNI_FALSE   0
#define JNI_TRUE    1

typedef uint8_t jboolean;

inline jboolean bool2jboolean( bool b ) { return b ? JNI_TRUE : JNI_FALSE; }

void test(bool *b) {

    volatile jboolean t = bool2jboolean(*b);

    std::cout << "t as int: " << (int)t << std::endl;
    std::cout << "t as bool : " << (bool)t << std::endl;
    std::cout << "t as jboolean(char) : " << t << std::endl;
}
int main() {
    int a = 105;
    test((bool*)&a);
    return 0;
}

-O0 工作正常,但如果我打开 -O3,似乎 bool2jboolean 不知何故被编译器优化了:

$ clang++ -O0 test.cpp
$ ./a.out 
t as int: 1
t as bool : 1
t as jboolean(char) : 

$ clang++ -O3 test.cpp
$ ./a.out         
t as int: 105
t as bool : 1
t as jboolean(char) : i

我不确定这里发生了什么。

编译器是否假定布尔值始终为 0 或 1?

(所以 bool2jboolean 变成了一个无意义的函数?)

在我的 mac 上测试:

$ clang++ -v
Apple clang version 12.0.0 (clang-1200.0.32.28)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

编辑: 我们不能确保 *b 指向一个“规范化”的 bool(例如,一个底层有随机值的未初始​​化的 bool),这也是我们引入 bool2jboolean 的原因,希望它可以“检查”bool 和 return 一个验证 jboolean。 (否则,如果 t 不是 0 或 1,JNI 运行时将抛出。)

但似乎 bool2jboolean 完全没有做到这一点。

我们如何检查未初始化的布尔值? (按位运算是剩下的唯一方法了??)

你故意欺骗编译器,告诉它 a 的内存是合法的 bool,尽管 C++ 明确表示 bool 只能是 truefalse。高度优化下的编译器发现true1false0,分别匹配JNI_TRUEJNI_FALSE,所以它可以直接复制 bool 的值,而不是进行条件选择。由于 bool“不能”是其他任何东西,因此这是一个安全的优化。你对编译器说谎使它变得不安全。

如果你只是完成了:

int main() {
    int a = 105;
    bool b = a;
    test(&b);
    return 0;
}

它会起作用,因为对 bool 的赋值标准化为 true(对于非零值)和 false(对于零值),而没有 假设 值已经是 truefalse。相比之下,您的演员表改变了在没有规范化的情况下解释内存的方式(您断言指向的内存已经已经以正常形式)。