C++:汇编代码包含断言结果

标签 c++ assembly assert compiler-optimization

代码如下:

#include <cassert>

int main() {
    assert(true==false); // A
    assert(true==true);  // B
}

这是汇编代码( link ):

.LC0:
.string "/tmp/compiler-explorer-compiler11778-61-1sgmkbd.5d1m6g8pvi/example.cpp"
.LC1:
  .string "true==false"
main:
  push rbp
  mov rbp, rsp
  mov ecx, OFFSET FLAT:main::__PRETTY_FUNCTION__
  mov edx, 5
  mov esi, OFFSET FLAT:.LC0
  mov edi, OFFSET FLAT:.LC1
  call __assert_fail
main::__PRETTY_FUNCTION__:
  .string "int main()"

应触发断言失败的 A 行已反射(reflect)在汇编代码中,但 B 并未反射(reflect)。

我的问题是:现在宏 assert() 用于运行时断言,编译器如何知道结果并将其写入程序集?

编译器:gcc 7.1,优化-O0(即无优化)。我以为是因为优化,所以我故意通过这个选项关闭它(是吗?)。

编辑:现在assert()可以在编译时求值,它似乎与static_assert()重叠..

最佳答案

看起来您正在使用 GCC 和 GNU C 库,因此假设 NDEBUG未定义,assert宏可能定义如下:

# define assert(expr)                                                   \
  ((expr)                                                               \
   ? __ASSERT_VOID_CAST (0)                                             \
   : __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))

(复制 self 的 /usr/include/assert.h )

替换它(以及它所依赖的所有宏)* 会得到类似的结果

int main() {

  ((true == false)
       ? static_cast<void>(0)
       : __assert_fail("true==false", "assert.cpp", 4, "int main()"));

  ((true == true)
       ? static_cast<void>(0)
       : __assert_fail("true==true", "assert.cpp", 5, "int main()"));
}

即使在 -O0 ,编译器足够聪明,可以看到 true == true而不是true == false ,所以它知道它将调用第一个 __assert_fail但它永远不会调用第二个。由于它永远不会调用第二个函数,因此不需要字符串 "true == true"并且不费心将其包含在内。

如果你提出了更严格的条件,它就不会知道它需要哪些条件,所以它会包含所有内容。

编辑添加:即使您添加了具有更复杂条件的另一行,也不一定会添加该行。例如,here我修改了您的代码以添加必须在运行时进行的检查:

#include <cassert>

bool check_collatz_conjecture();

int main() {
    // assert(check_collatz_conjecture());
    assert(true==false);
    assert(true==true);
    assert(check_collatz_conjecture());
}

因为编译器知道第一个断言将被命中,并且因为 __assert_fail声明为 __attribute__((__noreturn__)) ,编译器知道它不需要担心函数的其余部分,并且它不包含 "check_collatz_conjecture()"字符串。如果您取消注释第一个断言,它将包括该断言和 "true == false"不过,因为它不知道 Collat​​z 猜想是否成立(公平地说,目前没有数学家知道这一点)。

* 我通过运行 g++ -E assert.cpp -o assert.ii 得到了这个获取预处理代码,然后运行 ​​cat assert.ii | sed '/^#/d'删除文件名和行号的标记并将代码重新格式化为更具可读性,然后手动替换 __PRETTY_FUNCTION__"int main()" s。如果您对编译器执行某些操作的原因感到困惑,那么查看像这样的预处理输出可能会有所帮助。

关于C++:汇编代码包含断言结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45573378/

相关文章:

c - RTOS 相对于单个顺序程序的优点

assembly - nasm 中的长 nop 指令

java - 使用 Selenium 和 Eclipse (Java) 断言表格边框的 css 样式

python - python 的 `assert` 语句有哪些可接受的用例?

c++ - 为什么指针用于 std::string.find?

c++ - 在 pthreads (Linux) 中使用成员函数

c++ - 我对吗 &str[0] 指向 len + 1 大小的缓冲区,包括空终止字符

字符串文字开始迭代器和结束迭代器的 C++ 宏

c++ - 我不知道如何打印数组,反过来,我也不知道如何交换数组中的元素

java - 与 Java 的断言不同,Groovy 的断言对于生产代码来说是一个好主意吗?