我的问题实际上很简单,对于那些可能知道它的人来说,但我问是因为我还不知道这种技术。
初始化或分配空字符串文本到指向 C 中char
的指针或指向C++中const char
的指针的原因是什么?
比如:
char* p = "";
或
char* p;
p = "";
或者,如评论中所建议的,C++:
const char* p = "";
或
const char* p;
p = "";
我确实经常看到这种技术,但不明白为什么有人要为char
指针(或指向const char
C++的指针)分配一个空字符串文字,或者这是从哪里来的。
我已经阅读了将 C 中的字符串初始化为空字符串,但这个问题的重点是用空字符串初始化char
数组。
我还阅读了:使用空字符串初始化字符串,但此问题涵盖了将空字符串分配给之前由malloc()
函数返回的指针的错误方法。
或将空字符串文字分配给 C 中指向 char 的指针或指向 C++ 中 const char 的指针的原因是什么?
在我看来,这里似乎有一个误解。指针未使用空字符串初始化。它被初始化为指向空字符串(编译器放置在内存中某处的字符串文本)。这是一个重大区别。
请考虑以下代码:
char* p = "";
printf("%pn", (void*)p);
p = "test";
printf("%pn", (void*)p);
可能的输出:
0x563e72497007
0x563e72497008
在这些情况下,p
保存一个内存地址,编译器在其中放置了两个字符串文字(即 " 在 0x563e72497007 处,"test" 放在 0x563e72497008 处)。所以在那个记忆中,你有:
0x563e72497007 ' ' (i.e. the empty string that only consists
a string termination character`)
0x563e72497008 't' 'e' 's' 't' ' ' (i.e. the string "test")
所以再说一次 -p
没有初始化/分配字符串,它被初始化/分配为指向字符串。
为什么要初始化指针以指向空字符串?
好吧,它就像您初始化的任何其他变量一样...这样做是因为您希望变量具有已知值,以防在对其进行任何其他赋值之前使用它。所以就像做int i = 0;
.换句话说 - 您char* p = "";
确保p
指向有效的 C 样式字符串。
一个非常简单的例子:
char* p = "";
if (answerIsWrong())
{
p = "not";
}
printf("The answer is %s correctn", 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 correctn", 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); }
的东西并显式检查错误条件,但如果在您的情况下不需要这样做,您可以将s
初始化为""
,如果some_condition
和some_other_condition
都是假的,代码将什么都不做。
const char *p = "";
生成一个有效的以 null 结尾的字符串(表示空字符串)。因此,它可以用于接受c 样式字符串等的函数中。
这与例如:
const char *p = nullptr;
这不是一个有效的字符串,并且在大多数接受 c 样式字符串的函数中会失败(例如。std::string(nullptr)
将产生 UB,最有可能崩溃)。
我不会称它为编程技术。
使用空字符串文本初始化 char 指针确实具有以下优点,即实际上空字符串文本不是"空"。如果您创建一个虚拟程序并使用调试器查看char* p = "";
,您将看到创建一个长度为 1 的 char 数组,其中包含