减去指针:这个缺失的间接级别来自何处?

时间:2010-07-21 15:29:58

标签: c++ pointers pointer-arithmetic

我无法理解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平台(我必须),这是关于它的唯一“奇怪”事情。任何想法,任何提示?

2 个答案:

答案 0 :(得分: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 ++)只允许减去指向单个对象(例如数组)内部的指针。

答案 1 :(得分:0)

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

此代码“适用于”gcc:


#include 

int func(int *a, int *b)
{
    return (char *)&a - (char *) &b;
}

int main(void)
{
    int a, b;
    printf("%d", func(&a, &b));
    return 0;
}

(gdb) disassemble func
Dump of assembler code for function func:
   0x0 80483e4 :    push   %ebp
   0x080483e5 : mov    %esp,%ebp
=> 0x080483e7 : lea    0x8(%ebp),%edx
   0x080483ea : lea    0xc(%ebp),%eax
   0x080483ed : mov    %edx,%ecx
   0x080483ef :    sub    %eax,%ecx
   0x080483f1 :    mov    %ecx,%eax
   0x080483f3 :    pop    %ebp
   0x080483f4 :    ret    
End of assembler dump.

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

(编辑:答案因某种原因被截断)

(gdb) disassemble  func
Dump of assembler code for function func:
   0x08048410 : push   %ebp
   0x08048411 : mov    $0xfffffffc,%eax
   0x08048416 : mov    %esp,%ebp
   0x08048418 : pop    %ebp
   0x08048419 : ret    
End of assembler dump.

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

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