指针与栈帧,谈形参、实参与栈帧的关联

文章探讨了函数内部利用实参创建变量时,这些变量地址与实参地址的区别,解释了栈帧中内存分配的过程。并通过实例说明了如何正确使用指针获取实参地址。此外,文章还给出了用队列实现后进先出栈的示例,强调了在实现特定操作时的注意事项。
摘要由CSDN通过智能技术生成

目录

一、提出一个问题

二、探讨        

代码一、

原因:

代码二、

原因:

总结

例题分析


一、提出一个问题

函数内部利用实参建立的变量的地址,与实参的地址一致吗?

二、探讨        

通过两端代码进行。

代码一、


int* test(int a)		
{
	int m = a;
	printf("m      %p\n", &m);			//也就是说,地址独立
	printf("形参a  %p\n", &a);
	return &m;
}

int main()
{
	int a = 10;
	test(a);

	printf("实参a  %p\n", &a);

	return 0;
}

运行结果

可以看到,实参 、  形参  、 利用实参的值新建立的变量m,都有不同的地址。

原因:

在函数栈帧中,使用空间的时候,会不断为空开开辟属于自己的栈帧。

最开始,会给实参a开辟属于自己的栈空间。

调用函数test时,此时需要建立形参接收实参,会在栈给形参a开辟空间。

当利用实参的值建立新的变量m时,又会给变量m开辟空间( 开辟的大小是sizeof(int) )。

((     需要注意的是,m只是存储的 实参a 的数值, 当新建变量时,都会额外开辟出新的空间。此时 &取地址 新的变量 ,得到的地址都不是实参的地址!     ))

对于栈区而言,绝大多数编译器实现的是,先使用高地址,再使用低地址,因此才会出现以上地址分布。

代码二、


int* test(int a)		
{
	int m = a;
	printf("m      %p\n", &m);			//也就是说,地址独立
	printf("形参a  %p\n", &a);
	return &m;
}

int main()
{
	int a = 10;
	test(a);

	printf("实参a  %p\n", &a);

	return 0;
}




typedef struct m
{
	int a;

}m;


int* test(m* m1)
{
	int* pm = &m1->a;

	int m2 = m1->a;

	printf("&m1->a  %p\n", &m1->a);
	printf("pm      %p\n", pm);
	printf("\n\n\n&m2     %p\n", &m2);		//是因为m2是一个独立的int类型变量,在函数栈帧中,需要为其开辟独立的空间。pm直接存储的是m1->a 的地址(与实参的地址相同)
}


int main()
{

	m* m1;		//不对,m1 没有指向有效空间,是野指针。指针建立一定要初始化

	m1 = (m*)malloc(sizeof(m));

	test(m1);
	printf("实参    %p\n", &m1->a);

	return 0;
}

结果

可以观察到 只有 &m2 的地址与其他三者不同。

原因:

虽然m1 是形参,但是却是指针类型,由于 -> 解引用的出现,使其找到了实参。

所以pm与实参地址相同。

对于m2而言,m2是一个新的变量,会为其单独开辟空间,表达式只是将数值赋给了m2,但是m2具有独立的空间,因此&m2的地址与其他三个不同。

总结

函数内部利用实参建立的变量的地址与实参地址是不一致的。在函数调用时,实参的值会被传递给形参,形参在函数内部被当作局部变量来使用。当函数内部使用实参创建变量时,这些变量会在函数栈帧中分配内存,并且拥有自己的地址。这意味着函数内部的变量与实参在内存中是独立的。

因此,在包含结构的函数中,采用直接取地址的防止,更符合规范

例题分析

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

在实现Pop功能时


int myStackPop(MyStack* obj) {          //返回Pop的值

   

    Q* E_Queue = &obj->q1;   //假设q1是空链表

    Q* None_E_Queue = &obj->q2;  



    if (!QueueEmpty(&obj->q1))  //肯定一个为空、一个不为空

    {

       E_Queue = &obj->q2;

       None_E_Queue = &obj->q1;

    }



    while (QueueSize(None_E_Queue) > 1)        //QueueSize     :  元素个数

    {



        QueuePush(E_Queue, QueueFront(None_E_Queue));     //将头元素传递

        QueuePop(None_E_Queue);



    }



    QDatetype top = QueueFront(None_E_Queue);

    QueuePop(None_E_Queue);



    return top;



}

其中

    Q* E_Queue = &obj->q1;   //假设q1是空链表

    Q* None_E_Queue = &obj->q2;  

(若写作Q E_Queue = obj->q1;则新建的Q类型的此变量,会额外开辟sizeof(Q)大小的空间,拷贝实参的值)

在建立变量时,应该建立指针变量,得到实参的地址。而不是拷贝实参,去取临时变量的地址!

要得到实参的地址:建立实参指针类型,指针解引用找到实参,取实参地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值