鱼C论坛

 找回密码
 立即注册
查看: 4028|回复: 15

类的成员函数地址存放问题

[复制链接]
发表于 2021-10-25 22:12:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

  1. class c
  2. {
  3. public:
  4.         int a;
  5.         char b;
  6.         int cc1;
  7.         c()
  8.         {

  9.         }
  10.         int add()
  11.         {
  12.                 return this->a;
  13.         }

  14.         int ss()
  15.         {
  16.                 int i = this->add();
  17.                 return i;
  18.         }

  19. };
复制代码


突然想到的问题,如c类字节是12,this指针的地址存放的是对象首地址,那么this是怎么调用函数的?12个字节已经被变量占完了,函数是存放在哪里的(虚函数除外)?又不像虚函数一样,前面有4个字节可以存放一张函数表
下断点看汇编也下不了,断点自动跳到mian函数里了
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-10-25 22:31:31 | 显示全部楼层
编译器最终会把this->???转换成地址,然后call 地址
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-26 12:10:49 | 显示全部楼层
jhq999 发表于 2021-10-25 22:31
编译器最终会把this->???转换成地址,然后call 地址

存放位置喃?这个我知道,this会指向一张表,表里就是各种JMP,JMP的地址就是真正要call的地址
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-26 12:55:37 | 显示全部楼层
2736946915 发表于 2021-10-26 12:10
存放位置喃?这个我知道,this会指向一张表,表里就是各种JMP,JMP的地址就是真正要call的地址

一般在代码段或者导入表指向的映射在内存里的动态库等等。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-27 20:34:32 | 显示全部楼层
jhq999 发表于 2021-10-26 12:55
一般在代码段或者导入表指向的映射在内存里的动态库等等。

我也认为函数是在代码段,但是网上说this是在对象的首地址(我不清楚),所以好奇this这四个字节他是跟着函数还是跟着对象走的,因为个人理解指针移动是用偏移的(类似于jmp内存偏移),既能调用函数又能调用变量,不可能偏移那么多吧,不然资源损耗太大
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-27 21:40:47 | 显示全部楼层
2736946915 发表于 2021-10-27 20:34
我也认为函数是在代码段,但是网上说this是在对象的首地址(我不清楚),所以好奇this这四个字节他是跟着函 ...

this 就是当前对象的地址,在调用成员函数的时候把这个地址传给成员函数
  1. #include <iostream>

  2. class point_t {
  3. public:
  4.     point_t(ssize_t x = 0, ssize_t y = 0): x(x), y(y) {}
  5.     void print() {
  6.         std::cout << "this: " << this << std::endl;
  7.         std::cout << "(" << x << ", " << y << ")" << std::endl;
  8.     }
  9. private:
  10.     ssize_t x, y;
  11. };

  12. int main() {
  13.     point_t p1(1, 2), p2(3, 4);
  14.     std::cout << "&p1: " << &p1 << std::endl;
  15.     std::cout << "&p2: " << &p2 << std::endl;
  16.     p1.print();
  17.     p2.print();
  18.     return 0;
  19. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-28 12:12:37 | 显示全部楼层
人造人 发表于 2021-10-27 21:40
this 就是当前对象的地址,在调用成员函数的时候把这个地址传给成员函数

就是回调函数呗,对象的内存空间已经被变量占完了,那么他的成员函数放在那?
目前网上看到最靠谱的答案是,this存在于CPU的寄存器中,通过ECX去访问代码段的地址,也就是说一个类编译时会把函数地址大小确定在代码区,生成的对象调用时会直接重系统表里查找地址并传参,
但是问题就是this访问两段内存块(对象内存和函数内存),听起来有点不可思议,
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-28 12:42:03 | 显示全部楼层
2736946915 发表于 2021-10-28 12:12
就是回调函数呗,对象的内存空间已经被变量占完了,那么他的成员函数放在那?
目前网上看到最靠谱的答案 ...

1. 不是回调函数
2. 函数放在代码段
3. this 不是存在于 CPU 的寄存器中,this 就是当前对象的地址,你在当前对象前面加上取地址符号(&)就可以得到当前对象的 this 值
4. 生成的对象调用时会直接重系统表里查找地址并传参,不是这样的,是直接取到当前对象的地址(汇编语言的 lea 指令),然后传给成员函数(通过堆栈或者寄存器)
5. 但是问题就是this访问两段内存块(对象内存和函数内存),没有对象内存和函数内存这么一说,是代码段,数据段,堆栈段
6. 如果你真的想要弄明白这些东西,你必须去学汇编语言,8086 汇编语言
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-28 12:47:11 | 显示全部楼层
这是上面那个代码的汇编语言版本,但是你现在还看不懂对吧?
你的这个问题的答案全在下面这个汇编语言代码中
等你什么时候看得懂了这个代码,你的这个问题才算得上是真的得到了解决
很抱歉,我没办法给你解释清楚这一切,因为你现在还有好多东西没有学,你至少也需要先把汇编语言学完

  1.     .file   "main.cpp"
  2.     .text
  3.     .section .rdata,"dr"
  4. _ZStL19piecewise_construct:
  5.     .space 1
  6. .lcomm _ZStL8__ioinit,1,1
  7.     .section    .text$_ZN7point_tC1Ell,"x"
  8.     .linkonce discard
  9.     .align 2
  10.     .globl  _ZN7point_tC1Ell
  11.     .def    _ZN7point_tC1Ell;   .scl    2;  .type   32; .endef
  12.     .seh_proc   _ZN7point_tC1Ell
  13. _ZN7point_tC1Ell:
  14. .LFB1496:
  15.     pushq   %rbp
  16.     .seh_pushreg    %rbp
  17.     movq    %rsp, %rbp
  18.     .seh_setframe   %rbp, 0
  19.     .seh_endprologue
  20.     movq    %rcx, 16(%rbp)
  21.     movq    %rdx, 24(%rbp)
  22.     movq    %r8, 32(%rbp)
  23.     movq    16(%rbp), %rax
  24.     movq    24(%rbp), %rdx
  25.     movq    %rdx, (%rax)
  26.     movq    16(%rbp), %rax
  27.     movq    32(%rbp), %rdx
  28.     movq    %rdx, 8(%rax)
  29.     nop
  30.     popq    %rbp
  31.     ret
  32.     .seh_endproc
  33.     .section .rdata,"dr"
  34. .LC0:
  35.     .ascii "this: \0"
  36. .LC1:
  37.     .ascii "(\0"
  38. .LC2:
  39.     .ascii ", \0"
  40. .LC3:
  41.     .ascii ")\0"
  42.     .section    .text$_ZN7point_t5printEv,"x"
  43.     .linkonce discard
  44.     .align 2
  45.     .globl  _ZN7point_t5printEv
  46.     .def    _ZN7point_t5printEv;    .scl    2;  .type   32; .endef
  47.     .seh_proc   _ZN7point_t5printEv
  48. _ZN7point_t5printEv:
  49. .LFB1497:
  50.     pushq   %rbp
  51.     .seh_pushreg    %rbp
  52.     movq    %rsp, %rbp
  53.     .seh_setframe   %rbp, 0
  54.     subq    $32, %rsp
  55.     .seh_stackalloc 32
  56.     .seh_endprologue
  57.     movq    %rcx, 16(%rbp)
  58.     leaq    .LC0(%rip), %rdx
  59.     movq    .refptr._ZSt4cout(%rip), %rcx
  60.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  61.     movq    16(%rbp), %rdx
  62.     movq    %rax, %rcx
  63.     call    _ZNSolsEPKv
  64.     movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
  65.     movq    %rax, %rcx
  66.     call    _ZNSolsEPFRSoS_E
  67.     leaq    .LC1(%rip), %rdx
  68.     movq    .refptr._ZSt4cout(%rip), %rcx
  69.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  70.     movq    %rax, %rcx
  71.     movq    16(%rbp), %rax
  72.     movq    (%rax), %rax
  73.     movq    %rax, %rdx
  74.     call    _ZNSolsEl
  75.     leaq    .LC2(%rip), %rdx
  76.     movq    %rax, %rcx
  77.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  78.     movq    %rax, %rcx
  79.     movq    16(%rbp), %rax
  80.     movq    8(%rax), %rax
  81.     movq    %rax, %rdx
  82.     call    _ZNSolsEl
  83.     leaq    .LC3(%rip), %rdx
  84.     movq    %rax, %rcx
  85.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  86.     movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
  87.     movq    %rax, %rcx
  88.     call    _ZNSolsEPFRSoS_E
  89.     nop
  90.     addq    $32, %rsp
  91.     popq    %rbp
  92.     ret
  93.     .seh_endproc
  94.     .def    __main; .scl    2;  .type   32; .endef
  95.     .section .rdata,"dr"
  96. .LC4:
  97.     .ascii "&p1: \0"
  98. .LC5:
  99.     .ascii "&p2: \0"
  100.     .text
  101.     .globl  main
  102.     .def    main;   .scl    2;  .type   32; .endef
  103.     .seh_proc   main
  104. main:
  105. .LFB1498:
  106.     pushq   %rbp
  107.     .seh_pushreg    %rbp
  108.     movq    %rsp, %rbp
  109.     .seh_setframe   %rbp, 0
  110.     subq    $64, %rsp
  111.     .seh_stackalloc 64
  112.     .seh_endprologue
  113.     call    __main
  114.     leaq    -16(%rbp), %rax
  115.     movl    $2, %r8d
  116.     movl    $1, %edx
  117.     movq    %rax, %rcx
  118.     call    _ZN7point_tC1Ell
  119.     leaq    -32(%rbp), %rax
  120.     movl    $4, %r8d
  121.     movl    $3, %edx
  122.     movq    %rax, %rcx
  123.     call    _ZN7point_tC1Ell
  124.     leaq    .LC4(%rip), %rdx
  125.     movq    .refptr._ZSt4cout(%rip), %rcx
  126.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  127.     movq    %rax, %rcx
  128.     leaq    -16(%rbp), %rax
  129.     movq    %rax, %rdx
  130.     call    _ZNSolsEPKv
  131.     movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
  132.     movq    %rax, %rcx
  133.     call    _ZNSolsEPFRSoS_E
  134.     leaq    .LC5(%rip), %rdx
  135.     movq    .refptr._ZSt4cout(%rip), %rcx
  136.     call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
  137.     movq    %rax, %rcx
  138.     leaq    -32(%rbp), %rax
  139.     movq    %rax, %rdx
  140.     call    _ZNSolsEPKv
  141.     movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rdx
  142.     movq    %rax, %rcx
  143.     call    _ZNSolsEPFRSoS_E
  144.     leaq    -16(%rbp), %rax
  145.     movq    %rax, %rcx
  146.     call    _ZN7point_t5printEv
  147.     leaq    -32(%rbp), %rax
  148.     movq    %rax, %rcx
  149.     call    _ZN7point_t5printEv
  150.     movl    $0, %eax
  151.     addq    $64, %rsp
  152.     popq    %rbp
  153.     ret
  154.     .seh_endproc
  155.     .def    _Z41__static_initialization_and_destruction_0ii;    .scl    3;  .type   32; .endef
  156.     .seh_proc   _Z41__static_initialization_and_destruction_0ii
  157. _Z41__static_initialization_and_destruction_0ii:
  158. .LFB1919:
  159.     pushq   %rbp
  160.     .seh_pushreg    %rbp
  161.     movq    %rsp, %rbp
  162.     .seh_setframe   %rbp, 0
  163.     subq    $32, %rsp
  164.     .seh_stackalloc 32
  165.     .seh_endprologue
  166.     movl    %ecx, 16(%rbp)
  167.     movl    %edx, 24(%rbp)
  168.     cmpl    $1, 16(%rbp)
  169.     jne .L7
  170.     cmpl    $65535, 24(%rbp)
  171.     jne .L7
  172.     leaq    _ZStL8__ioinit(%rip), %rcx
  173.     call    _ZNSt8ios_base4InitC1Ev
  174.     movq    .refptr.__dso_handle(%rip), %r8
  175.     leaq    _ZStL8__ioinit(%rip), %rdx
  176.     movq    .refptr._ZNSt8ios_base4InitD1Ev(%rip), %rcx
  177.     call    __cxa_atexit
  178. .L7:
  179.     nop
  180.     addq    $32, %rsp
  181.     popq    %rbp
  182.     ret
  183.     .seh_endproc
  184.     .def    _GLOBAL__sub_I_main;    .scl    3;  .type   32; .endef
  185.     .seh_proc   _GLOBAL__sub_I_main
  186. _GLOBAL__sub_I_main:
  187. .LFB1920:
  188.     pushq   %rbp
  189.     .seh_pushreg    %rbp
  190.     movq    %rsp, %rbp
  191.     .seh_setframe   %rbp, 0
  192.     subq    $32, %rsp
  193.     .seh_stackalloc 32
  194.     .seh_endprologue
  195.     movl    $65535, %edx
  196.     movl    $1, %ecx
  197.     call    _Z41__static_initialization_and_destruction_0ii
  198.     nop
  199.     addq    $32, %rsp
  200.     popq    %rbp
  201.     ret
  202.     .seh_endproc
  203.     .section    .ctors,"w"
  204.     .align 8
  205.     .quad   _GLOBAL__sub_I_main
  206.     .ident  "GCC: (GNU) 7.4.0"
  207.     .def    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc;    .scl    2;  .type   32; .endef
  208.     .def    _ZNSolsEPKv;    .scl    2;  .type   32; .endef
  209.     .def    _ZNSolsEPFRSoS_E;   .scl    2;  .type   32; .endef
  210.     .def    _ZNSolsEl;  .scl    2;  .type   32; .endef
  211.     .def    _ZNSt8ios_base4InitC1Ev;    .scl    2;  .type   32; .endef
  212.     .def    __cxa_atexit;   .scl    2;  .type   32; .endef
  213.     .section    .rdata$.refptr._ZNSt8ios_base4InitD1Ev, "dr"
  214.     .globl  .refptr._ZNSt8ios_base4InitD1Ev
  215.     .linkonce   discard
  216. .refptr._ZNSt8ios_base4InitD1Ev:
  217.     .quad   _ZNSt8ios_base4InitD1Ev
  218.     .section    .rdata$.refptr.__dso_handle, "dr"
  219.     .globl  .refptr.__dso_handle
  220.     .linkonce   discard
  221. .refptr.__dso_handle:
  222.     .quad   __dso_handle
  223.     .section    .rdata$.refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, "dr"
  224.     .globl  .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
  225.     .linkonce   discard
  226. .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_:
  227.     .quad   _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
  228.     .section    .rdata$.refptr._ZSt4cout, "dr"
  229.     .globl  .refptr._ZSt4cout
  230.     .linkonce   discard
  231. .refptr._ZSt4cout:
  232.     .quad   _ZSt4cout
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-28 21:36:21 | 显示全部楼层
人造人 发表于 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是不是类似于虚函数的函数表,只是猜测

  1. 00EA109B  jmp         __execute_onexit_table (0EA50CDh)  
  2. 00EA10A0  jmp         __scrt_stub_for_acrt_uninitialize (0EA51C0h)  
  3. 00EA10A5  jmp         __scrt_main_policy::set_app_type (0EA2180h)  
  4. 00EA10AA  jmp         __wsplitpath_s (0EA50F1h)  
  5. 00EA10AF  jmp         __scrt_dllmain_before_initialize_c (0EA2D00h)  
  6. 00EA10B4  jmp         __scrt_get_show_window_mode (0EA3740h)  
  7. 00EA10B9  jmp         ___current_exception (0EA4FF5h)  
  8. 00EA10BE  jmp         _CRT_RTC_INITW (0EA1CC0h)  
  9. 00EA10C3  jmp         _GetProcessHeap@0 (0EA5163h)  
  10. 00EA10C8  jmp         _GetCurrentThreadId@0 (0EA50FDh)  
  11. 00EA10CD  jmp         printf (0EA19E0h)  
  12. 00EA10D2  jmp         _get_startup_file_mode (0EA3430h)  
  13. 00EA10D7  jmp         __CrtDbgReportW (0EA5031h)  
  14. 00EA10DC  jmp         _RTC_Failure (0EA23C0h)  
  15. 00EA10E1  jmp         _GetCurrentProcessId@0 (0EA5121h)  
  16. 00EA10E6  jmp         _FreeLibrary@4 (0EA516Fh)  
  17. 00EA10EB  jmp         __set_app_type (0EA503Dh)  
  18. 00EA10F0  jmp         __scrt_dllmain_exception_filter (0EA2D90h)  
  19. 00EA10F5  jmp         _matherr (0EA3400h)  
  20. 00EA10FA  jmp         ___stdio_common_vfprintf (0EA5025h)  
  21. 00EA10FF  jmp         __register_thread_local_exe_atexit_callback (0EA5091h)  
  22. 00EA1104  jmp         __scrt_dllmain_uninitialize_c (0EA2DF0h)  
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-28 21:51:21 | 显示全部楼层
2736946915 发表于 2021-10-28 21:36
3,我指的是this的地址(&this),不是this指向的地址
4,我是指使用的时候这个this会一直存在ECX中,
5.知 ...


3. this 没有地址,&this 是错误的表达式
  1. #include <iostream>

  2. class point_t {
  3. public:
  4.     point_t(ssize_t x = 0, ssize_t y = 0): x(x), y(y) {}
  5.     void print() {
  6.         std::cout << "this: " << this << std::endl;
  7.         std::cout << "&this: " << &this << std::endl;
  8.         std::cout << "(" << x << ", " << y << ")" << std::endl;
  9.     }
  10. private:
  11.     ssize_t x, y;
  12. };

  13. int main() {
  14.     point_t p1(1, 2), p2(3, 4);
  15.     std::cout << "&p1: " << &p1 << std::endl;
  16.     std::cout << "&p2: " << &p2 << std::endl;
  17.     p1.print();
  18.     p2.print();
  19.     return 0;
  20. }
复制代码


4. 这个不一定,和编译器有关,编译器想用哪个寄存器就用哪个,编译器也完全可以使用 push 指令
6. 不是虚函数的函数表,在我这边就没有这个
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-28 21:58:15 | 显示全部楼层
这里就没有使用 ecx 寄存器

  1. __ZN7point_tC1Eii:
  2.         pushl        %ebp
  3.         movl        %esp, %ebp
  4.         movl        8(%ebp), %eax
  5.         movl        12(%ebp), %edx
  6.         movl        %edx, (%eax)
  7.         movl        8(%ebp), %eax
  8.         movl        16(%ebp), %edx
  9.         movl        %edx, 4(%eax)
  10.         nop
  11.         popl        %ebp
  12.         ret
复制代码

  1.         movl        $2, 8(%esp)
  2.         movl        $1, 4(%esp)
  3.         leal        24(%esp), %eax
  4.         movl        %eax, (%esp)
  5.         call        __ZN7point_tC1Eii
  6.         movl        $4, 8(%esp)
  7.         movl        $3, 4(%esp)
  8.         leal        16(%esp), %eax
  9.         movl        %eax, (%esp)
  10.         call        __ZN7point_tC1Eii
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-28 22:02:14 | 显示全部楼层
6. 不同的编译器在调用成员函数的时候,生成的指令一般是不一样的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-29 13:47:54 | 显示全部楼层
人造人 发表于 2021-10-28 21:58
这里就没有使用 ecx 寄存器

如果ECX被占用,就会用其他的寄存器,大部分情况下都是用ECX来传this,做外挂的也是找ECX,为什么,省事啊,
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-10-29 13:52:30 | 显示全部楼层
人造人 发表于 2021-10-28 21:51
3. this 没有地址,&this 是错误的表达式

一个类有虚函数为什么比普通类多4个字节?这4个字节就是虚函数的一个函数指针,指向的就是一张表,那个子类调用移动函数指针就行了,省事又方便,,ECX传this,EAX,EBX一般用于传参数,return的数据一般用EAX传,还有好多,大部分情况下是这样,除非当时被占用,这个资料百度上也是大把的,寄存器是有优先用那个,后用那个的,不是随意
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-10-29 14:09:46 | 显示全部楼层
2736946915 发表于 2021-10-29 13:52
一个类有虚函数为什么比普通类多4个字节?这4个字节就是虚函数的一个函数指针,指向的就是一张表,那个子 ...

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-26 03:01

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表