在数组char指针的情况下,'数组名称'是什么意思?

时间:2013-07-10 07:19:21

标签: c pointers sizeof

在我的代码中:

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

我的输出为12 2,所以我的怀疑是:

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

4 个答案:

答案 0 :(得分:61)

通过问题已经回答并被接受,但我正在添加更多描述(也回答原始问题),我认为这对新用户有帮助。 (当我搜索时,这个描述在其他任何地方都没有解释(至少在stackoverflow上)因此我现在正在添加。

首先阅读: sizeof Operator

  

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

根据这一点,当sizeof应用于静态数组标识符的名称(不是通过malloc分配)时,结果是整个数组的大小(以字节为单位)而不是地址。这是few exceptions to the rule that the name of an array is converted/decay to a pointer to the first element of the array之一,它可能只是因为实际的数组大小是固定的,并且在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[][]之间的差异以及两者如何存储在内存中

声明-1: 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[]存储在继续内存分配中,每个字符串都以随机地址存储在内存中(不在继续空间中)。

<强> [ANSWER]

根据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
  • 在此代码中,str 4 char-addresses的数组,其中每个char*的大小为4字节,因此根据上面的引用总大小数组是4 * sizeof(char*) = 16个字节。

  • str的数据类型为char*[4]

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

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

关于输出,我们还应该阅读glglgl comment问题:

  

无论你是什么架构,第一个值应该是第二个值的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'无效(我们无法在只读内存上写入),这样做会导致运行时错误。
    但假设str[i]指向一个简单的char数组,那么str[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)
    

点击此处的工作代码:Codepad

声明-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.

内存中2D阵列的这种排列称为Row-Major: 组织线性存储器中的多维阵列,使得行一个接一个地存储。它是C编程语言使用的方法。

注意两个图中的差异。

  • 在第二种情况下,在继续存储器中分配完整的二维字符数组。
  • 对于任何i = 0 to 2str[i]str[i + 1]值相差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运算符对数组的处理,应用2-d数组大小应返回整个数组大小为24字节。

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

  • 在此声明中,str的类型为char[4][6]

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

补充要点:

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

  • str[i]char[6]类型的char数组的名称,是str[i]的常量和赋值,例如str[i] = "newstring"是非法操作(感染它将是编译时错误)。

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

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

声明-2 char str[][6] = {"forgs", "do", "not", "die"};中,&str的类型为char(*)[4][6],其地址为4行和6列的2-D字符数组。

如果想要读取1-D阵列的类似描述:What does sizeof(&array) return?

答案 1 :(得分:7)

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

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

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

答案 2 :(得分:6)

我的计算机上有16 4,我可以解释一下:str是一个char*的数组,所以sizeof(str)==sizeof(char*)*4

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

答案 3 :(得分:3)

这两个指针是不同的。 strarray of char pointers,在您的示例中是char*[4]str[0]char pointer

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

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