代码
class Person{
public:
int id;
int age;
int height;
void display(){
cout<<this<<" id = "<<id<<",age = "<<age<<",height = "<<height<<endl;
}
};
int main(int argc, const char * argv[]) {
Person person;
person.id = 10;
person.age = 20;
person.height = 30;
person.display();
// 指针访问 1
Person *p1 = (Person *)&person;
p1->age = 40;
p1->height = 50;
p1->display();
// 指针访问 2
Person *p2 = (Person *)&person.id;
p2->age = 40;
p2->height = 50;
p2->display();
// 指针访问 3
Person *p3 = (Person *)&person.age;
p3->age = 40;
p3->height = 50;
p3->display();
return 0;
}
打印如下
0x7ffeefbff430 id = 10,age = 20,height = 30
0x7ffeefbff430 id = 10,age = 40,height = 50
0x7ffeefbff430 id = 10,age = 40,height = 50
0x7ffeefbff434 id = 40,age = 40,height = 50
可以看出第四个打印不一样,细思一下,大致可以猜到是因为person对象与其第一个成员id地址都是一样,正如打印0x7ffeefbff430,而第四个是0x7ffeefbff434是指向第二个成员age,正好后4个字节。但还是有疑问滴,明明没改id,为啥变呢,那就探究下其汇编代码了,在这之前先简单了解下指令相关:
/**
*%rax 通常用于存储函数调用的返回结果,同时也用于乘法和除法指令中。
%rsp 是堆栈指针寄存器,通常会指向栈顶位置
%rbp 是栈帧指针寄存器,用于标识当前栈帧的起始位置
%rdi,%rsi,%rdx,%rcx,%r8,%r9 用来传递函数参数,依次对应第1参数,第2参数至第6参数
%rbx,%r12,%r13,%14,%15 ,%r10,%r11 用作数据存储,属于通用性更为广泛的寄存器,编译器或汇编程序可以根据需要存储任何数据。
*/
对象访问:
0x1000030b6 <+22>: movl $0xa, -0x20(%rbp) // 将10赋给该地址所在内存值,可以判断该地址是id成员的地址
0x1000030bd <+29>: movl $0x14, -0x1c(%rbp) // person.age = 20;
0x1000030c4 <+36>: movl $0x1e, -0x18(%rbp) // person.height = 30;
0x1000030cb <+43>: leaq -0x20(%rbp), %rdi //
0x1000030cf <+47>: callq 0x100003160 // person.display();
很明显,只是直接赋值方式
P1:
/**
Person *p1 = (Person *)&person;
p1->age = 40;
p1->height = 50;
p1->display();
*/
0x1000030d4 <+52>: leaq -0x20(%rbp), %rax // 将-0x20(%rbp)的地址拷贝到rax中,这里应该是person 的地址,p1指针的数据
0x1000030d8 <+56>: movq %rax, -0x28(%rbp) //
0x1000030dc <+60>: movq -0x28(%rbp), %rax // -0x28(%rbp)赋给rax
0x1000030e0 <+64>: movl $0x28, 0x4(%rax) // rax下4字节的数据存储40,age
0x1000030e7 <+71>: movq -0x28(%rbp), %rax
0x1000030eb <+75>: movl $0x32, 0x8(%rax) // 将50赋给0x8(%rax) ,第8字节后数据height
0x1000030f2 <+82>: movq -0x28(%rbp), %rdi
0x1000030f6 <+86>: callq 0x100003160
可以看出底层是通过指针地址偏移指向来修改成员变量的
P2:
0x1000030fb <+91>: leaq -0x20(%rbp), %rax
0x1000030ff <+95>: movq %rax, -0x30(%rbp) //
0x100003103 <+99>: movq -0x30(%rbp), %rax
0x100003107 <+103>: movl $0x28, 0x4(%rax)
0x10000310e <+110>: movq -0x30(%rbp), %rax
0x100003112 <+114>: movl $0x32, 0x8(%rax)
0x100003119 <+121>: movq -0x30(%rbp), %rdi
0x10000311d <+125>: callq 0x100003160
P3:
0x100003122 <+130>: leaq -0x20(%rbp), %rax
0x100003126 <+134>: addq $0x4, %rax // 递增%rdx 4字节
0x10000312c <+140>: movq %rax, -0x38(%rbp)
0x100003130 <+144>: movq -0x38(%rbp), %rax
0x100003134 <+148>: movl $0x28, 0x4(%rax)
0x10000313b <+155>: movq -0x38(%rbp), %rax
0x10000313f <+159>: movl $0x32, 0x8(%rax)
0x100003146 <+166>: movq -0x38(%rbp), %rdi
0x10000314a <+170>: callq 0x100003160
p3指向了原person.age,也就意味着其首成员p3->id是原person.age的值,这就是解释了为啥p3->id为40。
同样值得注意的是person.display())与p3->display()结果又不一样,因为两者地址已经不一样
网友评论