指针是C/C++中最灵活的变量,可随意指向任何内存地址,稍有不慎,就会造成程序崩溃,且很难找出原因。
下面这道题,程序crash,一开始没找到原因,经过gdb调试之后,找到根本原因。
struct sInt
{
int i;
int *p;
};
//在64位环境下,sizeof(sInt) = 8; 其中 i占4个字节,p占8个字节,为了保证字节对齐,编译器对i之后地址补齐(加上int padding,占4个字节),共16字节
//8字节对齐是为了提高CPU对数据的读取效率
int test_sInt()
{
sInt s;
int *p=&s.i;
p[0]=0;
p[1]=1;
p[2]=2;
p[3]=3;
s.p=p; //s.p指向s.i的地址
s.p[0]=0;//对s.i赋值
s.p[1]=1;//对padding赋值
s.p[2]=2; //对s.p赋值,s.p指针将“飞走”,指向一个非法地址
s.p[3]=3;//对一个非法地址赋值,Segmentation fault
return 0;
}
//下面对程序做了下扩展测试,如果i,p为2个局部变量,程序一样crash。
int test_int()
{
int i;
int *p;
i = 0;
p = &i;
p[0]=0;
p[1]=1;
p[2]=2;//i,p不属于结构体,不用8字节对齐,所以在p[1]赋值时,指针p就"飞走了",p[2]为非法内存,Segmentation fault
p[3]=3;
return 0;
}
//如果结构体中成员变量为char,程序不一定会crash。
//这是因为char *p 占8个字节,p[8]赋值改变1个字节的内容,指针“飞”的很近,可能在合法范围内,所以不一定会crash;
// 而int *p也占8个字节,p[2]赋值将会改变4个字节的内容,指针“飞”跑了,逃出合法范围,必将crash.
struct sChar
{
char i;
char *p;
};
int test_sChar()
{
sChar s;
char *p=&s.i;
for(int i = 0; i<16; i++)
{
p[i] = 'a';
}
s.p=p;
for(int i = 0; i<16; i++)
{
s.p[i] = 'a'; //i>8时,随机Segmentation fault
}
return 0;
}
int main()
{
test_sInt();
test_int();
test_sChar();
return 1;
}