1

我无法理解 MS VC 编译器在这个上的行为。这条线编译得很好,但我得到的结果根本不是我所期望的:

this->Test((char *)&CS2 - (char *)&CS1 == sizeof(void *));

CS1 和 CS2 参数声明如下:

myFunction(tCS1* CS1, tCS2* CS2) {...

tCS1 和 tCS2 是分别包含一个 int 和一个 __int64 的结构。

这是为了检查我的参数 CS1 和 CS2 之间的堆栈距离,它们都是指针。当我在这一行中断执行并使用调试器获取我的两个变量的地址时,我发现它们确实相距 8 个字节(x64 平台)。

但是,比较的结果是错误的。

下面是编译器生成的汇编代码:

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax 

(然后它使用存储在 rdi 中的结果进行比较,并进行调用)

是的,编译器正在比较我的指针参数的值,而不是它们的地址。我在这里遗漏了一个间接级别,它去哪儿了?

当然,我无法在测试环境中重现这一点,而且我不知道该往哪里看。我正在将 32 位机器上的这段代码交叉编译到 x64 平台(我必须这样做),这是唯一“奇怪”的事情。任何想法,任何提示?

4

2 回答 2

0

正如@jpalacek 和评论者所观察到的,这是未定义的,编译器可能会利用它来做任何它喜欢的事情。这很奇怪。

此代码在 gcc 上“有效”:

#包括

整数函数(整数 *a,整数 *b)
{
    返回 (char *)&a - (char *) &b;
}

诠释主要(无效)
{
    整数a,b;
    printf("%d", func(&a, &b));
    返回0;
}

(gdb) 反汇编函数
函数 func 的汇编代码转储:
   0x0 80483e4 : 推送 %ebp
   0x080483e5 : 移动 %esp,%ebp
=> 0x080483e7 : lea 0x8(%ebp),%edx
   0x080483ea : lea 0xc(%ebp),%eax
   0x080483ed : 移动 %edx,%ecx
   0x080483ef : 子 %eax,%ecx
   0x080483f1 : 移动 %ecx,%eax
   0x080483f3 : 弹出 %ebp
   0x080483f4 : 回复    
汇编程序转储结束。

并且通过优化它只知道它们的相对地址:

(编辑:由于某种原因,答案在此处被截断)

(gdb) 反汇编函数
函数 func 的汇编代码转储:
   0x08048410 : 推送 %ebp
   0x08048411 : 移动 $0xfffffffc,%eax
   0x08048416 : 移动 %esp,%ebp
   0x08048418 : 弹出 %ebp
   0x08048419 : 回复    
汇编程序转储结束。

有趣的是,-O4优化后它返回+4,没有优化它返回-4。

你为什么要这样做呢?一般来说,不能保证参数具有任何内存地址:它们可以在寄存器中传递。

于 2010-07-23T05:49:46.807 回答
0

大会

mov         rax,qword ptr [CS1] 
mov         rdi,qword ptr [CS2] 
sub         rdi,rax

表示 CS1 和 CS2 不是真正的堆栈参数,而是一些全局符号 - 如果我想产生类似的结果,我会做这样的事情:

int* CS1 = NULL, *CS2 = NULL; /* or any other value...*/
#define CS1 *CS1
#define CS2 *CS2

当然这是丑陋的代码 - 但是你检查过你的代码中没有这样的东西吗?此外,动态链接器可能在其中发挥作用。

最后但同样重要的是:如果您尝试编写如下代码:

void foo()
{
  int a;
  int b;
  printf("%d", &a-&b);
}

您应该知道这实际上是未定义的行为,因为 C(和 C++)只允许减去指向单个对象(例如数组)内部的指针。

于 2010-07-21T15:43:43.287 回答