在C语言中初始化字符串的正确方法

15

我看到过人们的代码像这样:

char *str = NULL;

我也看过这个问题,

char *str;

我想知道,初始化字符串的正确方式是什么?以及何时应该使用带有和不带有NULL的字符串初始化?


8
代码中并没有对字符串进行初始化,而是对字符指针进行了初始化。 - San Jacinto
11个回答

15
你必须在使用它之前设置它。这是你要遵循的唯一规则,以避免未定义的行为。无论你是在创建时初始化它还是在使用之前分配给它,都不相关。
就我个人而言,我更喜欢从未将变量设置为未知值,因此通常会执行第一个选项,除非它在接近(几行内)的地方设置。
事实上,在C99中,你不再需要在块的顶部声明局部变量,所以我通常会推迟创建变量,直到需要它,此时也可以进行初始化。
注意,在某些情况下,变量会被赋予默认值(例如,如果它们具有静态存储期间,例如在任何函数外部声明的文件级别)。但是,局部变量没有此保证。因此,如果你上面的第二个声明(char *str;)在函数内部,它可能包含垃圾值,尝试使用它将调用上述可怕的未定义行为。
C99标准的相关部分 6.7.8/10:
如果具有自动存储期间的对象没有明确初始化,则其值是不确定的。如果具有静态存储期间的对象没有明确初始化,则:
- 如果它具有指针类型,则初始化为null指针; - 如果它具有算术类型,则初始化为(正或无符号的)零; - 如果它是一个聚合体,则根据这些规则递归初始化每个成员; - 如果它是一个联合,则根据这些规则递归初始化第一个命名成员。

7
常量字符数组s的值为"Nice answer"。 - Matt Joiner

5
我想知道初始化字符串的正确方法是什么?
嗯,既然第二个片段定义了一个未初始化的指向字符串的指针,我会选择第一个。:)
总的来说,如果你想玩得安全,最好将所有指针都初始化为NULL;这样,很容易发现由未初始化指针引起的问题,因为对一个NULL指针进行解引用将导致崩溃(实际上,就标准而言,这是未定义的行为,但在我见过的每台机器上,这都是一次崩溃)。
但是,你不应该将指向字符串的NULL指针与空字符串混淆:指向字符串的NULL指针意味着该指针指向没有任何东西,而空字符串是一个“真正的”,零长度的字符串(即它只包含一个NUL字符)。
char * str=NULL; /* NULL pointer to string - there's no string, just a pointer */
const char * str2 = ""; /* Pointer to a constant empty string */

char str3[] = "random text to reach 15 characters ;)"; /* String allocated (presumably on the stack) that contains some text */
*str3 = 0; /* str3 is emptied by putting a NUL in first position */

2
请注意:str2(指向字符串字面量)未被修改,而str3在堆栈上分配(或者在其他地方分配,这取决于代码放置的位置),该字面量仅用于初始化。 - Matteo Italia
4
不对。 str3 是一个数组,不是指针!该文字串用于初始化,它被复制到缓冲区(即str3)中,编译器会自动调整数组大小以容纳该字符串。例如,在此处查看:http://msdn.microsoft.com/en-us/library/7w7xccx8%28VS.80%29.aspx。 - Matteo Italia
2
@EboMike:str3 的类型为 char[],一旦使用字符串字面值初始化为 char[38],它就变成了完整的类型。这种类型是可修改的左值。您没有修改字符串字面值,而是修改了一个具有与字符串字面值相同类型和内容的左值。 - dreamlax
1
@EboMike: §6.7.8:[8] 初始化程序指定对象中存储的初始值。[14] 字符类型的数组可以通过字符字符串字面量进行初始化,可选地用大括号括起来。字符字符串字面量的连续字符(包括终止空字符,如果有空间或数组大小未知)初始化数组的元素。[22] 如果初始化未知大小的数组,则其大小由具有显式初始化程序的最大索引元素确定。在其初始化程序列表的末尾,该数组不再具有不完整类型。 - Matteo Italia
2
啊...我应该仔细看看的! - EboMike
显示剩余4条评论

4

这是一个关于C语言变量的一般性问题,不仅限于char指针。

在声明变量时立即进行初始化被认为是最佳实践。例如:

char *str = NULL;

使用空值是一件好事。这样你就不会有未知值的变量。例如,如果你在代码中稍后执行以下操作:

if(str != NULL)
 doBar(str);

会发生什么?str处于未知状态(几乎肯定不是NULL)

请注意,静态变量将自动初始化为零/ NULL。从问题中不清楚您是否在询问局部变量还是静态变量。


1
哇,你是一个苛刻的听众;我想知道这个答案有什么问题(它和其他所有答案都一样)。 - pm100
也许你说的是“最佳实践”,而不是“好的实践”?我不知道,我不明白你所说的怎么能成为否决投票的理由。 - Jeff Mercado
这会把代码弄得杂乱无章,充斥着一堆毫无意义的指针值设置和检查。 - Edward Hartnett
@EdwardHartnett,不是这样的。我没有提倡任何检查。我主张在声明时养成初始化的习惯。 - pm100

2

全局变量由编译器初始化为默认值,但局部变量必须初始化。


1

这完全取决于你将如何使用它。在下面的情况下,不初始化变量更有意义:

int count;
while ((count = function()) > 0)
{
}

1

因为如果你传递给free()一个NULL值,它不会执行任何操作,所以你可以像这样简化你的程序:

char *str = NULL;

if ( somethingorother() )
{
    str = malloc ( 100 );

    if ( NULL == str )
        goto error;
}

...

error:

cleanup();
free ( str );

如果由于某种原因somethingorother()返回0,如果您没有初始化str,则可能会释放任意地址,从而导致失败。
对于使用goto,我很抱歉,我知道有些人觉得这很冒犯。 :)

1

未初始化的指针应被视为未定义,因此为避免使用未定义值而导致错误,最好始终使用

char *str = NULL;

同样也因为

char *str;

这只是一个未分配的指针,指向某个地方,如果您忘记分配它,使用时会导致大多数问题,您仍然需要分配它(或复制另一个指针)。

这意味着您可以选择:

  • 如果您知道在声明后不久将分配它,则可以避免将其设置为NULL(这是一种经验法则)
  • 在任何其他情况下,如果您想确保,只需这样做。唯一真正的问题是如果尝试在未初始化的情况下使用它。

1

不要在声明时将所有指针变量都初始化为NULL,以防万一。

如果您尝试使用未初始化的指针变量,编译器会发出警告,除非您通过地址将其传递给函数(通常这样做是为了给它赋值)。

将指针初始化为NULL并不等同于将其初始化为一个合理的值,将其初始化为NULL只会禁用编译器告诉您未将其初始化为合理值的能力。

仅在声明时将指针初始化为NULL,如果您不这样做会收到编译器警告,或者您正在通过地址将它们传递给期望它们为NULL的函数。

如果您无法在同一屏幕中看到指针变量的声明和第一次赋值的位置,则说明您的函数太大了。


编译器警告你。让我重新表述一下:“如果你要求编译器警告某些事情,它将会警告你。” - jvriesem

1
static const char str[] = "str";

或者

static char str[] = "str";

0

你的第一个片段是带初始化的变量定义;第二个片段是不带初始化的变量定义。

正确初始化字符串的方法是在定义时提供一个初始化器。将其初始化为NULL或其他内容取决于您想要做什么。

还要注意您所谓的“字符串”。在C语言中没有这样的类型:通常在C语言上下文中,“字符串”意味着“[某些数量的]字符数组”。在上面的代码片段中,您有指向char的指针。

假设您有一个程序,它想要在argv [1]中获取用户名并将其复制到字符串“name”中。当您定义name变量时,可以将其保持未初始化状态,或将其初始化为NULL(如果它是指向char的指针),或使用默认名称进行初始化。

int main(int argc, char **argv) {
    char name_uninit[100];
    char *name_ptr = NULL;
    char name_default[100] = "anonymous";

    if (argc > 1) {
        strcpy(name_uninit, argv[1]); /* beware buffer overflow */
        name_ptr = argv[1];
        strcpy(name_default, argv[1]); /* beware buffer overflow */
    }

    /* ... */

    /* name_uninit may be unusable (and untestable) if there were no command line parameters */
    /* name_ptr may be NULL, but you can test for NULL */
    /* name_default is a definite name */
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接