#include <stdio.h>
int main(void){
unsigned a[3][4] = {
{2,23,6,7},
{8,5,1,4},
{12,15,3,9}
};
printf("%u",*((int*)(((char*)a)+4)));
return 0;
}
我的机器中的输出是a[0][1]
的值,即 23 。有人可以解释这是如何工作的吗?
编辑:回滚到旧yucky
代码,完全呈现给我的是:P
答案 0 :(得分:13)
所以你把你的数组放在内存中:
2, 23, 6, 7, 8...
这样做是将数组转换为char*
,它允许您访问单个字节,并指向此处:
2, 23, 6, 7, 8...
^
然后添加四个字节,将其移动到下一个值(稍后将详细介绍)。
2, 23, 6, 7, 8...
^
然后它将其变为int*
并取消引用它,得到值23。
此代码在技术上有三个问题。
首先,它假设unsigned
的大小为4个字节。 (因此+ 4
)。但这不一定是真的!最好是+ sizeof(unsigned)
,无论大小unsigned
是什么大小,都要确保正确无误。
第二个问题是转化为int
:原始数组为unsigned
,但值正在转换为int
。 unsigned
范围内存在int
无法表示的值(因为int
范围的一半是负数。)因此,如果数组中的某个值无法表示作为int
(意味着值大于INT_MAX
),您将获得错误的值。更好的是转换为unsigned*
,以保持正确的类型。
最后一件事是格式说明符。整数的说明符是%d
,但代码使用%u
,它用于无符号整数。实际上,即使回退到int*
错误,printf
也会将 值转换回unsigned*
,从而恢复其完整性。通过解决问题二,问题三解决了问题。
有一个隐藏的第四个问题:代码很糟糕。这可能是出于学习目的,但 yuck 。
答案 1 :(得分:9)
数组:
unsigned a[3][4] = {
{2,23,6,7},
{8,5,1,4},
{12,15,3,9}
};
在内存中布局为(假设a
本身位于内存位置0x8000
,特定字节序和4字节int
):
0x8000 0 0 0 2
0x8004 0 0 0 23
0x8008 0 0 0 6
0x800C 0 0 0 7
0x8010 0 0 0 8
0x8014 0 0 0 5
0x8018 0 0 0 14
0x801C 0 0 0 12
0x8020 0 0 0 15
0x8024 0 0 0 3
0x8028 0 0 0 9
打破表达:
*((int*)(((char*)a)+4))
((char*)a)
为您提供char
指针。+4
将指针前进4个字节(4 * sizeof(char)
)(int*)
将其结果转换为int
指针。*
取消引用指针以提取int
。这是一种非常愚蠢的方式,因为它本质上是不可移植的(例如,int
为2或8个字节的环境)。
答案 2 :(得分:2)
它首先隐式地将数组a转换为指向其开头的指针。然后它将指针转换为char *并将值递增4.值4恰好与系统上的sizeof(unsigned)相同,所以实际上它已经从一开始就向前移动了一个元素。然后它将地址转换为int *并读取它指向的值(operator *)。这个结果值打印为无符号整数,因为int和unsigned的大小相同。
内存中静态2D数组的布局使得所有元素实际上按顺序存储为一维数组。
答案 3 :(得分:1)
unsigned int的大小为4.即sizeof(unsigned)== 4
它可以容纳4个字符,每个字符都是一个字节[在C中不在Java / C#等]。
数组在内存中连续分配。将无符号数组视为char *时,需要将指针移动4步以达到数组中的下一个无符号值。
答案 4 :(得分:1)
首先,创建一个大小为3x4的2-dim数组。
在((char*)a)
之后,您可以将其作为char数组使用。我们将其指定为b。
((char*)a)+4
与b[4]
相同,它指向char数组的5
- 元素(你记得,C中的aarays是基于0的)。或者只是第5个字节。
当您将数组转换回int时,如果i-th
,则int数组的i*4
元素从sizeof(int) = 4
字节开始。因此,在第5个字节处,int数组的第二个元素开始于指针指向的位置。编译器从第4个位置开始获取4个字节,并说它是int。那恰好是[0] [1]。