11
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

为什么不是1在每种情况下都有输出?

4

3 回答 3

6

好吧,如果我想分头:首先,代码在整个printf()语句中到处调用未定义的行为。两个指针的区别是 type ptrdiff_t,为此,正确的转换说明符是%td, not %d

其余的只是猜测。假设您的系统是合理的,并且指针值在数字&arr上始终相同,无论它转换为什么类型。

现在,(&arr + 1) - &arr当然是1,根据指针运算规则。(两个指针之间的实际区别是210 * sizeof(int)字节,但这不是学校数学而是指针算术,这就是为什么结果以 size 为单位给出的原因sizeof(T),其中T是指针的基本类型。)

然后(char *)(&arr + 1) - (char *)&arr将指针转换为char *,并且由于 的​​大小char为 1,这将以字节为单位打印差异你在这里有效地欺骗/滥用指针算术。

此外:printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr)减去两个类型的指针int (*)[7][6]。就这样arr腐烂了。当然,7 * 6 = 42,所以和之间的大小差arr + 1arr42 个元素。

p但是,它不是指向数组第一个元素的指针,而是指向数组本身的指针。它的类型正确地表示为int (*)[5][6][7]。现在,如果您使用该类型打印差异,但您不让编译器通过欺骗指针只是 来进行除法unsigned,那么您将得到5 * 6 * 7210。

于 2013-07-16T14:02:50.253 回答
5

注意&arr是完整的 3 维 char 数组的地址,而arr指向第一个元素是 2 维 char 数组。如下图所示:

 0xbf8ce2c6
+------------------+     ◄-- arr  =  0xbf8ce2c6  
|    0xbf8ce2f0    |  
|   +------------------+     ◄-- arr + 1 = 0xbf8ce2f0
|   |   0xbf8ce31a |   |
|   |   +------------------+      ◄-- arr + 2 = 0xbf8ce31a 
|   |   0xbf8ce344 |   |   |
|   |   |   +------------------+      ◄-- arr + 3 = 0xbf8ce344
|   |   0xbf8ce36e |   |   |   |
|   |   |   |  +------------------+      ◄-- arr + 4 = 0xbf8ce36e
|   |   |   |  |   |   |   |   |  |
+---|---|---|--|---+   |   |   |  |  Each are 7*6, 2-Dimensional 
    |   |   |  |       |   |   |  |  Consists Of 42 bytes 
    +---|---|--|-------+   |   |  |  
        |   |  |           |   |  |
        +---|--|-----------+   |  |
            |  |               |  |
            +--|---------------+  |
               |                  |
               +------------------+

 The diagram show: 
 1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays
 2. Here (arr + i) points to a 2-D array 
 3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4]

类型&arrchar(*)[5][7][6]char 3D-array 的地址[5][7][6]&arr和之间的价值差异&arr + 15 * 7 * 6 * sizeof(char)= 210
因为 的大小char[5][7][6]5 * 7 * 6 * sizeof(char)
在您的代码&arr中指向 3-D 数组和&arry + 1下一个 3-D 数组(在我们的代码中不存在)。

在codepade检查此工作代码:

int main()
{
    char arr[5][7][6];
    printf(" &arr  : %p", &arr);
    printf(" &arr+1: %p", &arr + 1);

    return 0;
}

输出:

 &arr  : 0xbf5dd7de
 &arr+1: 0xbf5dd8b0

(&arr + 1) - (&arr)= 0xbf5dd8b0 - 0xbf5dd7de= 0xd2=之间的区别210

在您的第二个 printf 中:

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

您将 type 的地址类型转换char(*)[5][7][6]为 plain (char*),并且因为 sizeofchar[5][7][6]210两个地址都是 210 远。(记住sizeof(char) == 1)。这就是输出的原因:210

现在正如我在第一个语句中所说,arr是第一个元素的地址,它是一个二维字符数组。类型arrchar(*)[7][6]。现在有一个元素(大小为 的二维数组6 * 7 * sizeof(char) = 42)。
(注意:您可以将 3-D 数组视为一维数组,其中每个元素都是一个二维数组)。

在您的第三个 printf 中:

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

您将类型转换为无符号值(但不是地址/指针类型)。arr + 1和之间的区别arr42 * sizeof(char)= 42(即等于 的大小char[7][6])。所以 printf 语句输出:42.

注意: 您应该阅读sizeof (int) == sizeof (void*)? ,因为您正在将地址类型转换为值。并且这种转换没有完全定义。(我的解释是你的输出和我给出的输出)。

如需进一步说明,请在codepade上查看以下工作代码:

int main()
{
    char arr[5][7][6];
    printf(" arr  : %p\n", arr);
    printf(" arr+1: %p", arr + 1);

    return 0;
}

输出是:

 arr  : 0xbf48367e
 arr+1: 0xbf4836a8

(arr + 1) - (arr)取= 0xbf4836a8- 0xbf48367e= 0x2a=之间的差异42

最后打印:

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

&arr+1只需取和&arr=之间的差异210(类似于第二个 printf),因为p它是指向 3-D 字符数组 (= &arr) 的指针。而且您正在将其类型转换为值类型(不是指针类型)。

另外,(只是为了理解目的而添加,我想读者会发现它有帮助),

arr让我们了解 sizeof 运算符和使用运算符之间的另一个区别,&arr这将有助于您更深入地理解概念。对于第一次阅读:sizeof操作员

sizeof运算符应用于数组标识符时,结果是整个数组的大小,而不是数组标识符所表示的指针的大小。

在codepade检查此工作代码:

int main()
{
    char arr[5][7][6];
    printf(" Sizeof(&arr)  : %lu and value &arr: %p\n", sizeof(&arr), &arr);
    printf(" Sizeof(arr)   : %lu and value arr : %p\n", sizeof(arr), arr);
    printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]);
    return 0;
}

它的输出:

Sizeof(&arr)  : 4 and value &arr: 0xbf4d9eda
Sizeof(arr)   : 210 and value arr : 0xbf4d9eda
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda
  • 这里&arr只是一个地址,在系统中地址是四字节的,这是完整的 3 维 char 数组的地址。

  • arr是 3 维数组的名称,sizeof运算符给出数组的总大小,即210 = 5 * 7 * 6 * sizeof(char).

正如我在图表中所示,arr指向二维数组的第一个元素。所以因为arr= (arr + 0)。现在使用*取消引用运算符 at(arr + 0)给出地址 so 的值*(arr + 0) = arr[0]

  • 注意sizeof(arr[0])给出 42= 7 * 6 * sizeof(char)。这在概念上证明了一个 3 维数组,但值得注意的是 2 维数组的数组。

因为上面在我的回答中很多时候我都写过:“size of char[5][7][6]is 5 * 7 * 6 * sizeof(char)。” 所以我在@codepade下面添加了一个有趣的代码:

int main(){
 printf(" Char         : %lu \n", sizeof(char));
 printf(" Char[5]      : %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;
}

输出:

 Char         : 1 
 Char[5]      : 6 
 Char[5][7]   : 42 
 Char[5][7][6]: 210 
于 2013-07-16T14:24:25.913 回答
5

(&arr + 1) - &arr

&arr是由 5 个数组组成的数组的地址,该数组由 7 个数组组成,由 6 个组成char。如果我们有一个由这些对象组成的数组而不是只有一个,则添加一个会产生下一个5 个数组的 7 个数组的 6 个数组的地址。char减去原始地址,&arr产生两个地址之间的差异。根据 C 标准,这种差异表示为两个地址之间的元素数量,其中元素类型是指向的对象的类型。由于该类型是由 5 个数组组成的数组,其中 7 个数组由 6 组成char,因此两个地址之间的距离是一个元素。换句话说,从&arrto的距离(&arr + 1)是67 阵列的 5 阵列的阵列char

(char *)(&arr + 1) - (char *)&arr

&arr + 1又是一个指针,指向下一个由 5 个数组组成的数组,由 7 个数组组成,由 6 个组成char。当它转换为 achar *时,结果是一个指向下一个数组的第一个字节的指针。同样,(char *)&arr是指向第一个数组的第一个字节的指针。然后减去两个指针会产生它们在元素中的差异。由于这些指针是指向 的指针char,因此差值产生为多个char。所以区别在于 5 个数组 7 个数组 6 的数组中的字节数char,即 5•7•6char或 210 char

(unsigned)(arr + 1) - (unsigned)arr

由于arr不与&(或sizeof其他例外情况)一起使用,它会自动从 5 个数组的 7 个数组的 6 个数组转换char为指向第一个元素的指针。因此,它是一个指向由 6 个 7 个数组组成的数组的指针char。然后arr + 1是指向下一个 7 数组的 6 数组的指针char。当此地址转换为unsigned时,在您使用的 C 实现中,结果实际上是对象的内存地址。(这很常见,但 C 标准不保证,并且当地址为 64 位但unsigned为 32 位时肯定会中断。​​)类似地,(unsigned)arr是第一个对象的地址。当地址被减去时,结果是它们之间的距离(以字节为单位)。所以区别在于 6 的 7 个数组中的字节数char,即 7•6 字节,即 42 字节。请注意在这种情况下的关键区别:&arr是指向由 5 个数组和 7 个 6 组成的数组的数组的指针char,但arr它是指向由 7 个 6 个数组组成的数组的指针char

(unsigned)(p + 1) - (unsigned)p

p是指向由 5 个数组组成的数组的指针,该数组由 7 个数组组成,由 6 个组成char。然后p+1是指向下一个数组所在位置的指针。转换unsigned为上述行为。减法产生它字节的差异,所以它是 5 个数组的数组的大小 7 个 6 的数组char,所以它又是 210 个字节。

作为旁白:

的类型(&arr + 1) - &arrptrdiff_t并且应该用 打印%td

的类型(char *)(&arr + 1) - (char *)&arrptrdiff_t并且应该用 打印%td

的类型(unsigned)(arr + 1) - (unsigned)arrunsigned int并且应该用 打印%u

的类型(unsigned)(p + 1) - (unsigned)punsigned int并且应该用 打印%u

于 2013-07-16T14:19:08.067 回答