簡體   English   中英

將 setjmp 和 longjmp 與本地 jmp_buf 一起使用

[英]Using setjmp and longjmp with a local jmp_buf

在本地jmp_buf實際上由寄存器而不是堆棧內存表示的情況下,當setjmplongjmp返回時, setjmplongjmp是否可能導致本地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變量的東西,以及這是否意味着在調用longjmpsetjmp返回時未指定其內容。

我以為我可以通過將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.

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