【问题标题】:Is there literally any difference between String and Null-terminated character array in CC中的String和Null终止字符数组之间有什么区别吗
【发布时间】:2025-11-30 09:10:01
【问题描述】:

想问一下,字符串是在 c 中可以互换的以 null 结尾的字符数组。 喜欢

    char string3[] = "abc";
    char string4[4] = {'a','b','c','\0'};

    if(!strcmp(string3,string4)){
        printf("yes");
    }

即使是 strcmp 也给出了 Yes。所以,只有初始化的区别,还是有一些深层次的区别。

【问题讨论】:

    标签: arrays c string


    【解决方案1】:
    char string3[] = "abc";
    

    正好是缩写

    char string4[4] = {'a','b','c','\0'};
    

    没有区别。

    但是,请注意

    //  now it's a pointer
    //   |
    //   v
    char *string3b = "abc";
    

    char string4[4] = {'a','b','c','\0'};
    

    等效。每当您编写一个字符串并且不使用它初始化数组(指针不是数组)时,编译器都会为您创建一个数组。第一个基本相当于:

    static const char _magic_compiler_variable_for_abc_string[4] = {'a','b','c','\0'};
    char *string3b = (char*)_magic_compiler_variable_for_abc_string;
    

    所以字符串被分配到其他地方,string3b 只保存一个指向它的指针。因为字符串变量是const(即使指针不是 const),所以在这种情况下不允许编辑字符串。编译器可能会在您编写"abc" 的任何地方使用相同的_magic_compiler_variable_for_abc_string,或者它可能会创建单独的_magic_compiler_variable_for_abc_string

    【讨论】:

    • "在这种情况下,您不能编辑字符串。"更像是尝试编辑的UB。它可能有效,可能无效,可能...
    • @chux-ReinstateMonica 我想说,这正是“你不被允许”的意思!不允许修改字符串字面量,红灯时不允许过路口等等等等。
    • @SteveSummit 是的,它给出了分段错误
    【解决方案2】:
    char string3[] = "abc";
    char string4[] = {'a','b','c','\0'};
    

    主要区别在于字符串字面量初始值设定项由多字节字符序列组成,而数组初始值设定项列表由整数常量表达式序列组成,每个表达式均由多字节字符常量组成。如果任何字符不能由单个char 表示,则它们的内容会有所不同。 string3[] 将长于 4 个字节,string4[] 将恰好是 4 个字节长,但其中一些元素的值被截断。它不应该影响字符abc,它们是基本字符集的一部分,因此应该适合单个char

    例如,在我的系统上,C 使用 UTF-8 作为源和执行字符集1,下面的程序:

    #include <stdio.h>
    
    int main(void)
    {
        char string1[] = "αβγ";
        char string2[] = { 'α', 'β', 'γ', '\0' };
        printf("sizeof string1 = %zu, sizeof string2 = %zu\n",
            sizeof string1, sizeof string2);
        return 0;
    }
    

    编译(带有警告2)并产生输出:

    sizeof string1 = 7, sizeof string2 = 4
    

    来自 gcc 10 的警告是:

    foo.c: In function ‘main’:
    foo.c:6:21: warning: multi-character character constant [-Wmultichar]
        6 |  char string2[] = { 'α', 'β', 'γ', '\0' };
          |                     ^~~
    foo.c:6:21: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52913’ to ‘-79’ [-Woverflow]
    foo.c:6:27: warning: multi-character character constant [-Wmultichar]
        6 |  char string2[] = { 'α', 'β', 'γ', '\0' };
          |                          ^~~
    foo.c:6:27: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52914’ to ‘-78’ [-Woverflow]
    foo.c:6:33: warning: multi-character character constant [-Wmultichar]
        6 |  char string2[] = { 'α', 'β', 'γ', '\0' };
          |                               ^~~
    foo.c:6:33: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘52915’ to ‘-77’ [-Woverflow]
    

    如果假定执行字符集为 UTF-81,则字符串字面量初始化器 "αβγ" 可以通过将每个字符展开为其字节序列来更改为数组初始化器列表:

        char string5[] = { '\xce', '\xb1', '\xce', '\xb2', '\xce', '\xb3', '\0' };
    

    如果无论执行字符集如何,数组初始值设定项都需要是 UTF-8 序列,则使用显式 u8-prefixed 字符串字面量初始值设定项会更具可读性:

        char string6[] = u8"αβγ";
    

    string5[]string6[]的初始内容无论执行字符集如何都是相同的,并且当且仅当执行字符集是UTF-8时才会与string1[]的初始内容相同。


    1“字符集”我真正的意思是一个字符集加上一个编码,字符集是从指定的编码推断出来的。 IE。 “UTF-8”是指 UCS 字符集加上 UTF-8 传输编码。

    2 编译器在编译给定示例时不需要发出任何诊断消息,但有些人选择这样做。

    【讨论】:

    • 如果你想做多字符串,你会使用wchar_t而不是char。因此,如果string1string2 使用wchar_t,则没有区别。字符数组(宽与否)与 null-terminated 字符串文字(宽与否)之间没有区别。 UTF-8 与 UTF-16 是字符数组/字符串的编码。
    • @Brandon 但是 UTF-8 的全部意义在于您不必必须重写每一行代码才能使用wchar_t。而且,正如 Ian 的示例所示,在实践中,以 null 结尾的字符串文字与像 { 'α', 'β', 'γ', '\0' }; 这样的数组初始值设定项之间可能存在很大差异。
    • @Brandon 在某些系统上 (*ahem* MS Windows *ahem*) 甚至 wchar_t 也无法将每个 Unicode 字符编码为单个 wchar_t,因此同样适用。
    • @Brandon 例如 wchar_t wstring1[] = L"?";wchar_t wstring2[] = { L'?', L'\0' }; 在 MS Windows 上并不相同。
    • 我的意思是,如果您不忽略编译器警告和溢出警告,它们是完全相同的。字符串实际上是一个字符数组。如果wchar_t 不能容纳字符,则使用更宽的类型,例如&lt;uchar.h&gt; 中的char32_t。语言环境和编码与字符串是否为字符数组无关。例如,对于 Windows,当您的字符串文字无法保存时,请使用 char32_tU"αβγ",如:ideone.com/CS5zoG 现在它们是等价的。对于所有字符串文字和不忽略编译器警告的 char 数组,它们是相同的。