C++函数传参的初步探索
admin
2024-05-23 11:26:38
0次

       今日发掘一个愚蠢、有趣又头疼的bug,因函数传参引起。我设定了一个函数专门给对象赋值,犯了一个极大的错误,我将变量直接传给函数,这个函数执行结束后,对象内部变量直接全部初始化了。后来请师父来看,他发现了问题,这直接刷新我都函数传参的认知。所以赶紧补上这篇函数传参的知识,以C++源代码和汇编为例,简单探索C/C++传参。

目录

一、问题

1.1 问题描述

 1.2 解决方法

二、函数传参原理及方式

2.1 原理

2.2 方式

三、总结


一、问题

1.1 问题描述

        直接上代码

#include
#includeint global;
class Inner
{
public:int a;int c;
public:Inner() { a = 0; c = 0; }~Inner() { global = 10; }
};class Outter
{
public:Inner * m_inner;
public://Outter(Outter& outter) = delete;Outter() { m_inner = new Inner(); }~Outter() { if (m_inner) { delete m_inner; m_inner = NULL;} }
};
template
void test(T out)
{}
int main()
{Outter out;test(out);return 0;
}

        声明一个Outter变量,将变量以普通变量的身份传入test函数,等到函数执行后,out里原有的值全部没有了。这源自于函数执行后析构了变量。具体如下:

main 中,首先声明了一个 Outter 类型的变量 out,然后将它作为参数传递给了函数 test。函数 test 具有模板类型,因此可以接受任何类型的参数。当函数调用结束时,参数将被析构。因此,当 main 函数结束时,变量 out 将被析构,指针变量也将被初始化。

        为了看到这个析构过程,我使用IDA反汇编工具将exe转成汇编去看:

; Attributes: bp-based frame fpd=0F0h; void test(class Outter)
??$test@VOutter@@@@YAXVOutter@@@Z proc nearvar_28= qword ptr -28h
arg_0= qword ptr  10h
arg_8= qword ptr  18h; __unwind { // j___CxxFrameHandler3_0
mov     [rsp-8+arg_0], rcx
push    rbp
push    rdi
sub     rsp, 108h
lea     rbp, [rsp+20h]
mov     rdi, rsp
mov     ecx, 42h ; 'B'
mov     eax, 0CCCCCCCCh
rep stosd
mov     rcx, [rsp+110h+arg_8]
mov     [rbp+0F0h+var_28], 0FFFFFFFFFFFFFFFEh
lea     rcx, unk_140023028
call    j___CheckForDebuggerJustMyCode
nop
lea     rcx, [rbp+0F0h+arg_0] ; this
call    j_??1Outter@@QEAA@XZ ; Outter::~Outter(void)
lea     rsp, [rbp+0E8h]
pop     rdi
pop     rbp
retn
; } // starts at 1400118B0
??$test@VOutter@@@@YAXVOutter@@@Z endp

        这是test函数的汇编解释部分:

1. 初始化栈帧:通过push指令将rbp和rdi寄存器的值压入栈中,并使用sub挀令分配内存。

2. 调用j___CheckForDebuggerJustMyCode函数来检查调试器是否正在运行。

3. 调用Outter类的构造函数和__autoclassinit2函数来初始化Outter类的实例。

4. 调用test函数并传入Outter类实例。

5. 调用Outter类的析构函数销毁实例。

6. 检查栈中的变量并进行安全检查。

7. 恢复栈帧并返回。

        Outter的析构确实被调用了: j_??1Outter@@QEAA@XZ ; Outter::~Outter(void)

        可以总结以下了:传入给函数带指针对象的类对象,不传入它的引用或者指针,那么函数执行完析构参数时,会将其指针对象同样析构,这带来的影响是灾难的,原本初始化好的对象啪就这么没了。

 1.2 解决方法

        1. 可以禁止类的copy构造,函数接收到参数时会对执行拷贝构造,我们将其禁止,组织编译成功,coder就会修改,不会出现这样的问题。在类里加上这句禁止拷贝构造:

Outter(Outter& outter) = delete;

        2. 很简单,传入参数的引用即可:

template
void test(T& out)
{}

        3. 声明对象时直接创建对象的指针,避免对象在栈上。

Outter* out = new Outter(); 

二、函数传参原理及方式

2.1 原理

        C++函数接受参数主要有两种情况:传值和传引用。

        传值:函数会copy一份变量在函数作用域使用,不改变原参数。

3. 调用Outter类的构造函数和__autoclassinit2函数来初始化Outter类的实例。

4. 调用test函数并传入Outter类实例。

5. 调用Outter类的析构函数销毁实例。

        在传入参数前,确实对Outter进行了重构造,也印证了进行了copy。

        传引用:传入参数的地址,函数可以对其修改,引用作为函数参数时,引用本质上是对原变量的别名,并且在函数内部对引用进行的操作实际上是对原变量进行的操作。因此,引用参数在内存中与原变量存储在同一个位置。当然前面加const也可以禁止修改。一、中代码传引用时test函数部分汇编如下:

; Attributes: bp-based frame fpd=0D0h; void test(class Outter &)
??$test@VOutter@@@@YAXAEAVOutter@@@Z proc neararg_0= qword ptr  10h
arg_8= qword ptr  18hmov     [rsp-8+arg_0], rcx
push    rbp
push    rdi
sub     rsp, 0E8h
lea     rbp, [rsp+20h]
mov     rdi, rsp
mov     ecx, 3Ah ; ':'
mov     eax, 0CCCCCCCCh
rep stosd
mov     rcx, [rsp+0F0h+arg_8]
lea     rcx, unk_140023028
call    j___CheckForDebuggerJustMyCode
lea     rsp, [rbp+0C8h]
pop     rdi
pop     rbp
retn
??$test@VOutter@@@@YAXAEAVOutter@@@Z endp

这行代码读取了引用类型的参数(arg_8),并将其存储在 rcx 寄存器中。

mov rcx, [rsp+0F0h+arg_8]

        寄存器存储的是CPU中处理数据的临时存储单元,在函数内部对寄存器中的参数进行修改时,函数结束后会将寄存器中的值返回到调用函数中,并在调用函数中通过寄存器进行读取,以此实现对参数的修改。

        这个时原理层面的函数传参。

2.2 方式

  •         值传递:这是最常见的传递参数的方式,它实际上是把实参的值复制到形参。如果函数修改形参的值,不会影响实参。
  •         引用传递:引用是一个别名,把它传递给函数实际上是把实参的地址传递给函数,因此函数可以直接操作实参。
  •         指针传递:指针是一个存储地址的变量,如果把一个指针传递给函数,函数就可以通过指针来访问实参。

当函数以指针的形式接收参数时,传递的是该变量的地址。函数内部可以通过访问该地址上的值来更改实际变量的值,并且这个修改对于调用该函数的代码是可见的,因为修改的是实际变量。

  •         数组传递:C++不允许直接把数组作为参数传递给函数,但是可以把数组名作为指针传递给函数,函数可以通过指针来访问数组中的元素。

当向函数传递数组时,数组的首地址实际上被当作指针传递。因此,函数内部可以通过指针访问数组中的元素。但是,数组的大小需要通过另一种方式传递给函数,以便函数可以正确处理数组。

        这里不做过多赘述了。

三、总结

        一个传参引发的血案,这边给大家建议,简单的参数例如int、string等可以直接传。复杂的参数,比如类对象,建议传指针或引用。祝大家周末愉快!

相关内容

热门资讯

蜻蜓订购APP贵金属订购非法期...   蜻蜓订购APP以"投资平台"名义对外宣传,主打贵金属期货(订购)交易。该平台一方面强调自身"正规...
悟空订购APP非法期货交易欺骗...   近期,有投资者反映,“悟空订购APP”等平台以白银、贵金属订购为名,宣传所谓“高收益”“低门槛”...
小赢淘金APP期货投资骗局,平...   “小赢淘金”之流,最擅长的就是用“注册送现金”、“充值返券”的糖衣炮弹来引流。别天真地以为那是福...
八方淘金APP非法期货交易欺骗...   警惕!八方生财、每日淘金、小赢淘金、悟空订购、蜻蜓订购、斗金订购等APP均涉嫌非法期货交易!  ...
智易购APP贵金属订购交易骗局...   很多人看到“3元起投”“充值送福利”“操作简单”“随时提现”这类宣传,很容易把智易购APP当成普...
喜玛银庄APP商品订购投资骗局...   喜玛银庄APP白银、铂金“现货订购”骗局警示:无资质平台涉嫌非法期货交易,投资者爆仓后如何维权?...
吉鑫乐享APP炒贵金属订购交易...   近期,多名投资者反映在“吉鑫乐享APP”进行贵金属投资时遭遇严重亏损,甚至血本无归。看似正规的“...
易掘金APP虚假期货投资骗局,...   “易掘金”的APP它打着“高收益、灵活投资”的旗号,宣称通过“商品订购”就能轻松赚钱,吸引了不少...
在大吉订购APP订购交易亏损被...   近期,不少投资者反映在“大吉订购App”上投资白银、铂金等商品遭遇严重亏损。该平台宣称提供现货订...
铂银壹号APP商品订购交易亏损...   许多中小投资者误以为网上的“商品现货订购”是合规合法的理财渠道,殊不知这背后隐藏着巨大的合规风险...
兴鑫淘金APP贵金属订购交易亏...   白银、贵金属或其他商品类投资,除国家批准的正规期货交易场所及持牌期货公司外,市面上大量第三方交易...
铂银掘金APP无资质的违规平台...   铂银掘金等平台的投资者,往往是被“注册送现金”“充值返现券”“首单免亏”等广告话术吸引,进而下载...
八方淘金APP订购交易不合规,...   “八方淘金APP”“铂银掘金APP”“铂银壹号APP”等软件被指以“现货订购”为名,实际采用高杠...
在指尖订购APP订购商品亏损被...   指尖订购APP这类平台,表面是“订购交易”,实则是披着电商外衣的非法期货陷阱。绝大多数投资者被蒙...
在犀贝易购APP做商品订购交易...   市面上打着“现货投资”“贵金属交易”旗号的APP并不少,但真正合规、受监管的平台并不多。犀贝易购...
易达淘金APP虚假商品订购交易...   易达淘金APP存在严重的虚假宣传与违规交易行为。该平台打着“商品订购”的旗号,实则通过不合规的交...
大浪淘金APP白银订购亏损被骗...   大浪淘金APP最隐蔽的套路,在于其完全封闭的内盘交易系统。表面上,它宣称与国际贵金属市场接轨;实...
一品好购APP上订购白银出现亏...   一品好购APP原名“一品订购”,运营方为上海宝智未来信息技术有限公司。近期不少投资者反映在该平台...
九州订购APP贵金属订购投资骗...   九州订购APP正通过抖.音、快手、今日头条等平台大量投放投资广告,用“5元起投”“充值返现”“0...
坚锅淘金APP交易软件并不正规...   当前,市面上大量所谓的“现货商城订购”交易软件,早已偏离了正常的实物商品买卖,演变为高杠杆、高手...