不了解编译器的行为

Not understanding the behavior of compiler

本文关键字:编译器 不了解      更新时间:2023-10-16

我想了解为什么下面的代码实际上有效,而没有给出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$ 

只有当您试图访问无效内存时,才会出现分段错误。您的代码只执行指针运算,调整指向Test的指针以获得指向其成员之一的指针,而不读取或写入指针的目标。

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

(此外,不要使用像__in1这样的保留名称。也不要使用-fpermissive来允许这样的无意义转换:类型系统会帮助你。)

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。。。要明白,没有理由在你的机器上它应该是一样的,你永远不应该依赖编译器如何构造结构及其大小。