2013-06-26 53 views
2

有人能澄清我的错误解释吗?我知道我的理解是不正确的,因为我的代码的结果输出(请参阅问题的底部)。 在此先感谢。指针访问和数组访问之间的值差异

为了澄清,什么是以下行的每个段的意思是?:

*(u8 *)((u32)BufferAddress + (u32)i) 

它是如何从以下线路不同:

*(u32 *)((u32)BufferAddress + (u32)i) 

我上面的解释是:

  1. segment1 =((u32)BufferAddress +(u32)i)=>确定一个地址为一个整数。
  2. segment2 =(u32 *)(segment1)=>将指针处理为待处理地址,其中指针的长度为32位。
  3. segment3 = *(segment2)=>取消引用指针以获取驻留在计算出的地址的值。

我的解释是什么不正确?我认为我的缺乏理解在第二部分领域...铸造(u32 *)和(u8 *)有什么区别?

这里是让我意识到我有知识差距代码:

初始化代码:

main(...) { 
    ... 
    u8 *Buffer = malloc(256); 
    ... 
    Buffer[0] = 1; 
    Buffer[1] = 0; 
    Buffer[2] = 0; 
    Buffer[3] = 4; 
    Buffer[4] = 0; 
    Buffer[5] = 0; 
    qFunction(... , Buffer, 6, ...); 
    ... 
} 

qFunction(... , const u8 *BufferPointer, u32 BufferLength, ...) { 
    u32 BufferAddress; 
    ... 
    BufferAddress = (u32) BufferPointer; 
    ... 

    /* Method 1: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, %p\n", BufferPointer[i], &BufferPointer[i]); 

    /* Method 2: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, 0x%lx\n", *(u8 *)(BufferAddress+i), BufferAddress+i); 

    /* Method 3: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, 0x%lx\n", *(u32 *)(BufferAddress+i), BufferAddress+i); 
    ... 
} 

方法1和方法2的输出如我所料(两者都相同):

1, 0x1000000 
0, 0x1000001 
0, 0x1000002 
4, 0x1000003 
0, 0x1000004 
0, 0x1000005 

但是,方法3的输出对我来说似乎很奇怪;只有部分结果与方法1/2相同:

-1442840511, 0x1000000 
11141120, 0x1000001 
43520, 0x1000002 
4, 0x1000003 
0, 0x1000004 
0, 0x1000005 

我很感激任何提示或参考资料。 谢谢。

+1

macduff的回答是正确的,但是一个评论加上;你应该尽量避免将指针转换为整数,因为这是不必要的,如果将来决定构建64位(指针将是64位而不是32位),会导致问题。 –

回答

3

我可以挑剔,并说“你没有给我们足够的信息”。从技术上讲,这是事实,但只需要做出一些假设。u8u32不是标准的C类型,你可以让它们对任何东西都进行typedefed,但可能它们表示一个无符号的8位值(例如uchar)和一个无符号的32位值(例如unsigned)。假设,让我们看看你理解的那些,并解释第三个离开的地方。

BufferPointer是一个常量u8 *,这意味着它是一个类型为u8的常量指针。这意味着它指向的数组是8位无符号类型。

现在,BufferAddress是一个u32 - 这是指针的典型代表,至少在32位系统上。由于它们始终是总线的大小,在64位系统上,指针是64位。

因此,方法1打印数组的元素和数组的地址。这很好,很酷。

方法2:

*(U8 *)(BufferAddress + I),BufferAddress +我

BufferAddress是一个无符号整数,您要添加值,它得到了其他地址。这是数组的一个基本点 - 内存将是连续的,您可以通过推进每个元素的字节数来访问下一个元素。因为它是一个u8数组,所以你只需提前1.这里有一个问题 - 如果它是一个整数数组,那么你需要BufferAddress +(i * 4),而不是BufferAddress + i,因为每个int的大小是4字节。顺便说一下,这就是指针运算在C中的工作方式。如果你执行了'(&(((u32 *)BufferAddress)+ 1),你会得到0x100004而不是0x100001,因为你将BufferAddress转换为4字节的指针,编译器知道当你在看下一个元素时,它必须是4个字节。因此(BufferAddress+i)是u8数组的第i个元素的地址。 (u8 *)将BufferAddress从一个无聊的整数转换为指向u8类型的内存位置的指针,以便当您执行*(u8 *)时,编译器知道将它视为u8。你可以做(u64 *),编译器会说“哦!这个内存区域是64位”,并试图以这种方式解释这些值。

这可能会说明方法3中发生了什么。你得到每个数组元素的适当地址,但是你要告诉编译器“将这个区域的内存视为32位数据”。因此,每次使用*(u32 *)时,您都会读取数组的4个字节,并将其视为无符号整数。顺便说一句,一旦i> = 3,你正在碰到未定义的行为,因为你正在读取数组之外。

让我尝试给什么你在这方面的记忆看起来像一个可视化:

0x1000000 = 1 
0x1000001 = 0 
0x1000002 = 0 
0x1000003 = 4 
0x1000004 = 0 
0x1000005 = 0 

对于方法2,i = 2时,你看BufferAddress(= 0x1000000)+ 3,即0x1000002,其中编号为0。编译器知道它只有一个字节,所以随之而来。

但是对于method3,当i = 3时,您告诉编译器将其视为32位。所以它看不到'0',它看到0,4,0,0,并且使用这些数字来得出一个整数值,这肯定不会是4.

3
*(u8 *)((u32)BufferAddress + (u32)i) 
*(u32 *)((u32)BufferAddress + (u32)i) 

上方的行注塑指针到一个无符号的8比特值之前解除引用,而秒它转换为无符号的32位值解引用之前。最上面一行取消了一个字节,最下面一行取消了整个4个字节。

为了解决您的其他问题:

什么是不正确我的解释呢?我认为我对 的理解缺失在segment2区域...铸造(u32 *)和(u8 *)之间有什么区别 ?

地址长度为32位的解释对于顶部和底部的代码行都是正确的。