多维数组和指针数学

时间:2016-03-15 00:39:44

标签: c multidimensional-array pointer-arithmetic

鉴于此代码:

#include <stdio.h>
int main()
{
    char a[3][5] = {2, 7, 3, 9, 5,
                    1, 2, 3, 4, 5,
                    6, 7, 8, 9, 10};
    printf("sizeof(a)=%d, sizeof(&a)=%d, sizeof(a[0])=%d\n", sizeof(a),
           sizeof(&a), sizeof(a[0]));
    printf("a+1=%p, a[0]+1=%p, &a+1=%p, &a[0]+1=%p\n", a+1, a[0] + 1, &a+1,
           &a[0]+1);
}

如果a从0x1000开始,而char是1个字节,那么输出是什么?真正的问题是为什么这些数字被打印,尤其是最后一行。

我已经执行了程序并观察了输出。

sizeof(a)=15, sizeof(&a)=8, sizeof(a[0])=5 a+1=0x7ffc7d4dfc15, a[0]+1=0x7ffc7d4dfc11, &a+1=0x7ffc7d4dfc1f, &a[0]+1=0x7ffc7d4dfc15

这里的问题是为什么不同的数字。例如:为什么[0] +1和&amp; a + 1?

之间存在差异

3 个答案:

答案 0 :(得分:1)

嗯,错误的格式说明符会产生一些问题。正如Vlad指出的那样,%zu的结果使用sizeof很重要,因为在某些系统(64位?)上可能不是int。如果对printf使用多个参数,这些事情很重要,因为堆栈布局不是printf所期望的。即使使用错误的整数长度转换说明符,单个小值也可能在小端系统上打印正常,因为第一个参数的位置是已知的,而较低有效位的字节首先出现。

但是,你的评论中的一个问题可以很容易地回答:

  

为什么[0] +1和&amp; a + 1?

之间存在差异

原因是a[0]是一个5 int s的数组(矩阵是3个一维数组的序列,每个数组包含5个整数)。它衰减到指向int的指针。向该指针添加一个将其前进到下一个int,这可能是通过将4添加到其数值。

相比之下,&a不是数组或矩阵,而是已经是地址 - 整个矩阵的地址。数值与&(a[0])&(a[0][0])相同;但它的类型不同。它确实是指向整个矩阵作为单个对象的指针。 (这种情况很少发生,但完全明确且合法。)

矩阵&#39;大小是15个整数,也许是60个字节。在指针中添加一个指针可以将其提升为&#34;下一个矩阵&#34;,可能通过在数字上添加60到地址。

答案 1 :(得分:1)

以下是指针运算的简要教程,因为它对理解事物至关重要。建议您尝试一些事情。

  1. P+n,其中P是指针而n是某个整数值,导致n*sizeof(*P)字节超过P。结果的类型与P的类型相同。这是指针运算的最基本情况。
  2. A+n,其中A是一个数组,n是一个整数值,导致n+sizeof(A[0])字节超过A。结果的类型是指向A[0]类型的指针。或者,您可以说A+n&A[0]+n相同。这被称为&#34;阵列衰减&#34;因为表达式A&#34;衰变&#34;指向其第一个元素的指针(衰减到&A[0])。例如,如果A的类型为int[20][40],则它会衰减为int(*)[40]类型。
  3. 现在我们可以继续。

    • sizeof(a)=15
      sizeof(a)结果为15,因为有3 * 5 = 15个char类型的项目,sizeof(char)为1.简单地说,sizeof(array) == count*sizeof(type)。对于多维数组,您只需将计数相乘并创建一个大数,从而生成sizeof(a) == (3*5)*sizeof(char)sizeof(a) == (15)*1

    • sizeof(&a)=8
      &a的类型为char (*)[3][5],或者&#34;指向3个5个字符的数组&#34;。因为它是一个指针,它将是你机器上指针大小的任何东西。在这种情况下,它是8(这意味着你可能在64位平台上运行它。)

    • sizeof(a[0])=5
      a是包含5个字符的3个数组,因此a[0]获得第一个包含5个字符的数组。如前所述,sizeof(array) == count*sizeof(type)sizeof(a[0]) == 5*sizeof(char)或5。

    • a+1=0x7ffc7d4dfc15
      请记住,指针算法强制数组衰减到指向其第一个元素的指针。因此,将a重写为&a[0]。现在将1添加到该指针,这将向地址添加1*sizeof(a[0])或5。如果从打印结果中减去5,则显示a本身的地址:0x7ffc7d4dfc10。这对于本答复的其余部分非常重要。

    • a[0]+1=0x7ffc7d4dfc11
      如前所述,a0x7ffc7d4dfc10开始,a[0]获得第一个包含5个字符的数组。由于a[0]的类型为char[5]并且应用了指针运算,因此会发生数组衰减。基本上,您现在拥有&a[0][0](类型:char *)而不是a[0]。这简单易懂:0x7ffc7d4dfc10 + 1*sizeof(a[0][0])0x7ffc7d4dfc10 + 1*1 = 0x7ffc7d4dfc11

    • &a+1=0x7ffc7d4dfc1f
      同样,&检索指向a的指针。 a具有类型char[3][5](3个5个字符的数组),因此&a具有类型char(*)[3][5](指向3个5个字符数组的指针)。将1添加到此结果为0x7ffc7d4dfc10 + 1*sizeof(char[3][5]) = 0x7ffc7d4dfc10 + (3*5)*sizeof(char) = 0x7ffc7d4dfc10 + 15。十六进制的15是0x0f,所以0x7ffc7d4dfc10 + 0x0f = 0x7ffc7d4dfc1f

    • &a[0]+1=0x7ffc7d4dfc15
      a[0]的类型为char[5]&a[0]的类型为char(*)[5]。在此指针中添加1会导致添加1*sizeof(a[0]),即5,从而生成0x7ffc7d4dfc10 + 5 = 0x7ffc7d4dfc15

答案 2 :(得分:0)

好的,这是我的解释版本。理解多个数组中的地址的关键是一些规则:

规则1

如果p是指向数组某个元素的指针,则p ++将p递增以指向下一个元素.. (K&amp; R sec 5.4)和如果是表达式或子表达式是“T的数组”,对于某些T,则表达式的值是指向数组中第一个对象的指针,表达式的类型被更改为指向T的指针。(K&amp; ; R,sec A7.1)

总之,这些暗示如果有一个数组:

T array[arraysize];

对于某些类型T,则数组变为(有一些例外)

T *ptr;

和(array + 1)=(ptr + 1)= ptr_value + 1 x sizeof(T)。 如果这没有意义,假设有一个

char str[] = "hello!";

然后str + 1 = str_addr + 1 x sizeof(char)= str_addr + 1,它指向字母'e'。

规则2:

在C中,二维数组实际上是一维数组,其每个元素都是一个数组(K&amp; R,sec 5.7)

规则3:

当sizeof应用于数组时,结果是数组中的总字节数(K&amp; R,sec A7.4.8)。请注意,这是上述规则1中提到的例外情况之一。

规则4:

对于任何类型的数组:

T array[arraysize];

&amp;数组的类型为T(*)[size]。规则1告诉我们&amp; array + 1将是(array_addr + arraysize)。 (在K&amp; R中没有很好地解释,我认为这是理解这些问题的混乱的根本原因)。

现在让我们来看看原始问题。假设数组a []从内存位置A开始(在本例中为0x7ffc7d4dfc10)。

规则2告诉我们a []实际上是一个包含3个元素的单个数组,每个元素都是一个大小为5的数组。 假装有一个:

typedef char char5_t[5];

然后a []可以表示为:

char5_t a[3];

因此使用规则3,sizeof(a)= 3 x sizeof(char5_t)= 3 x 5 = 15.

sizeof(&amp; a)是指针的大小= 8(64位),因为我在64位机器上运行它。

sizeof(a [0])只是char5_t = 5的大小。您还可以应用规则3来获取sizeof(char [5])= 5.

要计算(a + 1),记住[]可以改写为char5_t a [3]。 规则1告诉我们a + 1 = A + 1 x sizeof(char5_t)= A + 5 = 0x7ffc7d4dfc15。

要计算[0] + 1,想象一下有一个    char5_t a0 = a [0]; 由于a0的实际类型是char [5],因此规则1告诉我们(a0 + 1)= A + 1 * sizeof(char)= 0x7ffc7d4dfc11。

&amp; a + 1由规则4提供,是A + 15 = 0x7ffc7d4dfc10 + 15 = 0x7ffc7d4dfc1f。

&amp; a [0]相当于指向char5_t的指针。使用规则1,&amp; a [0] + 1 = A + 1 * sizeof(char5_t)=

A + 5 = 0x7ffc7d4dfc15。