![](/img/trans.png)
[英]What are the actual stuff in the jmp_buf when using setjmp and longjmp?
[英]Using setjmp and longjmp with a local jmp_buf
在本地jmp_buf
實際上由寄存器而不是堆棧內存表示的情況下,當setjmp
從longjmp
返回時, setjmp
或longjmp
是否可能導致本地jmp_buf
的內容不確定?
建議的重復是否允許為一次 setjmp() 調用多次執行 longjmp() ? 在全局變量的上下文中詢問。 之所以建議這樣做,是因為答案解釋說該變量沒有以阻止其隨后被調用的方式進行修改,這也足以回答局部變量的問題。
但是,局部變量的處理不同於全局變量。 特別是,如果本地jmp_buf
變量實際上保存在寄存器中而不是內存中,那么longjmp
之后的恢復可能不會呈現可重用的jmp_buf
變量。
作為一項學術練習,我試圖使用setjmp
作為goto
的替代品。 為了使循環替換保持在函數的本地,使用的jmp_buf
也是一個局部變量。
void foo (int n) {
jmp_buf jb;
volatile int i;
i = setjmp(jb);
if (i < n) {
do_stuff(i);
longjmp(jb, ++i);
}
}
我了解在setjmp
調用和longjmp
調用之間修改的非易失性局部變量在longjmp
之后未指定。 但是,我對本地jmp_buf
變量本身很好奇,特別是在jmp_buf
變量由寄存器而不是堆棧上的內存表示的情況下。
尚不清楚longjmp
本身是否可以被視為可能修改本地jmp_buf
變量的東西,以及這是否意味着在調用longjmp
后setjmp
返回時未指定其內容。
我以為我可以通過將jb
聲明為volatile
來輕松解決問題,但這觸發了警告(我將其視為錯誤):
... error: passing argument 1 of ‘_setjmp’ discards ‘volatile’ qualifier from pointer target type [-Werror=discarded-qualifiers]
setjmp(jb);
^~
此外, setjmp
的規范並沒有說明它是在設置jmp_buf
之后還是在設置jmp_buf
之前保存寄存器值。
如果我需要關心它,我可以創建jmp_buf
的易失副本並復制其內容。 但是,如果不需要,我想避免這種情況。
C11 標准部分§7.13.2.1 第 3 點規定:
所有可訪問對象都有值,並且抽象機的所有其他組件都有狀態,截至調用
longjmp
函數時,除了包含調用相應setjmp
的函數的本地自動存儲持續時間的對象的值沒有volatile
限定類型並且已在setjmp
調用和longjmp
調用之間更改的宏是不確定的。
您的jmp_buf
對象在setjmp(jb)
和longjmp(jb, ++i)
之間沒有變化。 正如標准所建議的那樣,在調用之間更改的唯一變量是i
,它被聲明為volatile
。
因此,要回答您的問題, longjmp
本身不能“修改本地jmp_buf
的內容 [以這種方式] 在setjmp
返回時導致其內容未定義”,但是通過其他方式修改兩個調用之間的jmp_buf
可以肯定會惹麻煩的。
沒關系。
在相關說明中,您不需要i
上的volatile
,因為它是由setjmp()
分配的。
在仔細閱讀longjmp()
的手冊頁和我的 K&R C 副本時, jb
的內容僅在您的函數體內無效,這意味着如果再次調用longjmp()
,它將看到jb
的有效視圖。 在有效代碼不會在較新的標准版本中失效的合理假設下,這仍然適用於今天。
TL;DR 您不需要將jmp_buf
類型的變量標記為 volatile。
TL;DR由於標准不明確,最好在本地longjmp
之后將本地jmp_buf
的值視為不確定。
ISO/IEC 9899:2018 §17.13.1.1 ¶2 描述了setjmp
的行為,而 ¶3 描述了返回時發生的情況。
setjmp
宏將其調用環境保存在其jmp_buf
參數中,以供longjmp
函數稍后使用。...
如果返回來自直接調用,則
setjmp
宏返回值零。 如果返回來自對longjmp
函數的調用,則setjmp
宏返回一個非零值。
我們推斷從setjmp
成功返回會導致初始化jmp_buf
參數。 但是,沒有提及初始化是否考慮到jmp_buf
本身具有自動存儲持續時間(因此,它本身可以由寄存器而不是內存來表示)。
ISO/IEC 9899:2018 §7.13.2.1 ¶3 描述了longjmp
的行為,措辭與Marko引用的 2011 文本相同:
在調用
longjmp
函數時,所有可訪問對象都有值,並且抽象機254的所有其他組件都有狀態,除了包含調用的函數的本地自動存儲持續時間的對象的值不具有 volatile 限定類型並且在setjmp
調用和longjmp
調用之間已更改的相應setjmp
宏是不確定的。
254)這包括但不限於浮點狀態標志和打開文件的狀態。
但是,之間這個詞的含義有些難以捉摸。 該標准可以明確指定between的上下文以表示setjmp
完成后。 例如,措辭可能是這樣的:
...
setjmp
返回和longjmp
調用之間的更改是不確定的。
當前的措辭建議應該包括調用setjmp
本身作為可能觸發不確定條件的東西。
然而, longjmp
返回的語義有可能涵蓋了這個問題。 ISO/IEC 9899:2018 §17.13.2.1 ¶4 規定:
longjmp
完成后,線程繼續執行,就好像setjmp
宏的相應調用剛剛返回了val
指定的值一樣。 ...
這句話可以解釋為無論是從直接調用返回還是從longjmp
函數返回, setjmp
的調用語義都是一樣的。 也就是說, setjmp
的返回意味着jmp_buf
參數被初始化並且可以被另一個longjmp
使用。 但同樣,這並不清楚。 在最具限制性的解釋中, as if子句僅涉及setjmp
返回的值,而不涉及調用本身。
由於語義不明確,因此在從longjmp
返回時將jmp_buf
對象值視為不確定是正確的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.