2010-01-24 35 views
3
#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]即 .Could有人值解释这是怎么工作?这个指针算法是如何工作的?

编辑:回滚到旧yucky代码,什么是专门给我:P

+0

是的,这是一个错字:| – 2010-01-24 08:29:16

+0

我在阅读GMan的文章后编辑了这段代码:) – 2010-01-24 08:35:54

+0

谢谢,但我不会这么做。 :)它混淆了人们,并使我们所给的答案无效。保持这个问题是如此的一切,所以一切都继续有意义,所以路人可以获得完整的价值。编辑:好得多。 :) – GManNickG 2010-01-24 08:37:42

回答

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,但值正在转换为intint范围内存在范围内的值(因为int范围的一半为负值)。因此,如果数组中的某个值不能表示为int(表示该值大于INT_MAX) ,你会得到错误的价值。最好是转换为unsigned*,以保持正确的类型。

最后一件事是格式说明符。整数说明符是%d,但代码使用%u,它是无符号整数。实际上,即使投回到int*是错误的,printf也会将转换成值,回到unsigned*,恢复它的完整性。通过解决问题二,问题三解决了问题。

还有一个隐藏第四个问题:代码糟透了。这可能是为了学习的目的,但yuck

+0

明白了!谢谢:) – 2010-01-24 08:27:43

+0

@nthgreek:指针算术:http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html – 2010-01-24 08:30:54

+0

@GMan:'有一个隐藏的第四个问题:代码很烂。这可能是为了学习的目的,但是糟糕。' - 同意:P – 2010-01-24 08:37:27

2

它首先隐式地将数组a转换为指向其开始的指针。然后它将指针转换为char *并将值递增4.值4恰好与系统上的sizeof(无符号)相同,所以实际上它已将一个元素从头开始向前移动。然后它将地址转换为int *并读取它指向的值(运算符*)。这个结果值被打印为无符号整数,这是因为int和unsigned是相同的大小。

静态二维数组在存储器中的布局使得所有元素实际上按照一维数组顺序存储。

1

无符号int是尺寸4.即的sizeof(无符号)== 4

它可以容纳4个字符,其中的每个是一个字节[用C未用Java/C#等]。

数组在内存中连续分配。当你将无符号数组视为char *时,你需要将指针移动4步以达到数组中的下一个无符号值。

+0

'sizeof(unsigned)== 4' *在这种情况下*,不一定在任何地方。这可能是你想说的。 – GManNickG 2010-01-24 08:34:45

1

首先,创建一个尺寸为3x4的2-dim数组。

((char*)a)之后你可以使用它作为char数组。我们将它指定为b。

((char*)a)+4b[4]相同,它指向char数组中的第012个元素(您记得,C中的aarays是从0开始的)。或者只是第5个字节。

当您将数组转换回int时,i-th int数组的元素从i*4字节开始,如果sizeof(int) = 4。因此,在第5个字节中,int数组的第二个元素开始位于指针指向的位置。编译器从第4个位置开始获取4个字节并表示它是int。这完全是[0] [1]。

9

阵列:

unsigned a[3][4] = { 
    {2,23,6,7}, 
    {8,5,1,4}, 
    {12,15,3,9} 
}; 

在存储器被布置为(假设a本身是在存储器位置0x8000,特定字节序和一个四字节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是两个或八个字节的环境)。

+1

+1仅仅是因为你的阵列内存布局看起来非常棒,与我的相比。 – GManNickG 2010-01-24 08:41:17

+0

好的解释+1! :) – 2010-01-24 08:42:16

+0

@ GMAN:是的,确实paxdiablo的'数组内存布局看起来很棒',但是我在阅读完这一行之后才得到解决方案'这个功能是将数组转换为char *,它允许您访问各个字节':) – 2010-01-24 08:46:36