如果我有一個嵌入的空終止符[是:那個UB?],它是爲我準備好後訪問它的值嗎?正在訪問嵌入的空終止符UB後的字符串部分?
#include <stdio.h>
const char foo[] = "abc\0def";
int main() {
printf("%s", foo+4);
return sizeof(foo);
}
爲了記錄在案,它打印你所期望的:
def
如果我有一個嵌入的空終止符[是:那個UB?],它是爲我準備好後訪問它的值嗎?正在訪問嵌入的空終止符UB後的字符串部分?
#include <stdio.h>
const char foo[] = "abc\0def";
int main() {
printf("%s", foo+4);
return sizeof(foo);
}
爲了記錄在案,它打印你所期望的:
def
嵌入式null
不是未定義行爲。它可能是一個邏輯錯誤,如果您使用期望字符串爲空終止的函數。但是,訪問已成功分配的數組的全部範圍,無論其內容如何,都沒有任何錯誤,邪惡或未定義。
一件事,雖然觀察:如果你試圖存儲在std::string
這個數據(這是你應該如何處理所有的字符串,TBH),如何您存儲字符串也很重要。
std::string str1 = foo; //contents of str1 becomes "abc".
std::string str2 = std::string(foo, sizeof(foo)); //contents of str2 becomes "abc\0def"
[dcl.init.string]狀態
窄字符類型(3.9.1),char16_t陣列,char32_t陣列或陣列的wchar_t的陣列可以由一個窄字符串文字來初始化,char16_t字符串字面量,char32_t字符串字面量或寬字符串字面量,或者通過大括號(2.14.5)中包含的適當類型的字符串字面量。 字符串字面值的連續字符初始化數組的元素。
重點煤礦
所以嵌入式空它不是一個問題,它只是成爲數組的元素。由於數組的大小可容納所有字符並轉義序列,因此我們知道在嵌入null之後存在元素,並且訪問這些元素是安全的。
真的,嵌入式null的唯一問題是任何C函數在它命中null時都會停止,並且不會完整地處理該字符串。您可以考慮使用std::string
而不是這些問題。
訪問C字符串beyound終止空字符本身從來沒有是未定義的行爲。儘管如此,我們可以產生不確定的行爲這種方式,但對於一個完全不同的原因:
如果終止空字符恰好居住在該字符串保留的字符數組中的最後一個位置,那麼我們訪問此基礎數組如果我們在字符串末尾訪問字符串,就會超出其範圍。而這出界外的訪問是真正產生了不確定的行爲...
編輯:
[旁白:?是UB]
UB,不確定的行爲,是無法定義的行爲,因爲沒有有意義的行爲。依賴於未定義的行爲可能導致任何事情,包括獲得預期的結果,但可能在任何其他時間慘敗(例如,在另一個平臺上,在切換編譯器版本之後,在簡單地重新編譯之後,甚至在重新啓動一個和相同的程序之後)。因此,一個依賴未定義行爲的程序被認爲是不明確的。
示例:取消引用指向已刪除對象的指針(「懸掛指針」),或者接近問題:訪問數組超出邊界(可能導致嘗試訪問內存不與當前進程甚至不存在,但可能會讀取或(錯誤!!!)覆蓋恰好位於給定地址的完全不同對象的內存(每次您的程序都不必是相同的對象運行,甚至在一次程序運行中都沒有)
未定義的行爲不應與未指定的行爲(或同義詞,實現定義的行爲)混淆:在這種情況下,給定輸入的行爲已定義良好,但它留給編譯器供應商來定義一些給定的合理限制內的行爲。
示例:負整數的右移 - 它可以在有或沒有符號擴展的情況下發生(因此可以是算術或邏輯移位)。儘管標準沒有規定哪一個適用,但在負整數上使用右移是明確的。
關於旁邊:我問,因爲它似乎是一個合理的優化可能是放棄第一個空終止符後的一切 - 就像它可以指定,但我不知道它是否是。 [當然,我試着在Godbolt上編譯,並注意到實際發生的情況] – wrhall
「在第一個空終止符後下降」 - 不是100%你的意思是 - 數組在那裏,它有一個固定的大小,而且你不能釋放它的一部分。如果你的意思是「爲了不同的目的而重用」 - 你完全可以自由地做到這一點 - 只要確保你沒有超出數組的邊界...... – Aconcagua
對不起 - 它似乎是一個合理的*編譯器*優化可能是在空終止符之後放下所有東西......即不在空終止符之外分配一個數組。 – wrhall
不,內存是分配的,沒有UB這樣做。 – user0042
這很好,除了'sizeof'將返回數組的大小,而不是字符串的長度(如果這是你的意圖)。 –