24

在我的代码中:

 char *str[] = {"forgs", "do", "not", "die"};
 printf("%d %d", sizeof(str), sizeof(str[0]));  

我得到的输出为12 2,所以我的疑问是:

  1. 为什么有区别?
  2. str和都是str[0]char 指针,对吧?
4

4 回答 4

65

通过这个问题已经得到回答和接受,但我添加了一些我认为对新用户有帮助的描述(也回答了原始问题)。(正如我搜索的那样,此描述在其他任何地方都没有解释(至少在stackoverflow上),因此我现在添加。

初读:运算符 sizeof

6.5.3.4 sizeof 运算符,1125:
sizeof运算符应用于数组类型时,结果是数组中的总字节数。

根据 this when sizeof应用于静态数组标识符的名称(不是通过 malloc 分配的),结果是整个数组的大小(以字节为单位),而不仅仅是地址。这是数组名称被转换/衰减为指向数组第一个元素的指针的规则的少数例外之一,这可能只是因为实际数组大小在编译时是固定的并且已知的,当sizeof运算符评估。

为了更好地理解它,请考虑以下代码:

#include<stdio.h>
int main(){
 char a1[6],       // One dimensional
     a2[7][6],     // Two dimensional 
     a3[5][7][6];  // Three dimensional

 printf(" sizeof(a1)   : %lu \n", sizeof(a1));
 printf(" sizeof(a2)   : %lu \n", sizeof(a2));
 printf(" sizeof(a3)   : %lu \n", sizeof(a3));
 printf(" Char         : %lu \n", sizeof(char));
 printf(" Char[6]      : %lu \n", sizeof(char[6]));
 printf(" Char[5][7]   : %lu \n", sizeof(char[7][6]));
 printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6]));

 return 1;
} 

它的输出:

 sizeof(a1)   : 6 
 sizeof(a2)   : 42 
 sizeof(a3)   : 210 
 Char         : 1 
 Char[5]      : 6 
 Char[5][7]   : 42 
 Char[5][7][6]: 210 

检查上面在@codepad的工作,注意大小char是一个字节,你在上面的程序中替换它char然后int每个输出将sizeof(int)在你的机器上乘以。

char* str[]两者之间以及char str[][]如何存储在内存中的区别

声明一: char *str[] = {"forgs", "do", "not", "die"};

在这个声明str[]中是一个指向char的指针数组。每个索引都str[i]指向{"forgs", "do", "not", "die"};.
逻辑上str 应按以下方式排列在内存中:

Array Variable:                Constant Strings:
---------------                -----------------

         str:                       201   202   203   204  205   206
        +--------+                +-----+-----+-----+-----+-----+-----+
 343    |        |= *(str + 0)    | 'f' | 'o' | 'r' | 'g' | 's' | '\0'|
        | str[0] |-------|        +-----+-----+-----+-----+-----+-----+
        | 201    |       +-----------▲
        +--------+                  502   503  504
        |        |                +-----+-----+-----+
 347    | str[1] |= *(str + 1)    | 'd' | 'o' | '\0'|
        | 502    |-------|        +-----+-----+-----+
        +--------+       +-----------▲
        |        |                  43    44    45    46
 351    | 43     |                +-----+-----+-----+-----+
        | str[2] |= *(str + 2)    | 'n' | 'o' | 't' | '\0'|
        |        |-------|        +-----+-----+-----+-----+
        +--------+       +-----------▲
 355    |        |
        | 9002   |                 9002  9003   9004 9005
        | str[3] |                +-----+-----+-----+-----+
        |        |= *(str + 3)    | 'd' | 'i' | 'e' | '\0'|
        +--------+       |        +-----+-----+-----+-----+
                         +-----------▲


Diagram: shows that str[i] Points to first char of each constant string literal. 
Memory address values are assumption.

注意:str[]存储在 continue 内存分配中,每个字符串都存储在内存中的随机地址(而不是 continue 空间中)。

[回答]

根据Codepad以下代码:

int main(int argc, char **argv){
    char *str[] = {"forgs", "do", "not", "die"};
    printf("sizeof(str): %lu,  sizeof(str[0]): %lu\n", 
            sizeof(str), 
            sizeof(str[0])
    );  
    return 0;
}

输出:

sizeof(str): 16,  sizeof(str[0]): 4
  • 在此代码中是一个包含4 个str字符地址的数组,其中每个字符的大小为 4 个字节,因此根据上面的引用,数组的总大小为= 16 个字节。 char*4 * sizeof(char*)

  • 的数据类型strchar*[4]

  • str[0]只不过是指向 char 的指针,所以它是四个字节。的日期类型str[i]char*

(注意:在某些系统地址可以是 2 字节或 8 字节)

关于输出,还应该阅读glglgl对问题的评论

在任何架构上,第一个值应该是第二个值的 4 倍。在 32 位机器上,你应该得到 16 4,在 64 位机器上应该得到 32 8。在非常老的机器上或在嵌入式系统上,你甚至可能得到 8 2,但永远不会得到 12 2,因为数组包含 4 个元素相同大小

附加点:

  • 因为每个str[i] 指向一个 char*(和字符串)是可变的,str[i]可以分配一个新的字符串的地址,例如:str[i] = "yournewname";i = 0 to < 4.

还有一点需要注意:

  • 在我们上面的示例str[i]中,指向无法修改的常量字符串;因此str[i][j] = 'A'是无效的(我们不能在只读内存上写入),这样做将是一个运行时错误。
    但是假设 ifstr[i]指向一个简单的 char 数组 thenstr[i][j] = 'A'可以是一个有效的表达式。
    考虑以下代码:

      char a[] = "Hello"; // a[] is simple array
      char *str[] = {"forgs", "do", "not", "die"};
      //str[0][4] = 'A'; // is error because writing on read only memory
      str[0] = a;
      str[0][5] = 'A'; // is perfectly valid because str[0] 
                       // points to an array (that is not constant)
    

在这里检查工作代码:键盘

声明 2 char str[][6] = {"forgs", "do", "not", "die"};::

str是一个大小为 4 * 6 的二维字符数组(其中每一行的大小相等)。(请记住,在这里你必须在声明中str明确给出列值,但行是 4,因为字符串的数量是 4)
在内存str[][]中将如下图所示:

                    str
                    +---201---202---203---204---205---206--+
201                 | +-----+-----+-----+-----+-----+-----+|   
str[0] = *(str + 0)--►| 'f' | 'o' | 'r' | 'g' | 's' | '\0'||
207                 | +-----+-----+-----+-----+-----+-----+|
str[1] = *(str + 1)--►| 'd' | 'o' | '\0'| '\0'| '\0'| '\0'||
213                 | +-----+-----+-----+-----+-----+-----+|
str[2] = *(str + 2)--►| 'n' | 'o' | 't' | '\0'| '\0'| '\0'||
219                 | +-----+-----+-----+-----+-----+-----+|
str[3] = *(str + 3)--►| 'd' | 'i' | 'e' | '\0'| '\0'| '\0'||
                    | +-----+-----+-----+-----+-----+-----+|
                    +--------------------------------------+
  In Diagram:                                 
  str[i] = *(str + i) = points to a complete i-row of size = 6 chars. 
  str[i] is an array of 6 chars.

内存中二维数组的这种排列称为行主要:线性内存中的多维数组被组织成行一个接一个地存储。这是 C 编程语言使用的方法。

注意两个图中的差异。

  • 在第二种情况下,完整的二维字符数组被分配在连续内存中。
  • 对于任何i = 0 to 2,str[i]str[i + 1]value 相差 6 个字节(即等于一行的长度)。
  • 此图中的双边界线str表示完整的 6 * 4 = 24 个字符。

现在考虑您在问题中针对二维字符数组发布的类似代码,请查看Codepad

int main(int argc, char **argv){
    char str[][6] = {"forgs", "do", "not", "die"};
    printf("sizeof(str): %lu,  sizeof(str[0]): %lu\n", 
            sizeof(str), 
            sizeof(str[0])
    );
    return 0;
}

输出:

sizeof(str): 24,  sizeof(str[0]): 6

根据sizeof运算符对数组的处理,在应用二维数组大小时,应返回整个数组大小,即 24 字节。

  • 众所周知,sizeof运算符在应用数组名称时返回整个数组的大小。因此,sizeof(str)它返回 = 24,即完整的 2D 字符数组的大小,由 24 个字符(6 列 * 4 行)组成。

  • 在这个声明类型中strchar[4][6]

  • 更有趣的一点是str[i]表示一个数组 chats,它的类型是char[6]. 并且sizeof(str[0])是完整数组的大小 = 6(行长)。

附加点:

  • 在第二个声明str[i][j]中不是常量,它的内容可以改变,例如str[i][j] = 'A'是一个有效的操作。

  • str[i]是 char 类型的数组的名称char[6]是一个常量,分配给str[i]egstr[i] = "newstring"是非法操作(感染它将是编译时错误)。

两个声明之间的另一个重要区别:

Declaration-1 :char *str[] = {"forgs", "do", "not", "die"};中,类型&strchar*(*)[4]char 指针数组的地址。

Declaration-2 :char str[][6] = {"forgs", "do", "not", "die"};中,类型&strchar(*)[4][6]4 行 6 列的二维字符数组的地址。

如果想阅读一维数组的类似描述:返回什么sizeof(&array)

于 2013-07-15T18:38:08.873 回答
8

在大多数情况下,数组名称将衰减为其第一个元素的地址值,并且类型与指向元素类型的指针相同。因此,您会期望 barestr的值等于&str[0]指向指针的类型指针char

但是,情况并非如此sizeof。在这种情况下,数组名称保持其类型为sizeof,这将是 4 指针的数组char

的返回类型sizeof是 a size_t。如果您有 C99 编译器,则可以%zu在格式字符串中使用以打印sizeof.

于 2013-07-10T07:33:51.000 回答
6

16 4在我的电脑上,我可以解释一下:str是一个数组char*,所以 sizeof(str)==sizeof(char*)*4

我不知道你为什么会得到12 2

于 2013-07-10T07:31:36.180 回答
3

两个指针不一样。str是一个array of char pointers,在你的例子中是一个(char*[4]),并且str[0]是一个char pointer

第一个sizeof返回包含的四个 char 指针的大小,第二个返回char*.
在我的测试中,结果是:

sizeof(str[0]) = 4   // = sizeof(char*)

sizeof(str) = 16  
            = sizeof(str[0]) + sizeof(str[1]) + sizeof(str[2]) + sizeof(str[3])
            = 4 * sizeof(char*)  
            = 4 * 4
            = 16
于 2013-07-10T07:30:06.550 回答