C中的字符串数组在内存中是怎么样的?

时间:2017-01-09 16:18:09

标签: c arrays

我试图弄清楚2d char数组在内存中的样子。 例如:

    char   c[][5]={"xa","ccc","bb","j","a","d"};

    printf("TEST: %u %u %u %u \n\n",c[0],*c[0],c[0]+1,*(c[0]+1));

输出:

TEST:3214246874 120 3214246875 97

c [0] = *(c + 0)是字符串" xa",并且等于3214246874,所以我猜c [0]是char数组的地址" xa& #34 ;. 当我把*放到c [0]时,我得到120,这是' x'在ascii。

所以我认为c数组中的第一个空格是char x的地址。 之后我用c [0] +1尝试了同样的东西,它打印了下一个地址,然后我把*和我得到了,97这是' a'在ascii。

所以我假设数组c看起来像这样:

c[0]                              c[1]
------------------------------------------------------------------
| pointer to x | pointer to a ||| pointer to c | pointer to c | etc ...
----------------------------------------------------------------------

但是我搜索了网页,但我没有找到任何证明我的假设。

4 个答案:

答案 0 :(得分:2)

c内部看起来像这样:

c[0]                   c[1]                  c[2]                  c[3]
|                      |                     |                     |
[0] [1] [2]  [3]  [4]  [0] [1] [2] [3]  [4]  [0] [1] [2]  [3]  [4] 
'x' 'a' '\0' '\0' '\0' 'c' 'c' 'c' '\0' '\0' 'b' 'b' '\0' '\0' '\0' ...

即。它是一长串的字符。它不存储任何指针或地址。

编译器知道每个部件的大小,因此当您编写例如c[2][1],它知道从偏移2 * 5 + 1 = 11(从c开头)获取它。

答案 1 :(得分:2)

这一行:

char c[][5] = {"xa", "ccc", "bb", "j", "a", "d"};

可以更明确地写成:

char c[6][5] = {"xa\0\0\0", "ccc\0\0", "bb\0\0\0", "j\0\0\0\0", "a\0\0\0\0", "d\0\0\0\0"};

c6个元素的数组,其中每个元素的类型为char[5]。每个"子阵列"取5个字节(char总是占用一个字节),它们彼此相邻放置。因此,c数组占用的总内存空间为30个字节。

答案 2 :(得分:2)

你正在混淆两个意义上的术语"字符串"因为它在C中使用。

最正确的说,C字符串是char的以空值终止的数组。您已声明了一个char数组数组,并使用以null结尾的char序列对其进行初始化。将其表征为"字符串数组"。

是完全合理的

然而,数组与指针 完全不同。数组的元素是其他数组,每个数组(在您的情况下)长五chars。这就是术语“#34; string"进来了.C阵列有点滑;如果计算数组类型的(子)表达式,它将计算指向第一个数组元素的指针。在字符串的情况下,这样的指针具有类型char *,因此通常将指向字符串的指针称为字符串本身。然而,这是一种俗语,如果你不认识这两种相关含义之间的区别,你就会陷入麻烦。

分解您的示例代码:

    char   c[][5]={"xa","ccc","bb","j","a","d"};

    printf("TEST: %u %u %u %u \n\n",c[0],*c[0],c[0]+1,*(c[0]+1));
  • 表达式c[0]指定一个包含五个char的数组。在函数调用表达式的上下文中进行求值时,它将成为指向数组第一个元素的指针。此值的类型为char *,这不是相应printf字段描述符%u的正确类型。未定义的行为结果。您可以通过将参数强制转换为void *并将字段描述符更改为%p来更正此问题。

  • 假设c[0]求值为指向第一个成员数组的第一个char的指针,则表达式*c[0]求值为指向{{1} }}。此值再次无法与相应的字段描述符匹配,该描述符应为char - 您应该期望' x'打印。或者,您可以转换值:%c。在这种情况下,你会期望' x'的数字代码。打印;这很可能是120.事实上,实际印刷的价值是你的程序未定义行为的具体表现的一个无关紧要的特征。

  • 再次假设(unsigned int)*c[0]求值为指向第一个成员数组的第一个c[0]的指针,那么char指针另外,导致指向该数组中第二个c[0] + 1的指针。与char一样,这与格式不符。

  • 并且可能在这一点上很明显c[0]评估数组*(c[0] + 1)中的第二个char(在索引1处)。表达式严格等同于c[0]。这再次与格式不符。

  

所以我假设数组c看起来像这个[...]

不。该数组如下所示:

c[0][1]

答案 3 :(得分:1)

注意:指针和2D数组的数组是不同的动物!一旦定义,您几乎以相同的方式使用它们,但它们以不同的方式存储在内存中。

  1. 指针数组:

    char   *c[]={"xa","ccc","bb","j","a","d"};
    

    这定义了一个包含6个指针的数组。每个指针都指向其字符串,该字符串将存储在内存中的其他位置。典型的表示形式是:

    c -> address_of_x, address_of_c, address_of_ ... (array of pointers)
    'x', 'a', '\0', 'c', 'c', 'c', '\0', 'b'... (arrays of chars)
     -               -                    -
    

    整个事情将使用(在32位架构中):6 * 4 + 3 + 4 + 3 + 2 + 2 + 2 = 40字节

  2. 2D数组:

    char   c[][5]={"xa","ccc","bb","j","a","d"};
    

    这定义了一个由6行组成的2D数组,每行5列(正好是30个字节):

    'x', 'a', '\0', ?, ?, 'c', 'c', 'c', '\0', ?, 'b' ...
    

    (字节标记为?不关心,根据实现和构建选项,它们可能会被初始化或不被初始化。)

  3. 但无论您使用何种定义,c[1][2]都将是第二个字符串的第三个字符,*(c[0] + 1)(根据定义 c[0][1]相同)是第一个字符串的第二个字符,即:x