[英]What is the reason to initialize or assign an empty string literal to a pointer to char in C, or a pointer to const char in C++?
我的問題實際上對於那些可能知道它的人來說非常簡單,但我問這個問題是因為我已經不知道這種技術了。
將空字符串文字初始化或分配給 C 中指向char
的指針或 C++ 中指向const char
的指針的原因是什么?
像fe:
char* p = "";
或者
char* p;
p = "";
或者,正如評論中所建議的,在 C++ 中:
const char* p = "";
或者
const char* p;
p = "";
我確實經常看到這種技術,但不明白為什么有人要將空字符串文字分配給char
指針(或指向 C++ 的const char
指針)或它來自何處。
我已經閱讀了Initialize a string in C to empty string ,但這個問題的重點是用空字符串初始化一個char
數組。
而且我還閱讀了: Initializing a string with the empty string ,但這個問題涵蓋了將空字符串分配給malloc()
函數先前返回的指針的錯誤方法。
將空字符串文字初始化或分配給 C 中指向 char 的指針或 C++ 中指向 const char 的指針的原因是什么?
在我看來,這里似乎存在誤解。 指針未用空字符串初始化。 它被初始化為指向一個空字符串(編譯器放置在內存中某處的字符串文字)。 這是一個主要的區別。
考慮這個代碼:
char* p = "";
printf("%p\n", (void*)p);
p = "test";
printf("%p\n", (void*)p);
可能的輸出:
0x563e72497007
0x563e72497008
在這些情況下, p
保存了編譯器放置兩個字符串文字的內存地址(即 0x563e72497007 處的“”和 0x563e72497008 處的“test”)。 所以在那個記憶中你有:
0x563e72497007 '\0' (i.e. the empty string that only consists
a string termination character`)
0x563e72497008 't' 'e' 's' 't' '\0' (i.e. the string "test")
所以再次 - p
沒有用字符串初始化/分配,它被初始化/分配為指向 strings 。
為什么要初始化一個指向空字符串的指針?
嗯,它就像您初始化的任何其他變量一樣......您這樣做是因為您希望變量具有已知值,以防在對其進行任何其他分配之前使用它。 所以就像做int i = 0;
.
換句話說 - 你做char* p = "";
確保p
指向有效的 C 樣式字符串。
一個非常簡單的例子:
char* p = "";
if (answerIsWrong())
{
p = "not";
}
printf("The answer is %s correct\n", p);
根據函數answerIsWrong()
的返回值,這可能會打印:
The answer is correct
或者
The answer is not correct
在第一種情況下,重要的是p
被初始化為指向一個空字符串。
但是,如果您知道在為p
分配新值之前永遠不會使用它,那么顯然沒有理由初始化它! 然而,一些程序員更喜歡總是初始化所有變量——即使他們在使用前分配了另一個值。
例子:
char* p = ""; // No reason for this initialization
// p will be assigned another value before it's used
if (answerIsWrong())
{
p = " not ";
}
else
{
p = " absolutely ";
}
printf("The answer is %s correct\n", p);
這完全取決於接下來的內容。 例如,以下是可以接受的:
const char *p = "";
if (f()) {
p = "Foo";
}
else if (g())
p = "Bar";
}
strcat(msg, p);
也就是說,這不是一種可能的情況。 隨后分配給p
值可能是一個指向需要釋放的動態分配內存的指針,但是""
不能被釋放,所以你最終會得到
char *p = "";
int free_it = 0;
if (f()) {
p = ff();
free_it = 1;
}
else if (g())
p = gg();
free_it = 1;
}
strcat(msg, p);
if (free_it)
free(p);
當你可以很容易地
char *p = NULL;
if (f()) {
p = ff();
}
else if (g())
p = gg();
}
if (p)
strcat(msg, p);
free(p);
您在評論中鏈接的問題中采用的方法導致無法釋放這些值。 擁有一個節點分配器和一個節點析構器會更有意義。
int Node_init(Node *node, const char *value) {
char *value_ = strdup(value);
if (!value_)
return 0;
node->value = value_;
node->sibling = NULL;
node->child = NULL;
return 1;
}
Node *Node_new(const char *value) {
Node *node = malloc(sizeof(Node));
if (!node) {
return NULL;
}
if (!Node_init(node, value)) {
free(node);
return NULL;
}
return node;
}
void Node_destroy(Node *node) {
free(node->value);
}
void Node_delete(Node *node) {
Node_destroy(node);
free(node);
}
int main(void) {
Node root;
Node_init(&root, "");
...
Node_destroy(&root);
}
我不一定會說這是某種“編碼技術”。 相反,我認為,在許多情況下,它比替代方案更好。
初始化為""
有時可能是首選,因為它更安全。 假設你有這樣的代碼:
const char* s;
if(some_condition) {
s = something();
} else if(some_other_condition) {
s = something_else();
}
for(const char* p = s; *p; ++p) {
/* do something */
}
現在,假設您有 95% 的把握知道some_condition
或some_other_condition
為真,但是這種代碼對您來說仍然看起來很可怕(對我來說確實如此)。
如果您根本不初始化s
並且沒有一個條件為真,則您的程序的行為是未定義的。 它可能會崩潰,也可能不會。 您以后將永遠無法檢查錯誤情況,因為s
可以是任何字面意思。
如果您使用NULL
初始化s
,您現在可以檢查錯誤情況,但您的for
循環仍然包含 UB。
這里最安全的方法顯然是包含像else { assert(0); }
類的else { assert(0); }
else { assert(0); }
並明確檢查的錯誤,但如果這不是在您的情況需要,可以初始化s
來""
,代碼將只是做什么,如果some_condition
和some_other_condition
都是假的。
const char *p = "";
產生一個有效的以空字符結尾的字符串(代表一個空字符串)。 因此,它可以用於接受c 樣式字符串等的函數中。
這不同於例如:
const char *p = nullptr;
這不是一個有效的字符串,並且在大多數接受 c 樣式字符串的函數中都會失敗(例如std::string(nullptr)
將產生 UB,最有可能發生崩潰)。
我不會稱之為編程技術。
用空字符串字面量初始化一個 char 指針確實有好處,實際上空字符串字面量不是“空”的。 如果您創建一個虛擬程序並查看char* p = "";
使用調試器,您將看到,創建了一個長度為 1 且包含\\0
的字符數組。 這意味着p
指向一個有效的零終止字符串。 因此,您可以將 p 傳遞給使用零終止字符串的大多數函數(例如,基本上所有標准庫字符串操作函數),而不必擔心它們會失敗/內存錯誤等。這很有用,例如在您正在使用的情況下p
某些值取決於某些可能失敗的條件,如果您沒有使用正確的值初始化指針,則可能會導致潛在的未定義行為。
最后一點,還有一些編碼標准禁止未初始化變量的問題,因為它們是潛在的錯誤來源。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.