1

请解释第一个和最后一个printf语句的输出。我认为他们应该给出相同的输出。

int main()
{
   char arr[5][7][6];
   char (*p)[5][7][6] = &arr;

   printf("%d\n", (&arr + 1) - &arr);
   printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
   printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
   printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

   return 0;
}

输出:

1
210
42
210
4

4 回答 4

4

让我们先看看内存布局:

char arr[5][7][6];arr是一个数组数组。

它将被布置到内存中,如下所示:

+--------------+ 
|              | arr[0]
+--------------+
|              | arr[1]
+--------------+
|              | arr[2]
+--------------+
|              | arr[3]
+--------------+

etc.

现在arr[i]是一个数组的数组char

所以a[i]会像

+--------------+  a[i][0]
|              |
+--------------+
|              |  a[i][1]
+--------------+
|              |  a[i][2]
+--------------+
|              |  a[i][3]
+--------------+

etc

现在arr[i][j]是一个数组chars

所以

+--------------+ 
|              | arr[i][j][0]
+--------------+
|              | arr[i][j][1]
+--------------+ 
|              | arr[i][j][2]
+--------------+

etc.

编译器将在其符号表中使用名称arr和第一个块的地址创建一个条目,并跟踪总大小(5*6*7 = 210 字节)。

现在的表达

printf("%d\n", (&arr + 1) - &arr);

它是一个指针算法。所以它将绑定到每个符号的类型。

让我们看看这个在行动。

(gdb) p &arr
$1 = (char (*)[5][7][6]) 0x7fffffffe160

你看的类型arr是指针char (*)[5][6][7]

换句话说,它指向数组数组的数组。指针算术实际上与指针指向的类型有关。所以重要的是类型的大小。

(gdb) p sizeof(char [5][7][6])
$2 = 210

因此,任何指向该类型的指针char [5][6][7]都将递增或递减,如下所示:

&arr+1 => 0x7fffffffe160 +0xd2 => 0x7fffffffe232

(&arr + 1) - &arr => 0x7fffffffe232 - 0x7fffffffe160=> 0xd2

现在它实际上返回0xd2。但对于指针算术,这意味着1*sizeof(char [5][7][6]) => 1

指针算术返回number of sizeof(type)而不是实际的字节偏移量。

你得到正确的结果

printf("%d\n", (char *)(&arr + 1) - (char *)&arr);

由于强制转换,您将其设为 char 指针。所以指针运算将使用sizeof(char)1 字节大小的单位。因此输出将是 210。

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);

这里arr的表达式将衰减为类型char (*)[7][6]所以它是一个指向二维数组的指针。它指向的类型的大小为 6*7 = 42。这就是你得到的结果。

printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

在这里,p+1 - p将导致指针算术1 * sizeof(char(*)[5][6][7])。所以在指针算术中,它应该返回 1。但是由于您将结果转换为 unsigned ,它将放弃指针算术并使用整数算术。因为使用整数运算,它会处理大数的指针值,所以你会得到实际的字节偏移量。

于 2012-08-10T14:22:43.487 回答
2

如果它们被视为指针,它们将打印相同的结果,但最后printf你通过强制转换强制它们被视为无符号整数。因此,p + 1p被减去以下整数算术规则,而不是指针算术规则。

编辑

为了让事情更清楚一点:

  • 当您减去 2 个指针时,结果是它们之间的元素数。在您的情况下,它们之间有 1 个元素
  • 当您将指针投射到unsigned您告诉编译器“这些只是普通数字,这里什么都看不到”时。因此编译器将地址视为数字并减去它们。
于 2012-08-10T12:09:33.477 回答
0

先取地址arr加一,再减去地址,结果明明是1。

不过,最后一个p是指向 的指针char [5][7][7],并且 using(*p)[x][y][z]可以是 as p[0][x][y][z]。所以(p + 1)可以认为是p[1],距离 210( 5 * 7 * 6) 个字节p

如果你这样做了(((unsigned) p) + 1) - ((unsigned) p),那就是1

于 2012-08-10T12:13:39.057 回答
0

地址之间的距离是 1 个元素或 210 个字节

第一个打印打印元素的数量最后一个 - 字节数

于 2012-08-10T12:14:25.603 回答