类的成员函数地址存放问题
class c
{
public:
int a;
char b;
int cc1;
c()
{
}
int add()
{
return this->a;
}
int ss()
{
int i = this->add();
return i;
}
};
突然想到的问题,如c类字节是12,this指针的地址存放的是对象首地址,那么this是怎么调用函数的?12个字节已经被变量占完了,函数是存放在哪里的(虚函数除外)?又不像虚函数一样,前面有4个字节可以存放一张函数表
下断点看汇编也下不了,断点自动跳到mian函数里了 编译器最终会把this->???转换成地址,然后call 地址 jhq999 发表于 2021-10-25 22:31
编译器最终会把this->???转换成地址,然后call 地址
存放位置喃?这个我知道,this会指向一张表,表里就是各种JMP,JMP的地址就是真正要call的地址 2736946915 发表于 2021-10-26 12:10
存放位置喃?这个我知道,this会指向一张表,表里就是各种JMP,JMP的地址就是真正要call的地址
一般在代码段或者导入表指向的映射在内存里的动态库等等。 jhq999 发表于 2021-10-26 12:55
一般在代码段或者导入表指向的映射在内存里的动态库等等。
我也认为函数是在代码段,但是网上说this是在对象的首地址(我不清楚),所以好奇this这四个字节他是跟着函数还是跟着对象走的,因为个人理解指针移动是用偏移的(类似于jmp内存偏移),既能调用函数又能调用变量,不可能偏移那么多吧,不然资源损耗太大 2736946915 发表于 2021-10-27 20:34
我也认为函数是在代码段,但是网上说this是在对象的首地址(我不清楚),所以好奇this这四个字节他是跟着函 ...
this 就是当前对象的地址,在调用成员函数的时候把这个地址传给成员函数
#include <iostream>
class point_t {
public:
point_t(ssize_t x = 0, ssize_t y = 0): x(x), y(y) {}
void print() {
std::cout << "this: " << this << std::endl;
std::cout << "(" << x << ", " << y << ")" << std::endl;
}
private:
ssize_t x, y;
};
int main() {
point_t p1(1, 2), p2(3, 4);
std::cout << "&p1: " << &p1 << std::endl;
std::cout << "&p2: " << &p2 << std::endl;
p1.print();
p2.print();
return 0;
}
人造人 发表于 2021-10-27 21:40
this 就是当前对象的地址,在调用成员函数的时候把这个地址传给成员函数
就是回调函数呗,对象的内存空间已经被变量占完了,那么他的成员函数放在那?
目前网上看到最靠谱的答案是,this存在于CPU的寄存器中,通过ECX去访问代码段的地址,也就是说一个类编译时会把函数地址大小确定在代码区,生成的对象调用时会直接重系统表里查找地址并传参,
但是问题就是this访问两段内存块(对象内存和函数内存),听起来有点不可思议, 2736946915 发表于 2021-10-28 12:12
就是回调函数呗,对象的内存空间已经被变量占完了,那么他的成员函数放在那?
目前网上看到最靠谱的答案 ...
1. 不是回调函数
2. 函数放在代码段
3. this 不是存在于 CPU 的寄存器中,this 就是当前对象的地址,你在当前对象前面加上取地址符号(&)就可以得到当前对象的 this 值
4. 生成的对象调用时会直接重系统表里查找地址并传参,不是这样的,是直接取到当前对象的地址(汇编语言的 lea 指令),然后传给成员函数(通过堆栈或者寄存器)
5. 但是问题就是this访问两段内存块(对象内存和函数内存),没有对象内存和函数内存这么一说,是代码段,数据段,堆栈段
6. 如果你真的想要弄明白这些东西,你必须去学汇编语言,8086 汇编语言
这是上面那个代码的汇编语言版本,但是你现在还看不懂对吧?
你的这个问题的答案全在下面这个汇编语言代码中
等你什么时候看得懂了这个代码,你的这个问题才算得上是真的得到了解决
很抱歉,我没办法给你解释清楚这一切,因为你现在还有好多东西没有学,你至少也需要先把汇编语言学完
.file "main.cpp"
.text
.section .rdata,"dr"
_ZStL19piecewise_construct:
.space 1
.lcomm _ZStL8__ioinit,1,1
.section .text$_ZN7point_tC1Ell,"x"
.linkonce discard
.align 2
.globl_ZN7point_tC1Ell
.def _ZN7point_tC1Ell; .scl 2;.type 32; .endef
.seh_proc _ZN7point_tC1Ell
_ZN7point_tC1Ell:
.LFB1496:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
.seh_endprologue
movq %rcx, 16(%rbp)
movq %rdx, 24(%rbp)
movq %r8, 32(%rbp)
movq 16(%rbp), %rax
movq 24(%rbp), %rdx
movq %rdx, (%rax)
movq 16(%rbp), %rax
movq 32(%rbp), %rdx
movq %rdx, 8(%rax)
nop
popq %rbp
ret
.seh_endproc
.section .rdata,"dr"
.LC0:
.ascii "this: \0"
.LC1:
.ascii "(\0"
.LC2:
.ascii ", \0"
.LC3:
.ascii ")\0"
.section .text$_ZN7point_t5printEv,"x"
.linkonce discard
.align 2
.globl_ZN7point_t5printEv
.def _ZN7point_t5printEv; .scl 2;.type 32; .endef
.seh_proc _ZN7point_t5printEv
_ZN7point_t5printEv:
.LFB1497:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movq %rcx, 16(%rbp)
leaq .LC0(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq 16(%rbp), %rdx
movq %rax, %rcx
call _ZNSolsEPKv
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
leaq .LC1(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq %rax, %rcx
movq 16(%rbp), %rax
movq (%rax), %rax
movq %rax, %rdx
call _ZNSolsEl
leaq .LC2(%rip), %rdx
movq %rax, %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq %rax, %rcx
movq 16(%rbp), %rax
movq 8(%rax), %rax
movq %rax, %rdx
call _ZNSolsEl
leaq .LC3(%rip), %rdx
movq %rax, %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
nop
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def __main; .scl 2;.type 32; .endef
.section .rdata,"dr"
.LC4:
.ascii "&p1: \0"
.LC5:
.ascii "&p2: \0"
.text
.globlmain
.def main; .scl 2;.type 32; .endef
.seh_proc main
main:
.LFB1498:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $64, %rsp
.seh_stackalloc 64
.seh_endprologue
call __main
leaq -16(%rbp), %rax
movl $2, %r8d
movl $1, %edx
movq %rax, %rcx
call _ZN7point_tC1Ell
leaq -32(%rbp), %rax
movl $4, %r8d
movl $3, %edx
movq %rax, %rcx
call _ZN7point_tC1Ell
leaq .LC4(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq %rax, %rcx
leaq -16(%rbp), %rax
movq %rax, %rdx
call _ZNSolsEPKv
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
leaq .LC5(%rip), %rdx
movq .refptr._ZSt4cout(%rip), %rcx
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movq %rax, %rcx
leaq -32(%rbp), %rax
movq %rax, %rdx
call _ZNSolsEPKv
movq .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
movq %rax, %rcx
call _ZNSolsEPFRSoS_E
leaq -16(%rbp), %rax
movq %rax, %rcx
call _ZN7point_t5printEv
leaq -32(%rbp), %rax
movq %rax, %rcx
call _ZN7point_t5printEv
movl $0, %eax
addq $64, %rsp
popq %rbp
ret
.seh_endproc
.def _Z41__static_initialization_and_destruction_0ii; .scl 3;.type 32; .endef
.seh_proc _Z41__static_initialization_and_destruction_0ii
_Z41__static_initialization_and_destruction_0ii:
.LFB1919:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movl %ecx, 16(%rbp)
movl %edx, 24(%rbp)
cmpl $1, 16(%rbp)
jne .L7
cmpl $65535, 24(%rbp)
jne .L7
leaq _ZStL8__ioinit(%rip), %rcx
call _ZNSt8ios_base4InitC1Ev
movq .refptr.__dso_handle(%rip), %r8
leaq _ZStL8__ioinit(%rip), %rdx
movq .refptr._ZNSt8ios_base4InitD1Ev(%rip), %rcx
call __cxa_atexit
.L7:
nop
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.def _GLOBAL__sub_I_main; .scl 3;.type 32; .endef
.seh_proc _GLOBAL__sub_I_main
_GLOBAL__sub_I_main:
.LFB1920:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
movl $65535, %edx
movl $1, %ecx
call _Z41__static_initialization_and_destruction_0ii
nop
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.section .ctors,"w"
.align 8
.quad _GLOBAL__sub_I_main
.ident"GCC: (GNU) 7.4.0"
.def _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc; .scl 2;.type 32; .endef
.def _ZNSolsEPKv; .scl 2;.type 32; .endef
.def _ZNSolsEPFRSoS_E; .scl 2;.type 32; .endef
.def _ZNSolsEl;.scl 2;.type 32; .endef
.def _ZNSt8ios_base4InitC1Ev; .scl 2;.type 32; .endef
.def __cxa_atexit; .scl 2;.type 32; .endef
.section .rdata$.refptr._ZNSt8ios_base4InitD1Ev, "dr"
.globl.refptr._ZNSt8ios_base4InitD1Ev
.linkonce discard
.refptr._ZNSt8ios_base4InitD1Ev:
.quad _ZNSt8ios_base4InitD1Ev
.section .rdata$.refptr.__dso_handle, "dr"
.globl.refptr.__dso_handle
.linkonce discard
.refptr.__dso_handle:
.quad __dso_handle
.section .rdata$.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, "dr"
.globl.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
.linkonce discard
.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_:
.quad _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
.section .rdata$.refptr._ZSt4cout, "dr"
.globl.refptr._ZSt4cout
.linkonce discard
.refptr._ZSt4cout:
.quad _ZSt4cout
人造人 发表于 2021-10-28 12:42
1. 不是回调函数
2. 函数放在代码段
3. this 不是存在于 CPU 的寄存器中,this 就是当前对象的地址,你 ...
3,我指的是this的地址(&this),不是this指向的地址
4,我是指使用的时候这个this会一直存在ECX中,
5.知道是代码段数据段,但是说习惯了。。。。
6.没必要太了解汇编,毕竟不是什么大牛,会用就行,能看懂就OK,比如说之前的LEA指令,意思类似于&,
call的时候也是call到一堆jmp中,并不是想象中的直接push了,不确定一截jmp是不是类似于虚函数的函数表,只是猜测
00EA109Bjmp __execute_onexit_table (0EA50CDh)
00EA10A0jmp __scrt_stub_for_acrt_uninitialize (0EA51C0h)
00EA10A5jmp __scrt_main_policy::set_app_type (0EA2180h)
00EA10AAjmp __wsplitpath_s (0EA50F1h)
00EA10AFjmp __scrt_dllmain_before_initialize_c (0EA2D00h)
00EA10B4jmp __scrt_get_show_window_mode (0EA3740h)
00EA10B9jmp ___current_exception (0EA4FF5h)
00EA10BEjmp _CRT_RTC_INITW (0EA1CC0h)
00EA10C3jmp _GetProcessHeap@0 (0EA5163h)
00EA10C8jmp _GetCurrentThreadId@0 (0EA50FDh)
00EA10CDjmp printf (0EA19E0h)
00EA10D2jmp _get_startup_file_mode (0EA3430h)
00EA10D7jmp __CrtDbgReportW (0EA5031h)
00EA10DCjmp _RTC_Failure (0EA23C0h)
00EA10E1jmp _GetCurrentProcessId@0 (0EA5121h)
00EA10E6jmp _FreeLibrary@4 (0EA516Fh)
00EA10EBjmp __set_app_type (0EA503Dh)
00EA10F0jmp __scrt_dllmain_exception_filter (0EA2D90h)
00EA10F5jmp _matherr (0EA3400h)
00EA10FAjmp ___stdio_common_vfprintf (0EA5025h)
00EA10FFjmp __register_thread_local_exe_atexit_callback (0EA5091h)
00EA1104jmp __scrt_dllmain_uninitialize_c (0EA2DF0h) 2736946915 发表于 2021-10-28 21:36
3,我指的是this的地址(&this),不是this指向的地址
4,我是指使用的时候这个this会一直存在ECX中,
5.知 ...
3. this 没有地址,&this 是错误的表达式
#include <iostream>
class point_t {
public:
point_t(ssize_t x = 0, ssize_t y = 0): x(x), y(y) {}
void print() {
std::cout << "this: " << this << std::endl;
std::cout << "&this: " << &this << std::endl;
std::cout << "(" << x << ", " << y << ")" << std::endl;
}
private:
ssize_t x, y;
};
int main() {
point_t p1(1, 2), p2(3, 4);
std::cout << "&p1: " << &p1 << std::endl;
std::cout << "&p2: " << &p2 << std::endl;
p1.print();
p2.print();
return 0;
}
4. 这个不一定,和编译器有关,编译器想用哪个寄存器就用哪个,编译器也完全可以使用 push 指令
6. 不是虚函数的函数表,在我这边就没有这个
这里就没有使用 ecx 寄存器
__ZN7point_tC1Eii:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
movl 12(%ebp), %edx
movl %edx, (%eax)
movl 8(%ebp), %eax
movl 16(%ebp), %edx
movl %edx, 4(%eax)
nop
popl %ebp
ret
movl $2, 8(%esp)
movl $1, 4(%esp)
leal 24(%esp), %eax
movl %eax, (%esp)
call __ZN7point_tC1Eii
movl $4, 8(%esp)
movl $3, 4(%esp)
leal 16(%esp), %eax
movl %eax, (%esp)
call __ZN7point_tC1Eii
6. 不同的编译器在调用成员函数的时候,生成的指令一般是不一样的
人造人 发表于 2021-10-28 21:58
这里就没有使用 ecx 寄存器
如果ECX被占用,就会用其他的寄存器,大部分情况下都是用ECX来传this,做外挂的也是找ECX,为什么,省事啊, 人造人 发表于 2021-10-28 21:51
3. this 没有地址,&this 是错误的表达式
一个类有虚函数为什么比普通类多4个字节?这4个字节就是虚函数的一个函数指针,指向的就是一张表,那个子类调用移动函数指针就行了,省事又方便,,ECX传this,EAX,EBX一般用于传参数,return的数据一般用EAX传,还有好多,大部分情况下是这样,除非当时被占用,这个资料百度上也是大把的,寄存器是有优先用那个,后用那个的,不是随意 2736946915 发表于 2021-10-29 13:52
一个类有虚函数为什么比普通类多4个字节?这4个字节就是虚函数的一个函数指针,指向的就是一张表,那个子 ...
嗯
页:
[1]