【问题标题】:What does the 'array name' mean in case of array of char pointers?如果是 char 指针数组,“数组名称”是什么意思?
【发布时间】:2013-07-08 00:38:29
【问题描述】:

在我的代码中:

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

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

  1. 为什么会有差异?
  2. strstr[0] 都是字符指针,对吧?

【问题讨论】:

  • 不,完全不对。 str 是一个数组,str[0] 是一个数组元素。
  • 你确定你得到的值 12 和 2 吗?那将是一个不寻常的架构。顺便说一句,%d 是打印size_tsizeof(…) 的类型)的错误格式。这里的一种解决方案是确保不会对(int)sizeof(str)(int)sizeof(str[0]) 产生误解。
  • 你说你得到的输出是不现实的。不能是 12 和 2。
  • 无论你是什么架构,第一个值应该是第二个值的 4 倍。在 32 位机器上,您应该得到 16 4,在 64 位机器上应该得到 32 8。在一个非常旧的系统或嵌入式系统上,您甚至可能会得到8 2,但永远不会得到12 2,因为该数组包含 4 个相同大小的元素
  • @PascalCuoq 即使这样,这也不适用于这里:数组应该有n 的大小乘以char *,不管它是什么,n 是数组元素的数量。

标签: c pointers sizeof


【解决方案1】:

虽然问题已经得到回答和接受,但我添加了更多描述(也回答了原始问题),我想这对新用户会有帮助。 (正如我搜索的那样,此描述在其他任何地方都没有解释(至少在 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 的大小是一个字节,如果你在上面的程序中用int 替换char,那么每个输出将在你的机器上乘以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[] 存储在 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
    1234563 @ = 16 个字节。
  • str 的数据类型是 char*[4]

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

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

关于输出一也应该阅读glglglcomment到问题:

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

补充点:

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

还有一点需要注意:

  • 在我们上面的示例中,str[i] 指向无法修改的常量字符串;因此str[i][j] = 'A' 无效(我们不能在只读内存上写入),这样做会导致运行时错误。
    但是假设如果str[i] 指向一个简单的字符数组,那么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.

这种二维数组在内存中的排列方式称为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 字符数组的大小,包含 24 个字符(6 列* 4 行)。

  • 在这个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"是非法操作(感染会编译时错误)。

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

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

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

如果想阅读一维数组的类似描述:What does sizeof(&amp;array) return?

【讨论】:

  • ... 结果是整个数组的大小,而不是数组标识符所代表的指针的大小。 如果标识符代表一个数组,那么它不会t 代表任何指针。
  • 你的报价说“......而不是由数组表示的指针的大小”。数组名称不代表指针。它可以方便地衰减为指针,但这并不能使其成为指针。
  • 你真的不需要自己引用和解释。如果您想引用一个,请查看 C11 中的 6.5.3.4,其中声明 ...When applied to an operand that has array type, the result is the total number of bytes in the array 并且可能也是同一部分中 sizeof 的定义。
  • 这里有错字。地址将是(十进制值)343, 351, 359, 367 而不是343, 344, 345, 346。适用于 32 位架构
  • @noufal 谢谢纠正你注意到的错字,但我假设 4 字节 char* 根据我链接代码的代码键盘
【解决方案2】:

在大多数情况下,数组名会衰减到其第一个元素的地址值,并且类型与指向元素类型的指针相同。所以,你会期望一个裸的str 的值等于&amp;str[0],类型指针指向char

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

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

【讨论】:

  • 但这并不能解释12 2...不过+1
  • @glglgl 根据我的评论,“12 2”可能解释为 size_t 是 64 位并在以 %d 格式传递给 printf 时调用未定义的行为。跨度>
  • @PascalCuoq 可能是这种情况,但是我宁愿在某个地方期待0,无论是小端还是大端......但是人们永远不知道一个或另一个有什么奇怪的东西平台品牌。
  • 如果您的实现支持它,请使用"%zu" 打印size_t 值。如果没有(Microsoft 可能没有),请使用 "%lu" 并将值转换为 unsigned long。或者,如果您很懒惰并且已知值很小,请使用 "%d" 并转换为 int
  • 非常规值也可能是程序中没有包含printf的正确原型的结果,因此系统将可变参数宏应用于随机的东西。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    这两个指针不同。 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-21
      • 2021-07-31
      • 2021-03-22
      • 2022-01-08
      • 2023-03-11
      • 1970-01-01
      • 2010-10-01
      • 2018-09-04
      相关资源
      最近更新 更多