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

我见过人们的代码:

char *str = NULL; 

而且我也看到了这一点,

 char *str; 

我想知道,初始化字符串的正确方法是什么? 什么时候你应该初始化一个字符串w /和w / out NULL?

你应该在使用它之前设置它。 这是您必须遵循的唯一规则,以避免未定义的行为。 无论是在创建时初始化它还是在使用它之前分配它都是不相关的。

就个人而言,我更喜欢永远不会将变量设置为未知值,所以我通常会做第一个,除非它设置得非常接近(在几行内)。

实际上,使用C99,你不必再在块的顶部声明局部,我通常会推迟创建它直到它需要,此时它也可以初始化。

请注意,在某些情况下,变量会被赋予默认值(例如,如果它们是静态存储持续时间,例如在文件级别,在任何函数之外声明)。

局部变量没有这种保证。 因此,如果上面的第二个声明( char *str; )在函数内部,它可能有垃圾并试图使用它将调用前面提到的,可怕的,未定义的行为。

C99标准6.7.8/10的相关部分:

如果未显式初始化具有自动存储持续时间的对象,则其值不确定。 如果未显式初始化具有静态存储持续时间的对象,则:

  • 如果它有指针类型,则将其初始化为空指针;
  • 如果它有算术类型,则初始化为(正或无符号)零;
  • 如果是聚合,则根据这些规则初始化(递归)每个成员;
  • 如果它是一个联合,则根据这些规则初始化(递归)第一个命名成员。

这是关于c变量的一般性问题,而不仅仅是char ptrs。

在声明点初始化变量被认为是最佳实践。 即

 char *str = NULL; 

是一件好事。 这种方式你永远不会有未知值的变量。 例如,如果您在代码中稍后使用

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

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

请注意,静态变量将初始化为零/ NULL。 如果您询问当地人或静力学,问题就不清楚了

我想知道,初始化字符串的正确方法是什么?

好吧,既然第二个片段定义了一个未初始化的字符串指针,我会说第一个。 🙂

一般来说,如果你想安全地玩它,最好将所有指针初始化为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 */ 

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

单位指针应该被认为是未定义的,所以为了避免使用未定义的值产生错误,使用它总是更好

 char *str = NULL; 

也因为

 char *str; 

这将只是一个未分配的指针指向某个地方,如果你忘记分配它将导致问题,你将需要分配它(或复制另一个指针)。

这意味着您可以选择:

  • 如果你知道你将在声明后立即分配它,你可以避免将其设置为 NULL (这是一种规则)
  • 在任何其他情况下,如果你想确定,就这样做。 如果您尝试在未初始化的情况下使用它,则会出现唯一的实际问题。

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

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

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

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

初始化指向NULL的指针与将其初始化为合理值并不相同,并将其初始化为NULL只会禁用编译器告诉您尚未将其初始化为合理值的能力。

如果您没有获得编译器警告,则仅在声明时初始化指向NULL的指针,或者您通过地址将它们传递给期望它们为NULL的函数。

如果你看不到指针变量的声明和它首先在同一个屏幕上给出一个值的点,那么你的函数太大了。

 static const char str[] = "str"; 

要么

 static char str[] = "str"; 

因为free()不会做任何事情,如果你传递一个NULL值,你可以像这样简化你的程序:

 char *str = NULL; if ( somethingorother() ) { str = malloc ( 100 ); if ( NULL == str ) goto error; } 

 error: cleanup(); free ( str ); 

如果由于某种原因,thingorother()返回0,如果你没有初始化str,你将释放一些可能导致失败的随机地址。

我为goto的使用道歉,我知道有些人觉得它很冒犯。 🙂

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

初始化字符串的正确方法是在定义字符串时提供初始化程序。 将其初始化为NULL或其他内容取决于您要对其执行的操作。

还要注意你所谓的“字符串”。 C没有这样的类型: C语境中的“string”通常是“[some number of char]的数组”。 您在上面的代码段中有指向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 */ } 

适当的你意味着没有bug? 好吧,这取决于具体情况。 但是我可以推荐一些经验法则。

首先,请注意C中的字符串与其他语言中的字符串不同。

它们是指向一个字符块的指针。 其末尾以0字节或NULL终止符结束。 因此null终止字符串。

例如,如果你要做这样的事情:

 char* str; gets(str); 

或以任何方式与str交互,那么这是一个巨大的bug。 原因是因为正如我刚才所说,在C字符串中不是像其他语言一样的字符串。 它们只是一个指针。 char * str是指针的大小,并且始终是。

因此,您需要做的是分配一些内存来保存字符串。

 /* this allocates 100 characters for a string (including the null), remember to free it with free() */ char* str = (char*)malloc(100); str[0] = 0; /* so does this, automatically freed when it goes out of scope */ char str[100] = ""; 

但是,有时您只需要一个指针。
例如

 /* This declares the string (not intialized) */ char* str; /* use the string from earlier and assign the allocated/copied buffer to our variable */ str = strdup(other_string); 

通常,它实际上取决于您期望使用字符串指针的方式。 我的建议是使用固定大小的数组forms,如果你只是在该函数的范围内使用它并且字符串相对较小。 或者将其初始化为NULL。 然后,您可以显式测试NULL字符串,这在传递给函数时很有用。

请注意,如果您使用的函数只是检查字符串结尾的位置,那么使用数组表单也可能会出现问题。 例如strcpy或strcat函数并不关心缓冲区有多大。 因此,请考虑使用像BSD的strlcpy和strlcat这样的替代方案。 或strcpy_s&strcat_s(windows)。

许多function都希望您传入适当的地址。 所以,请再次注意

 char* str = NULL; strcmp(str, "Hello World"); 

因为strcmp不喜欢传入NULL,所以会崩溃。

您已将其标记为C,但如果有人使用C ++并读取此问题,请切换到尽可能使用std :: string并在字符串上使用.c_str()成员函数,您需要与需要与之交互的API进行交互标准的null终止的c字符串。