【问题标题】:Why can you initialize a string pointer as a string literal, but not as an array?为什么可以将字符串指针初始化为字符串文字,而不是数组?
【发布时间】:2021-07-29 12:36:11
【问题描述】:

可以用字符串字面量初始化字符串

char word1[] = "abc";

或作为带有空终止符的 char 数组。

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

除了写word1[],word1也可以写成指针符号

char *word1 = "abc";

但是,当尝试使用指针表示法编写 word2 时

char *word2 = {'a', 'b', 'c', '\0'};

它向我显示了一堆警告,例如

警告:标量初始值设定项 char 中的多余元素 *word2 = {'a', 'b', 'c', '\0'};

当我运行程序时,我得到Segmentation fault (core dumped)

这是为什么呢?为什么你可以写char *word = "abc" 而不能写char *word = {'a', 'b', 'c', '\0'}

【问题讨论】:

  • 试试compound literalchar *word2 = (char[4]){'a', 'b', 'c', '\0'};
  • @pmg 非常感谢您的指点。复合文字对于我来说仍然有点太新奇了。我已经更新了我的答案。

标签: c string


【解决方案1】:

为什么可以将字符串指针初始化为字符串字面量,而不是数组?

因为{'a', 'b', 'c', '\0'} 不是数组;它是要放入正在初始化的事物中的值列表。

{'a', 'b', 'c', '\0'} 语法不代表 C 中的数组。人们看到它被用来初始化数组,但是,当以这种方式使用时,它只是一个值列表。它也可以用于初始化结构,因为它只是列出要放入正在初始化的事物中的值。它本身并不是一个数组。

char *word2 = {'a', 'b', 'c', '\0'}; 中,用值'a''b''c''\0' 初始化word2 是没有意义的。它只是一个指针,应该用一个值初始化。给出一个包含四个值的列表来初始化一件事是没有意义的。

char *word2 = "abc"; 中,"abc" 不是值列表。它是一个字符串文字。字符串字面量定义了一个用字符串字符填充的静态数组。然后字符串字面量会自动转换为指向其第一个元素的指针,而正是这个指针用于初始化word2

所以char *word2 = "abc"; 做了两件事:字符串字面量定义了一个数组,初始化设置word2 指向该数组的第一个元素。相比之下,在char *word2 = {'a', 'b', 'c', '\0'}; 中,没有定义数组;值列表只是一个值列表。

将此与数组初始化进行比较,在char word2[] = {'a', 'b', 'c', '\0'}; 中,数组使用值列表进行初始化,这很好。然而,在char word1[] = "abc"; 中,发生了一些特别的事情。 C 2018 6.7.9 14 说我们可以用字符串字面量初始化一个字符类型的数组,字符串中的字符会用来初始化数组的元素。

【讨论】:

    【解决方案2】:

    这没有根本原因——这只是语言最初定义的方式。

    数组初始化的基本语法是

    type array[] = {value, value, value};
    

    指针初始化的基本语法是

    type *pointer = value;
    

    但是我们有字符串文字。事实证明,在内心深处,编译器对字符串文字做了两件几乎完全不同的事情。

    如果你说

    char array[] = "string";
    

    编译器处理它就像你说的一样

    char array[] = { 's', 't', 'r', 'i', 'n', 'g', '\0' };
    

    如果你说

    char *p = "string";
    

    编译器做了一些完全不同的事情。它悄悄地为你创建一个数组,包含字符串,或多或少就像你写的一样

    char __hidden_unnamed_array[] = "string";
    char *p = __hidden_unnamed_array;
    

    但重点——你问题的答案——是编译器只为字符串文字做这个特殊的事情。至少在 C 的原始定义中,没有办法使用{value, value, value} 语法来创建一个隐藏的、未命名的数组,您可以用它来做其他事情。 {value, value, value} 语法仅被定义为用作显式声明数组的直接初始化程序。

    正如@pmg 在评论中提到的那样,新版本的 C 有一个新的语法,复合文字,它确实让你,基本上,“使用{value, value, value} 语法创建一个隐藏的、未命名的数组来做其他事情”。所以你实际上可以写

    char *word2 = (char[]){'a', 'b', 'c', '\0'};
    

    这很好用。它也适用于其他情况:例如,您可以说类似

    printf("%s\n", (char[]){'d', 'e', 'f', '\0'});
    

    回到你问的一个附带问题:你写的时候

    char *word2 = {'a', 'b', 'c', '\0'};
    

    编译器对自己说:“等一下,word2 是一回事,但初始化程序有四件事。所以我会扔掉三件事,并警告程序员我正在这样做。”然后它做了相当于

    char *word2 = {'a'};
    

    如果你后来尝试了类似的东西

    printf("%s", word2);
    

    printf 尝试访问地址 0x00000061 时发生崩溃。

    【讨论】:

      【解决方案3】:

      一般来说,初始化器的类型必须与被初始化的类型相匹配。

      这行得通:

      char *word1 = "abc";
      

      因为字符串常量的数组类型为char,并且这样的数组在表达式或初始化中使用时会衰减为char * 类型,因此它与声明的类型匹配。

      这行得通:

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

      因为char 的数组正在使用初始化字符列表进行初始化(技术上它们的类型为int,但被转换为char)。

      这给出了一个警告:

      char *word2 = {'a', 'b', 'c', '\0'};
      

      因为初始化列表被用来初始化一个不是数组或结构的类型。

      这没关系:

      char word1[] = "abc";
      

      因为C standard 专门允许使用字符串字面量初始化char 数组,如第 6.7.9p14 节所述:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-09-19
        • 2019-04-09
        • 1970-01-01
        • 2016-06-27
        • 1970-01-01
        • 2015-08-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多