簡體   English   中英

將空字符串文字初始化或分配給 C 中指向 char 的指針或 C++ 中指向 const char 的指針的原因是什么?

[英]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_conditionsome_other_condition為真,但是這種代碼對您來說仍然看起來很可怕(對我來說確實如此)。

如果您根本不初始化s並且沒有一個條件為真,則您的程序的行為是未定義的。 它可能會崩潰,也可能不會。 您以后將永遠無法檢查錯誤情況,因為s可以是任何字面意思。

如果您使用NULL初始化s ,您現在可以檢查錯誤情況,但您的for循環仍然包含 UB。

這里最安全的方法顯然是包含像else { assert(0); }類的else { assert(0); } else { assert(0); }並明確檢查的錯誤,但如果這不是在您的情況需要,可以初始化s"" ,代碼將只是做什么,如果some_conditionsome_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.

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