预处理器出现奇怪的错误

时间:2017-06-25 10:11:33

标签: c++ c

目前我对预处理器有一个非常奇怪的错误。我搜索过,找不到任何相关内容,因此想在此发布。 以下是我的示例代码

#include <stdio.h>

#define DDR_BASEADDR  0x000000000U + 128*1024*1024

int main()
{
     uint32_t* test2 = (uint32_t*) DDR_BASEADDR;

     uint32_t whatIsThis = DDR_BASEADDR;

     uint32_t* test3 = (uint32_t*) whatIsThis;

     printf( "%x %x %x %x\n\r", DDR_BASEADDR, test2, test3, whatIsThis);

     return 0;
}

此代码的输出应全部为0x8000000。 但是,输出为:8000000 20000000 8000000 8000000

我认为这不是导致此问题的数据类型,因为即使我将uint32_t* test2 = (uint32_t*) DDR_BASEADDR;更改为int32_t* test2 = (int32_t*) DDR_BASEADDR;

,它也会出现

我在Zedboard的ARM A9和C++ online compiler上测试了这一点并得到了相同的结果。 感谢您的时间和精力。

Thang Tran

4 个答案:

答案 0 :(得分:8)

宏扩展为您提供

uint32_t* test2 = (uint32_t*) 0x000000000U + 128*1024*1024;

当你想要的时候

uint32_t* test2 = (uint32_t*) (0x000000000U + 128*1024*1024);

这就是为什么建议在宏定义中使用大量括号,或者 - 甚至更好 - 使用函数。

答案 1 :(得分:4)

你的宏扩展为:

uint32_t* test2 = (uint32_t*) 0x000000000U + 128*1024*1024;

这与执行此操作相同:

//Check multiply by sizeof at the end
uint32_t* test2 = (uint32_t *) (0x000000000U + 128*1024*1024 * sizeof(*test2));

因为指针类型的大小为sizeof(uint32_t),因此任何增加/减少操作都会乘以此大小。

你想要的是:

uint32_t* test2 = (uint32_t *) (0x000000000U + 128*1024*1024);

这意味着您首先计算地址,然后转换为所需的指针。

答案 2 :(得分:3)

这一点绝对没什么奇怪的:在这一行

uint32_t* test2 = (uint32_t*) DDR_BASEADDR;

施法指针操作应用于0x000000000U,之后int 128*1024*1024被添加到施法结果中,即

uint32_t* test2 = (uint32_t*) 0x000000000U + 128*1024*1024;
//                ^^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^
//                       Pointer                Offset

这意味着执行指针算术,因此在整数术语中,偏移值乘以sizeof(uint32_t),产生0x8000000

然而,在这一行

uint32_t whatIsThis = DDR_BASEADDR;

没有强制转换为指针,因此添加完全以整数形式完成。这就是sizeof(uint32_t)没有乘法的原因,结果是0x20000000

如果您希望所有四种情况都打印800000000,则转换为指针需要位于宏内:

#define DDR_BASEADDR  ((uint32_t*)0x000000000U + 128*1024*1024)

int32_t的演员需要在whatIsThis

的声明中
uint32_t whatIsThis = (uint32_t)DDR_BASEADDR;

但是,uint32_t不是指针的可移植类型。请改用uintptr_t

答案 3 :(得分:1)

#define DDR_BASEADDR  0x000000000U + 128*1024*1024

代码行

uint32_t* test2 = (uint32_t*) DDR_BASEADDR;

扩展为

uint32_t* test2 = (uint32_t*) 0x000000000U + 128*1024*1024;

实际上是(因为指针运算的方式)

uint32_t* test2 = (uint32_t*) (0x000000000U + (sizeof(uint32_t) *(128*1024*1024));

uint32_t* test2 = (uint32_t*) (0x000000000U + (4 * 0x8000000));

这样可以提供 200 00000而不是 80 00000,当然也是十六进制格式。
将乘以4的十六进制乘以4的倍数,就像乘以16(十六进制乘以10乘以十进制)并除以4。 即它产生1/4的值,向左移动一个十六进制数字:80-> 200。

其他代码行以某种方式具有一对大括号()的效果。

如果您使用

    #define DDR_BASEADDR  (0x000000000U + 128*1024*1024)

你应该没事,并且正如你已经证实的那样。

慷慨地使用牙箍是非常好的做法,特别是对于宏。

注意:您还应该更加小心printf并使用要打印的值的正确表示(请参阅注释和其他答案)。否则产生的 U ndefined B 行为是邪恶的! UB可能会导致各种不必要的和无法解释的事情。 但是,解释在这种特殊情况下得到的值不需要UB。 (你很幸运。)