我有以下代码片段:
class ABC{
public:
int a;
void print(){cout<<"hello"<<endl;}
};
int main(){
ABC *ptr = NULL:
ptr->print();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它运行成功.有人可以解释一下吗?
Jam*_*lis 31
使用不指向有效对象的指针调用成员函数会导致未定义的行为.什么事情都可能发生.它可以运行; 它可能崩溃.
在这种情况下,它似乎工作,因为this没有使用指向有效对象的指针print.
Ale*_*hov 14
(我不记得我从哪里得到这些知识,所以我可能完全错了)
在引擎盖下,大多数编译器会将你的类转换成这样的东西:
struct _ABC_data{
int a ;
};
// table of member functions
void _abc_print( _ABC_data* this );
Run Code Online (Sandbox Code Playgroud)
其中_ABC_data是C风格的结构
并且您的电话ptr->print();将转换为:
_abc_print( NULL)
Run Code Online (Sandbox Code Playgroud)
因为你不使用thisarg,所以在执行时没问题.
更新:(感谢Windows程序员正确评论)
这样的代码只适用于执行它的CPU.
绝对没有理由利用这个实现功能.这就是为什么:
Win*_*mer 14
大多数答案说未定义的行为可能包括"出现"工作,他们是对的.
Alexander Malakhov的回答给出了一些常见的实施细节,并解释了为什么你的情况似乎有效,但他做了一个轻微的错误陈述.他写道"因为你不使用这个arg,所以执行时没什么问题"但是意思是"因为你不使用这个arg而执行时似乎没什么问题".
但请注意,您的代码仍然是未定义的行为.它打印了您想要的内容,并将您的银行账户余额转移到我的账户.我谢谢你.
(SO风格说这应该是一个评论,但它太长了.虽然我做了CW.)
表达式ptr->print();将(*ptr).print();根据C++标准(5.2.5/3)隐式转换为.并且取消引用空指针会导致未定义的行为.幸运的是,有问题的代码在您的情况下可以正常运行.你不应该依赖它.
5.2.5/3:
如果E1具有"指向类X的指针"类型,则表达式E1-> E2被转换为等效形式(*(E1)).E2; 5.2.5的剩余部分将仅解决第一个选项(点)59).缩写客观表达.id-expression为E1.E2,然后此表达式的type和lvalue属性确定如下.在5.2.5的其余部分中,cq表示const或不存在const; vq代表挥发性或不存在挥发性.cv表示一组任意的cv限定符,如3.9.3中所定义.
虽然我不确定这是否是确切的答案,但这是我的理解.(另外,我的CPP术语很糟糕 - 如果可能,请忽略它)
对于C++,当声明任何类(即尚未创建即时)时,函数将放在正在创建的二进制文件的.text部分中.创建瞬间时,不会复制函数或方法.也就是说,当编译器解析CPP文件时,它将ptr->print()使用.text部分中定义的适当地址替换函数调用.
因此,所有的编译器可能做的就是代替基于适当的地址类型的ptr的功能print.(这也意味着一些检查相关的公共/私人/继承等)
我为您的代码(命名test12.cpp)执行了以下操作:
编辑:在下面给ASM添加一些评论(我真的不擅长ASM,我几乎无法阅读它 - 只够了解一些基本内容) - 最好是阅读这个Wikibook链接,我也做过:D以防万一有人在ASW中发现错误,请发表评论 - 我很乐意修复它们并了解更多信息.
$ g++ test.cpp -S
$ cat test.s
...
// Following snippet is part of main function call
movl $0, -8(%ebp) //this is for creating the NULL pointer ABC* ptr=NULL
//It sets first 8 bytes on stack to '0'
movl -8(%ebp), %eax //Load the ptr pointer into eax register
movl %eax, (%esp) //Push the ptr on stack for using in function being called below
//This is being done assuming that these elements would be used
//in the print() function being called
call _ZN3ABC5printE //Call to print function after pushing arguments (which are none) and
//accesss pointer (ptr) on stack.
...
Run Code Online (Sandbox Code Playgroud)
v ZN3ABC5printEv表示在以下定义的函数的全局定义class ABC:
...
.LC0: //This declares a label named .LC0
.string "hello" // String "hello" which was passed in print()
.section .text._ZN3ABC5printEv,"axG",@progbits,_ZN3ABC5printEv,comdat
.align 2
.weak _ZN3ABC5printEv //Not sure, but something to do with name mangling
.type _ZN3ABC5printEv, @function
_ZN3ABC5printEv: //Label for function print() with mangled name
//following is the function definition for print() function
.LFB1401: //One more lavbel
pushl %ebp //Save the 'last' known working frame pointer
.LCFI9:
movl %esp, %ebp //Set frame (base pointer ebp) to current stack top (esp)
.LCFI10:
subl $8, %esp //Allocating 8 bytes space on stack
.LCFI11:
movl $.LC0, 4(%esp) //Pushing the string represented by label .LC0 in
//in first 4 bytes of stack
movl $_ZSt4cout, (%esp) //Something to do with "cout<<" statement
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
movl %eax, (%esp)
call _ZNSolsEPFRSoS_E //Probably call to some run time library for 'cout'
leave //end of print() function
ret //returning control back to main()
...
Run Code Online (Sandbox Code Playgroud)
因此,即使((ABC *)0)->print();效果也很好.
| 归档时间: |
|
| 查看次数: |
8466 次 |
| 最近记录: |