」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Bjarne Stroustrup 程式碼中的 std::string 連結中是否潛藏著未定義的行為?

Bjarne Stroustrup 程式碼中的 std::string 連結中是否潛藏著未定義的行為?

發佈於2024-11-11
瀏覽:849

Does Undefined Behavior Lurk in std::string Chaining in Bjarne Stroustrup\'s Code?

Bjarne Stroustrup 程式碼中的 std::string 連結表達式是否表現出未定義的行為?

In Bjarne Stroustrup 的《C 程式語言》第四版,程式碼片段舉例說明了使用std::string 的替換進行連結方法:

void f2() {
    std::string s = "but I have heard it works even if you don't believe in it";
    s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");

    assert(s == "I have heard it works only if you believe in it");
}

但是,此程式碼表現出未指定的行為而不是呼叫未定義的行為。

此未指定行為的原因在於評估順序,而評估順序未指定鍊式函數呼叫的子表達式。在這種情況下,s.find 函數呼叫在第一個 s.replace 呼叫之前或之後進行評估,改變結果字串的長度並影響後續 find 呼叫的結果。

問題中的範例示範了這一點:當由不同的編譯器(clang、gcc)評估時,由於不同的評估而獲得不同的結果order.

Details

函數參數具有未指定的求值順序,雖然連結函數呼叫為每個函數呼叫引入了從左到右的求值順序,但參數每個呼叫的順序僅在 之前相對於 該特定函數呼叫。

在範例中,這種不確定性出現在評估中s.find("even") 和s.find(" don't") 相對於s.replace(0, 4, "" ).

忽略進一步的子表達式細分,序列評估步驟及其相互依賴關係可以描述如下:

Step 1: s.replace(0, 4, "")  // A
Step 2: s.find("even")       // B
Step 3: s.replace(B, 4, "only") // C
Step 4: s.find("don't")      // D
Step 5: s.replace(D, 6, "")   // E

雖然A排序在B之前,B又排序在C之前,但是之間沒有排序關係 B和D 相對於A。因此,D 可以在 A 之前或之後進行評估,從而根據所選順序產生不同的結果。

C 17更改

C 17 標準加強了後綴表達式及其表達式清單的求值規則的順序,為相關代碼提供了明確的行為。排序如下:

  • 後綴表達式在表達式清單中的每個表達式之前排序。
  • 每個參數的初始化相對於任何參數的初始化是不確定排序的其他參數。

因此,在 C 17 及更高版本中,此程式碼將始終正確計算。

版本聲明 本文轉載於:1729674849如有侵犯,請洽[email protected]刪除
最新教學 更多>

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3