将数组的地址转换为其他数据类型

Ale*_*lex 11 c arrays pointers

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;
}
Run Code Online (Sandbox Code Playgroud)

当我运行上面的代码时,我得到以下输出:

 1
 210 
 42
 210
Run Code Online (Sandbox Code Playgroud)

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

小智 6

好吧,如果我想拆分头发:首先,代码在整个printf()语句中调用整个地方的未定义行为.两个指针的区别在于类型ptrdiff_t,为此,正确的转换说明符%td不是%d.

剩下的只是猜测.假设您的系统是合理的,并且在数字上指针值&arr始终是相同的,无论它转换为何种类型.

现在,(&arr + 1) - &arr当然是1,根据指针算法的规则.(两个指针之间的实际差异是210 * sizeof(int)字节,但这不是学校数学而是指针算术,这就是为什么结果以大小为单位给出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 + 1和之间的大小差异arr是42个元素.

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

  • 答案写得很差,并没有解释所涉及的类型.然后回答是以这样的方式写成,以便做出真实的陈述并炫耀但不向不懂理解的人传授概念.另外,转换为`char*`并减去不是欺骗或滥用指针算术; 它完全由C标准定义. (3认同)

Eri*_*hil 5

(&arr + 1) - &arr:

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

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

&arr + 1再次指向7个7个数组的5个数组的下一个数组的指针char.当它转换为a时char *,结果是指向下一个数组的第一个字节的指针.类似地,(char *)&arr是指向第一个数组的第一个字节的指针.然后减去两个指针会产生元素之间的差异.由于这些指针是指针char,因此产生的差异为数量char.所以区别在于7个阵列中5个阵列的阵列中的字节数为6个char,即5•7•6 char或210 char.

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

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

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

p是一个指向由5个7个数组组成的5个数组的数组的指针char.然后p+1是指向下一个数组的指针.转换unsigned为如上所述的行为.减去产生差异的字节,所以它是5个7个数组的5个数组的数组的大小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.


Gri*_*han 5

注意&arr是完整的三维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]
Run Code Online (Sandbox Code Playgroud)

类型&arrchar(*)[5][7][6]char三维数组的地址[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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

 &arr  : 0xbf5dd7de
 &arr+1: 0xbf5dd8b0
Run Code Online (Sandbox Code Playgroud)

(&arr + 1) - (&arr)= 0xbf5dd8b0 - 0xbf5dd7de= 0xd2= 之间的差异210.

在你的第二个printf:

printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
Run Code Online (Sandbox Code Playgroud)

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

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

在你的第三个printf:

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
Run Code Online (Sandbox Code Playgroud)

您将类型转换为无符号值(但不是地址/指针类型).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;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

 arr  : 0xbf48367e
 arr+1: 0xbf4836a8
Run Code Online (Sandbox Code Playgroud)

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

最后的printf:

 printf("%d\n", (unsigned)(p + 1) - (unsigned)p);
Run Code Online (Sandbox Code Playgroud)

只需区分&arr+1&arr= 210(类似于第二个printf)因为p是指向3-D char数组(= &arr)的指针.而且你将它类型化为值类型(不是指针类型).

此外,(只是为了理解目的添加,我想读者会发现它有用),

让我们学习arr&arr使用sizeof运算符之间的另一个区别,这将有助于您更深入地理解概念.对于第一次阅读: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;
}
Run Code Online (Sandbox Code Playgroud)

它的输出:

Sizeof(&arr)  : 4 and value &arr: 0xbf4d9eda
Sizeof(arr)   : 210 and value arr : 0xbf4d9eda
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda
Run Code Online (Sandbox Code Playgroud)
  • 这里&arr只是一个地址,系统地址是四个字节,这是完整的三维字符数组的地址.

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

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

  • 通知sizeof(arr[0])给出 42= 7 * 6 * sizeof(char).并且这在概念上证明了三维阵列注意到了二维阵列的阵列.

因为上面在我的回答很多时候我写的是:"大小char[5][7][6]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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

 Char         : 1 
 Char[5]      : 6 
 Char[5][7]   : 42 
 Char[5][7][6]: 210 
Run Code Online (Sandbox Code Playgroud)