在為什么 volatile 適用于 setjmp/longjmp中,用戶greggo評論:
實際上,現代 C 編譯器確實需要知道 setjmp 是一種特殊情況,因為通常存在由 setjmp 引起的流變化可能會嚴重破壞事物的優化,這些需要避免。回到 K&R 時代,setjmp 不需要特殊處理,也沒有得到任何處理,因此適用于當地人的警告。由于該警告已經存在并且(應該!)被理解 - 當然,setjmp 的使用非常罕見 - 現代編譯器沒有動力去任何額外的長度來解決“clobber”問題 - 它仍然是在語言中。
是否有任何參考資料對此進行了詳細說明,如果這是真的,是否可以安全地存在(行為不會比標準 setjmp/longjmp 更容易出錯)setjmp/longjmp 的定制實作(例如,也許我想要保存一些命名不同的額外(執行緒本地)背景關系)?反正有沒有告訴編譯器“這個函式實際上是 setjmp/longjmp”?
uj5u.com熱心網友回復:
setjmp/longjmp保證與優化器的任何不透明函式(不可行內)的外觀相同。對非本地人的副作用volatile可能已在編譯時重新排序。setjmp(或longjmp)如果轉義分析可以表明沒有全域變數可以有它們的地址。
所以我認為他們的原型不需要任何特殊屬性。glibc 宣告應該在呼叫它之前對區域變數產生副作用,例如,避免在非 void 函式的末尾出現有關執行的警告,longjmp例如__attribute__((noreturn))int foo(){ if(x) return y; longjmp(jmpbuf); }
來自 glibc 的/usr/include/setjmp.h
// earlier CPP macros to define __THROWNL as __attribute__ ((__nothrow__)) in C mode
extern int setjmp (jmp_buf __env) __THROWNL;
extern void longjmp (struct __jmp_buf_tag __env[1], int __val)
__THROWNL __attribute__ ((__noreturn__));
extern void siglongjmp (sigjmp_buf __env, int __val)
__THROWNL __attribute__ ((__noreturn__));
有一堆 C 前處理器來定義_版本等,但總的來說,它們中的任何一個看起來都不需要編譯器本身的任何特殊支持。(我不認為它依賴于 GCC / clang 已經將這些名稱識別為特殊的內置函式;如果他們這樣做,他們會將它們定義為__builtin_setjmp好像存在這樣的東西,但它不在GCC 手冊中。)
uj5u.com熱心網友回復:
首先,為什么volatile在鏈接的帖子中有效的正確答案是“因為 C 標準明確說明了這一點”。我不認為參考的部分是正確的,因為 C 明確列出了許多與setjmp/相關的定義不明確的行為longjmp。相關部分可以在 C17 7.13.2.1 中找到:
所有可訪問物件都有值,并且抽象機器的所有其他組件都有狀態,截至
longjmp呼叫函式時,除了自動存盤持續時間的物件的值,這些物件的值對于包含呼叫相應setjmp宏的函式來說是本地的沒有-qualified 型別并且在呼叫和呼叫volatile之間已更改是不確定的。setjmplongjmp
甚至 C90 說的也差不多。因此,無論現代與否,編譯器都不需要“修復這個”的原因是因為 C 從未要求它們這樣做。在發布參考評論的示例中,第二次if ( foo != 5 )執行時, 的值foo是不確定的(并且foo永遠不會占用它的地址),所以嚴格來說,該行只是呼叫未定義的行為,編譯器可以從那里隨心所欲- 這是由應用程式程式員而不是優化器創建的錯誤。
通常,任何使用的應用程式程式員setjmp.h都會得到什么。這是最糟糕的意大利面條編程形式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/495921.html
