C字符数组初始化:如果字符串文字中的字符数少于数组大小会发生什么?

152

以下是三种初始化char数组的方式,我不确定初始化后char数组中会包含什么内容:

1. char buf[10] = "";
2. char buf[10] = " ";
3. char buf[10] = "a";

对于第二种情况,我认为buf[0]应该是' 'buf[1]应该是'\0',而从buf[2]buf[9]将是随机内容。对于第三种情况,我认为buf[0]应该是'a'buf[1]应该是'\0',而从buf[2]buf[9]将是随机内容。

以上是否正确?

而对于第一种情况,buf中会包含什么?buf[0] == '\0',而从buf[1]buf[9]将是随机内容。


2
我的编译器不接受你(已经更正的)代码:“数组类型'char [10]'不可分配”。 - Martin R
@MartinR 现在它会工作了... - lkkeepmoving
1
@lkkeepmoving: char buf[10]; buf = "a"; 这段代码无法编译。- 请先尝试一下,然后将实际代码复制/粘贴到问题中。这样可以为您和所有读者节省很多工作。 - Martin R
@MartinR 很抱歉,我以为可以稍后分配buf[],但似乎不行。现在代码可以运行了。 - lkkeepmoving
2
可能是字符串字面值是否算作部分初始化和零初始化?的重复问题。 - Antti Haapala -- Слава Україні
显示剩余3条评论
6个回答

270
第一个声明: ``` char buf[10] = ""; ``` 相当于 ``` char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ```
第二个声明: ``` char buf[10] = " "; ``` 相当于 ``` char buf[10] = {' ', 0, 0, 0, 0, 0, 0, 0, 0, 0}; ```
第三个声明: ``` char buf[10] = "a"; ``` 相当于 ``` char buf[10] = {'a', 0, 0, 0, 0, 0, 0, 0, 0, 0}; ```

正如你所看到的,没有随机内容:如果初始化器较少,则数组的其余部分将被初始化为0。即使数组在函数内部声明,也是如此。


56
为了问问题的人,值得指出C标准要求任何部分完成的数组初始化都要由编译器用零填充剩余元素。这适用于所有数据类型,不仅仅是“char”。 - paddy
8
为什么buf[]结尾没有'\0'呢? - lkkeepmoving
22
@lkkeepmoving 的意思是 0'\0' 具有相同的值。 - ouah
3
char buff[3] = "abcdefghijkl"; 是无效的。char p3[5] = "String"; 也是无效的。char p[6] = "String"; 是有效的,它与 char p[6] = {'S', 't', 'r', 'i', 'n', 'g'}; 相同。 - ouah
2
这不是数组的正确初始化方式,你是什么意思? - Tiina
显示剩余12条评论

42
  1. 这两者是等价的

char buf[10] = "";
char buf[10] = {0};
char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  • 它们是等价的

  • char buf[10] = " ";
    char buf[10] = {' '};
    char buf[10] = {' ', 0, 0, 0, 0, 0, 0, 0, 0, 0};
    
  • 它们是等价的

  • char buf[10] = "a";
    char buf[10] = {'a'};
    char buf[10] = {'a', 0, 0, 0, 0, 0, 0, 0, 0, 0};
    

    31

    编辑:原帖作者(或编辑)在我回答后的某个时刻将原问题中的一些单引号静默地更改为双引号。

    你的代码将导致编译错误。你的第一个代码片段:

    char buf[10] ; buf = ''
    

    这是双重非法的。首先,在C语言中,不存在空的char类型。你可以使用双引号表示一个空字符串,例如:

    char* buf = ""; 
    

    这将为您提供指向 NUL 字符串的指针,即仅包含 NUL 字符的单字符字符串。但是你不能使用空的单引号--这是未定义的。如果您需要指定 NUL 字符,您必须显式指定它:

    char buf = '\0';
    

    反斜杠是必要的,以消除与字符 '0' 的歧义。

    char buf = 0;
    

    完成相同的任务,但我认为前者更容易阅读,少了一点歧义。

    其次,在定义后你无法初始化数组。

    char buf[10];
    

    声明并定义了数组。数组标识符buf现在是内存中的一个地址,您不能通过赋值更改buf指向的位置。


    buf =     // anything on RHS
    

    是非法的。由于这个原因,你的第二和第三段代码片段也是非法的。

    要初始化一个数组,你必须在定义时进行:

    char buf [10] = ' ';
    

    将会给你一个长度为10的数组,第一个字符是空格 '\040',其余字符都是NUL,即'\0'。当一个数组被声明和定义并使用初始化器时,在指定初始值的元素之后的数组元素(如果有的话)将自动填充0,不会有任何“随机内容”。

    如果你只声明和定义了数组,但没有对其进行初始化,就像下面这样:

    char buf [10];
    

    你将在所有元素中拥有随机内容。


    要初始化一个数组,你必须在定义时进行...这一点和下面的一行使得它比被接受的答案更好。 - Laurie Stearn

    14
    C11标准草案n1570 6.7.9初始化的相关部分指出:

     

    14字符类型的数组可以用字符字符串字面量或UTF-8字符串字面量进行初始化,也可以用括号括起来(可选)。字符串文字的连续字节(包括终止的空字符,如果有空间或者数组大小未知)初始化数组元素。

     

    21如果花括号括起来的列表中的初始化程序比聚合体的元素或成员少,或者用于初始化已知大小的数组的字符串文字中的字符比数组中的元素少,则聚合体的其余部分应隐式初始化与具有 静态存储持续时间的对象 初始化相同。

    因此,如果有足够的空间,则会添加'\0',并使用在函数内初始化为static char c;的值初始化剩余的字符。

    最后,

     

    10如果没有明确初始化具有自动存储期的对象,则其值是不确定的。如果没有明确初始化具有静态或线程存储期的对象,则:

     

    [--]

     
       
    • 如果它具有算术类型,则初始化为(正或无符号)零;
    •  
     

    [--]

    因此,作为算术类型的char也保证使用零填充数组的其余部分。


    3

    有趣的是,在程序中的任何时间,只要它们是structunion的成员,就可以以任何方式初始化数组。

    示例程序:

    #include <stdio.h>
    
    struct ccont
    {
      char array[32];
    };
    
    struct icont
    {
      int array[32];
    };
    
    int main()
    {
      int  cnt;
      char carray[32] = { 'A', 66, 6*11+1 };    // 'A', 'B', 'C', '\0', '\0', ...
      int  iarray[32] = { 67, 42, 25 };
    
      struct ccont cc = { 0 };
      struct icont ic = { 0 };
    
      /*  these don't work
      carray = { [0]=1 };           // expected expression before '{' token
      carray = { [0 ... 31]=1 };    // (likewise)
      carray = (char[32]){ [0]=3 }; // incompatible types when assigning to type 'char[32]' from type 'char *'
      iarray = (int[32]){ 1 };      // (likewise, but s/char/int/g)
      */
    
      // but these perfectly work...
      cc = (struct ccont){ .array='a' };        // 'a', '\0', '\0', '\0', ...
      // the following is a gcc extension, 
      cc = (struct ccont){ .array={ [0 ... 2]='a' } };  // 'a', 'a', 'a', '\0', '\0', ...
      ic = (struct icont){ .array={ 42,67 } };      // 42, 67, 0, 0, 0, ...
      // index ranges can overlap, the latter override the former
      // (no compiler warning with -Wall -Wextra)
      ic = (struct icont){ .array={ [0 ... 1]=42, [1 ... 2]=67 } }; // 42, 67, 67, 0, 0, ...
    
      for (cnt=0; cnt<5; cnt++)
        printf("%2d %c %2d %c\n",iarray[cnt], carray[cnt],ic.array[cnt],cc.array[cnt]);
    
      return 0;
    }
    

    1

    我不确定,但通常我会将数组初始化为"",这样我就不需要担心字符串的末尾为空。

    main() {
        void something(char[]);
        char s[100] = "";
    
        something(s);
        printf("%s", s);
    }
    
    void something(char s[]) {
        // ... do something, pass the output to s
        // no need to add s[i] = '\0'; because all unused slot is already set to '\0'
    }
    

    你不应该使用隐式int规则。你应该为main()指定一个类型(你也应该使用void,即int main(void) { ... })。C99取消了这个规则,所以这段代码在C99及以后的版本中将无法编译。另一件需要注意的事情是,从C99开始,如果在main函数中省略了return语句,在main函数结尾的}之前会自动添加/暗示return 0;语句。你正在使用只在C99及以后版本中有效的隐式return,但同时你又在使用只在C99之前版本中有效的隐式int规则,这两者显然是矛盾的。 - RastaJedi

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