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



我的问题实际上很简单,对于那些可能知道它的人来说,但我问是因为我还不知道这种技术。

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

比如:

char* p = "";

char* p;
p = "";

或者,如评论中所建议的,C++:

const char* p = "";

const char* p;
p = "";

我确实经常看到这种技术,但不明白为什么有人要为char指针(或指向const charC++的指针)分配一个空字符串文字,或者这是从哪里来的。


我已经阅读了将 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_conditionsome_other_condition始终都是正确的,但这种代码对您来说仍然看起来很可怕(对我来说确实如此)。

如果根本不初始化s并且所有条件都不为真,则程序的行为是未定义的。它可能会崩溃,也可能不会。您以后将永远无法检查错误条件,因为s实际上可能是任何东西。

如果使用NULL初始化s,现在可以检查错误条件,但for循环仍包含 UB。

这里最安全的方法显然是包含类似else { assert(0); }的东西并显式检查错误条件,但如果在您的情况下不需要这样做,您可以将s初始化为"",如果some_conditionsome_other_condition都是假的,代码将什么都不做。

const char *p = "";

生成一个有效的以 null 结尾的字符串(表示空字符串)。因此,它可以用于接受c 样式字符串等的函数中。

这与例如:

const char *p = nullptr;

这不是一个有效的字符串,并且在大多数接受 c 样式字符串的函数中会失败(例如。std::string(nullptr)将产生 UB,最有可能崩溃)。

我不会称它为编程技术。

使用空字符串文本初始化 char 指针确实具有以下优点,即实际上空字符串文本不是"空"。如果您创建一个虚拟程序并使用调试器查看char* p = "";,您将看到创建一个长度为 1 的 char 数组,其中包含。这意味着p指向一个有效的零终止字符串。因此,您可以将 p 传递给大多数使用零终止字符串的函数(例如,基本上所有标准库字符串操作函数),而不必担心它们失败/内存错误等。这是有用的,例如,如果您正在将某些值辅助到p,这取决于可能失败的某些条件,如果您没有使用正确的值初始化指针,则会留下潜在的未定义行为。

关于最后一点,还有一些编码标准禁止未初始化变量的问题,因为这些是错误的潜在来源。

最新更新