在 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
的指针的原因是什么?
喜欢f.e.:
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
数组。
我也读过:,但这个问题涵盖了将空字符串分配给先前由 malloc()
函数返回的指针的错误方法。
这完全取决于接下来的内容。例如,以下内容是可以接受的:
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);
}
用空字符串文字初始化一个 char 指针确实有好处,实际上空字符串文字不是 "empty"。如果您创建一个虚拟程序并使用调试器查看 char* p = "";
,您将看到创建了一个长度为 1 且包含 [=11=]
的字符数组。这意味着 p
指向有效的零终止字符串。因此,您可以将 p 传递给大多数使用零终止字符串的函数(例如,基本上所有标准库字符串操作函数),而不必担心它们 failing/memory 错误等。这很有用,例如在您正在为 p
分配一些值,这取决于某些可能会失败的条件,如果您没有使用适当的值初始化指针,就会给您留下潜在的未定义行为。
关于最后一点,还有一些编码标准禁止未初始化变量的问题,因为这些是错误的潜在来源。
我不一定会说这是某种“编码技术”。相反,我认为在很多情况下,它比其他选择更好。
初始化为 ""
有时可能是首选,因为它更安全。假设您有这样的代码:
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
并且 none 的条件为真,那么您的程序的行为是未定义的。它可能会崩溃,也可能不会。您以后将永远无法检查错误情况,因为 s
可以是任何字面意思。
如果您使用 NULL
初始化 s
,您现在可以检查错误条件,但您的 for
循环仍然包含 UB。
这里最安全的方法显然是包含类似 else { assert(0); }
的内容并显式检查错误情况,但如果您的情况不需要这样做,您可以将 s
初始化为 ""
如果 some_condition
和 some_other_condition
都为假,代码将什么都不做。
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++?
在我看来,这里似乎存在误区。指针 未 用空字符串初始化。它被初始化为 指向 一个空字符串(编译器放置在内存中某处的字符串文字)。这是一个主要的区别。
考虑这段代码:
char* p = "";
printf("%p\n", (void*)p);
p = "test";
printf("%p\n", (void*)p);
可能的输出:
0x563e72497007
0x563e72497008
在这些情况下,p
保存编译器放置两个字符串文字的内存地址(即“”在 0x563e72497007 和 "test" 在 0x563e72497008)。所以在那个记忆中你有:
0x563e72497007 '[=12=]' (i.e. the empty string that only consists
a string termination character`)
0x563e72497008 't' 'e' 's' 't' '[=12=]' (i.e. the string "test")
所以再次 - p
不是 initialized/assigned 与字符串,它是 initialized/assigned 到 指向字符串 .
为什么要初始化一个指向空字符串的指针?
好吧,这就像您初始化的任何其他变量一样...您这样做是因为您希望该变量具有已知值,以防在对它进行任何其他赋值之前使用它。所以就像做 int i = 0;
.
换句话说 - 你做 char* p = "";
以确保 p
指向有效的 C 风格字符串。
一个非常简单的例子:
char* p = "";
if (answerIsWrong())
{
p = "not";
}
printf("The answer is %s correct\n", p);
根据函数 answerIsWrong()
的 return 值,这可能会打印:
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 = "";
产生一个有效的以 null 结尾的字符串(代表一个空字符串)。因此它可以用于接受 c-style 字符串等的函数中
这不同于例如:
const char *p = nullptr;
这不是有效的字符串,并且在大多数接受 C 风格字符串的函数中都会失败(例如,std::string(nullptr)
会产生 UB,很可能会崩溃)。
我不会称之为编程技术。
对于那些可能了解它的人来说,我的问题实际上很简单,但我问是因为我还不知道这种技术。
在 C 中将空字符串文字初始化或分配给指向 char
的指针或在 C++ 中指向 const char
的指针的原因是什么?
喜欢f.e.:
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
数组。
我也读过:malloc()
函数返回的指针的错误方法。
这完全取决于接下来的内容。例如,以下内容是可以接受的:
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);
}
用空字符串文字初始化一个 char 指针确实有好处,实际上空字符串文字不是 "empty"。如果您创建一个虚拟程序并使用调试器查看 char* p = "";
,您将看到创建了一个长度为 1 且包含 [=11=]
的字符数组。这意味着 p
指向有效的零终止字符串。因此,您可以将 p 传递给大多数使用零终止字符串的函数(例如,基本上所有标准库字符串操作函数),而不必担心它们 failing/memory 错误等。这很有用,例如在您正在为 p
分配一些值,这取决于某些可能会失败的条件,如果您没有使用适当的值初始化指针,就会给您留下潜在的未定义行为。
关于最后一点,还有一些编码标准禁止未初始化变量的问题,因为这些是错误的潜在来源。
我不一定会说这是某种“编码技术”。相反,我认为在很多情况下,它比其他选择更好。
初始化为 ""
有时可能是首选,因为它更安全。假设您有这样的代码:
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
并且 none 的条件为真,那么您的程序的行为是未定义的。它可能会崩溃,也可能不会。您以后将永远无法检查错误情况,因为 s
可以是任何字面意思。
如果您使用 NULL
初始化 s
,您现在可以检查错误条件,但您的 for
循环仍然包含 UB。
这里最安全的方法显然是包含类似 else { assert(0); }
的内容并显式检查错误情况,但如果您的情况不需要这样做,您可以将 s
初始化为 ""
如果 some_condition
和 some_other_condition
都为假,代码将什么都不做。
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++?
在我看来,这里似乎存在误区。指针 未 用空字符串初始化。它被初始化为 指向 一个空字符串(编译器放置在内存中某处的字符串文字)。这是一个主要的区别。
考虑这段代码:
char* p = "";
printf("%p\n", (void*)p);
p = "test";
printf("%p\n", (void*)p);
可能的输出:
0x563e72497007
0x563e72497008
在这些情况下,p
保存编译器放置两个字符串文字的内存地址(即“”在 0x563e72497007 和 "test" 在 0x563e72497008)。所以在那个记忆中你有:
0x563e72497007 '[=12=]' (i.e. the empty string that only consists
a string termination character`)
0x563e72497008 't' 'e' 's' 't' '[=12=]' (i.e. the string "test")
所以再次 - p
不是 initialized/assigned 与字符串,它是 initialized/assigned 到 指向字符串 .
为什么要初始化一个指向空字符串的指针?
好吧,这就像您初始化的任何其他变量一样...您这样做是因为您希望该变量具有已知值,以防在对它进行任何其他赋值之前使用它。所以就像做 int i = 0;
.
换句话说 - 你做 char* p = "";
以确保 p
指向有效的 C 风格字符串。
一个非常简单的例子:
char* p = "";
if (answerIsWrong())
{
p = "not";
}
printf("The answer is %s correct\n", p);
根据函数 answerIsWrong()
的 return 值,这可能会打印:
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 = "";
产生一个有效的以 null 结尾的字符串(代表一个空字符串)。因此它可以用于接受 c-style 字符串等的函数中
这不同于例如:
const char *p = nullptr;
这不是有效的字符串,并且在大多数接受 C 风格字符串的函数中都会失败(例如,std::string(nullptr)
会产生 UB,很可能会崩溃)。
我不会称之为编程技术。