char*str = {"foo",...}和char str [] [5] = {"foo",...}数组定义有什么区别?

Ash*_*gra 30 c arrays pointers arrayofarrays

案例1:我写的时候

char*str={"what","is","this"};
Run Code Online (Sandbox Code Playgroud)

然后str[i]="newstring";是有效的,而str[i][j]='j';无效.

案例2:我写的时候

char str[][5]={"what","is","this"};
Run Code Online (Sandbox Code Playgroud)

然后str[i]="newstring";无效,而str[i][j]='J';有效.

为什么会这样?我是一个初学者,在阅读其他答案后已经非常困惑.

Sou*_*osh 26

首先:一个建议:请阅读有关数组不是指针,反之亦然!

也就是说,为了启发这种特殊情况,

  • 在第一种情况下,

    char*str={"what","is","this"};
    
    Run Code Online (Sandbox Code Playgroud)

    不会做你认为它做的事情.这是一种约束违规,需要根据章节§6.7.9/ P2从任何符合要求的C实现进行诊断:

    初始化程序不应尝试为未初始化的实体中包含的对象提供值.

    如果你启用警告,你(至少)会看到

    警告:标量初始化程序中的多余元素

      char*str={"what","is","this"};
    
    Run Code Online (Sandbox Code Playgroud)

    但是,打开严格一致性的(ny)编译器应该拒绝编译代码.如果编译器选择编译并生成二进制文件,那么行为就不会影响C语言的定义范围,这取决于编译器的实现(因此可以有很大的不同).

    在这种情况下,编译器决定此语句在功能上仅与...相同 char*str= "what";

    所以,这str是一个指向a的指针char,指向一个字符串文字.您可以重新分配指针,

    str="newstring";  //this is valid
    
    Run Code Online (Sandbox Code Playgroud)

    但是,像一个声明

     str[i]="newstring";
    
    Run Code Online (Sandbox Code Playgroud)

    无效,因为这里,尝试转换指针类型并将其存储到char类型不兼容的类型中.在这种情况下,编译器应该抛出有关无效转换的警告.

    此后,声明如

    str[i][j]='J'; // compiler error
    
    Run Code Online (Sandbox Code Playgroud)

    在语法上是无效的,因为你在[]不是"指向完整对象类型的指针"的东西上使用Array下标运算符,就像

    str[i][j] = ...
          ^^^------------------- cannot use this
    ^^^^^^ --------------------- str[i] is of type 'char', 
                                 not a pointer to be used as the operand for [] operator.
    
    Run Code Online (Sandbox Code Playgroud)
  • 另一方面,在第二种情况下,

    str是一个数组数组.你可以改变单个数组元素,

     str[i][j]='J'; // change individual element, good to go.
    
    Run Code Online (Sandbox Code Playgroud)

    但你不能分配给一个数组.

     str[i]="newstring";  // nopes, array type is not an lvalue!!
    
    Run Code Online (Sandbox Code Playgroud)

  • 实际上`char*str = {"what","is","this"};`是一种约束违规,需要从任何符合C的实现中进行诊断.[N1570](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf)6.7.9p2:"没有初始化程序应尝试为未包含在内的对象提供值.实体正在初始化." (恕我直言,恕我直言,gcc默认将此视为非致命警告.) (4认同)

cma*_*ter 16

内存布局不同:

char* str[] = {"what", "is", "this"};

    str
+--------+      +-----+
| pointer| ---> |what0|
+--------+      +-----+   +---+
| pointer| -------------> |is0|
+--------+                +---+    +-----+
| pointer| ----------------------> |this0|
+--------+                         +-----+
Run Code Online (Sandbox Code Playgroud)

在此内存布局中,str是指向各个字符串的指针数组.通常,这些单独的字符串将驻留在静态存储中,尝试修改它们是错误的.在图中,我曾经0用来表示终止空字节.

char str[][5] = {"what", "is", "this"};

  str
+-----+
|what0|
+-----+
|is000|
+-----+
|this0|
+-----+
Run Code Online (Sandbox Code Playgroud)

在这种情况下,str是位于堆栈上的连续的2D字符数组.在初始化数组时,字符串被复制到该存储区中,并且用零字节填充各个字符串以使该数组具有规则形状.

这两种内存布局从根本上是不相容的.您不能将任何一个传递给期望指向另一个的函数.但是,访问单个字符串是兼容的.在编写时str[1],您将获得char*包含字节的内存区域的第一个字符is0,即C字符串.

在第一种情况下,很明显这个指针只是从内存加载.在第二种情况下,指针是通过数组指针衰减创建的:str[1]实际上表示一个恰好五个字节(is000)的数组,它几乎在几乎所有上下文中都会衰变为指向其第一个元素的指针.但是,我认为对数组指针衰减的完整解释超出了这个答案的范围.如果你好奇,谷歌数组指针会衰减.