目录
一、提出一个问题
函数内部利用实参建立的变量的地址,与实参的地址一致吗?
二、探讨
通过两端代码进行。
代码一、
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的地址与其他三个不同。
总结
函数内部利用实参建立的变量的地址与实参地址是不一致的。在函数调用时,实参的值会被传递给形参,形参在函数内部被当作局部变量来使用。当函数内部使用实参创建变量时,这些变量会在函数栈帧中分配内存,并且拥有自己的地址。这意味着函数内部的变量与实参在内存中是独立的。
因此,在包含结构的函数中,采用直接取地址的防止,更符合规范
例题分析
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 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)大小的空间,拷贝实参的值)
在建立变量时,应该建立指针变量,得到实参的地址。而不是拷贝实参,去取临时变量的地址!
要得到实参的地址:建立实参指针类型,指针解引用找到实参,取实参地址。