不了解编译器的行为

时间:2014-03-03 14:53:21

标签: c++ pointers gcc

我想了解为什么下面的代码实际上有效并且没有给出seg错误。我有一位同事告诉我这件事,我感到很惊讶。

有人可以解释并指出一些良好的链接来理解我对此的理解吗?

struct Test {
    int __in;
    int __in1;
};

int main()
{
    struct Test* t = NULL;
    int i = &(t->__in1) + 4;
    std::cout << i << std::endl;
}

arun@arun-desktop:~/Code$ g++ -fpermissive -g test8.cc 
test8.cc: In function ‘int main()’:
test8.cc:11:24: warning: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
arun@arun-desktop:~/Code$ ./a.out   
20
arun@arun-desktop:~/Code$ 

2 个答案:

答案 0 :(得分:8)

如果您尝试访问无效内存,则只会出现分段错误。您的代码只执行指针运算,调整指向Test的指针以获取指向其中一个成员的指针,并且不读取或写入指针的目标。

它仍然是未定义的行为。小孩,不要在家里这样做。

(另外,请不要使用__in1之类的reserved names。并且不要使用-fpermissive来允许这样的无意义转换:类型系统可以帮助您。)< / p>

答案 1 :(得分:2)

struct Test {
    int __in;
    int __in1;
};

unsigned int fun ( void )
{
    struct Test* t=NULL;
    unsigned int i = (unsigned int)(&(t->__in1)) + 4;
    return(i);
}

unsigned int fun2 ( void )
{
    struct Test t;
    unsigned int i = (unsigned int)(&(t.__in1)) + 4;
    return(i);
}

我稍微修改了你的代码,部分是为了帮助解决警告/错误。在第一种情况下,你有一个没有内存的指针,所以它没有元素。您已将其指向NULL。你需要将它指向null以外的其他东西(所以数学是NULL或零加上4加4的偏移量),但这不会解决问题。

在第二种情况下,它背后有一些内存,编译器在堆栈上分配了一个实际的结构。所以我明白了:

00000000 <fun>:
   0:   e3a00008    mov r0, #8
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e24dd008    sub sp, sp, #8
   c:   e28d0008    add r0, sp, #8
  10:   e28dd008    add sp, sp, #8
  14:   e12fff1e    bx  lr

所以对代码有一些希望,就像给你一个你可以使用的地址。

当您使用适当的类型转换和命令行选项构建程序以通过错误时,它也将输出8。

我没有看到像这样做地址数学有什么问题,不是这个结构,但我可以看到这样的事情的一些用例。您应该能够获得结构中项目的地址,并且您应该能够使用该地址进行数学地址处理,这种数学没有任何违法行为。

这里尝试这个例子(只是用了一点谷歌搜索并找到另一个stackoverflow问题):

//g++ -std=c++11 ptr.c -o ptr
#include <iostream>
#include <cstdint>
struct Test {
    int __in;
    int __in1;
};

int main()
{
    struct Test t;
    intptr_t i = (intptr_t)(&(t.__in1))-(intptr_t)(&t) + 4;
    std::cout << i << std::endl;
}

结果在我的机器上显示为8 ...理解在机器上没有理由它应该是相同的,你永远不应该依赖于编译器如何构造结构及其大小。

相关问题