簡體   English   中英

C ++:匯編代碼包含斷言結果

[英]C++: assembly code contains assert results

這是代碼:

#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反映在匯編代碼中,而行B沒有。

我的問題是:既然宏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))

(從我的/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和not true == false ,因此它知道它將調用第一個__assert_fail但永遠不會調用第二個。 由於它將永遠不會調用第二個函數,因此它不需要字符串"true == true" ,也不會費心包含它。

如果您使條件變得更困難,那么它就不會知道它需要哪些條件,因此它將包括所有內容。

編輯添加:即使您添加了具有更復雜條件的另一行,也不一定要添加該行。 例如, 這里我修改了您的代碼以添加必須在運行時進行的檢查:

#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()"字符串。 但是,如果您取消注釋第一個斷言,它將同時包含that和"true == false" ,因為它不知道Collat​​z猜想是否為真(公平地說,目前還沒有數學家做)。

*我通過運行g++ -E assert.cpp -o assert.ii來獲得預處理的代碼,然后運行cat assert.ii | sed '/^#/d'得到了這一點cat assert.ii | sed '/^#/d' cat assert.ii | sed '/^#/d'刪除文件名和行號的標記,並將代碼重新格式化為更具可讀性的內容,然后用"int main()"手動替換__PRETTY_FUNCTION__ 如果您對編譯器為何要執行某項操作感到困惑,那么查看這樣的預處理輸出可能會有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM