函数范围和指针
Function scope and Pointers
我不明白这个程序的行为。
根据我的理解,main() 中的指针应该具有局部变量的原始地址,该局部变量现在已被销毁。因为此时指针被分配了有效地址。
它应该只保留那个地址。为什么它会丢失?
这是程序
#include <stdio.h>
void fun(int* ptr)
{
int a = 5;
ptr = &a;
printf("address: %p, value: %d\n", &a, a);
// address: 0x7fff1aa00374, value: 5
printf("address: %p, value: %d\n", ptr, *ptr);
// address: 0x7fff1aa00374, value: 5
}
int main(void)
{
int* ptr = NULL;
fun(ptr);
printf("address: %p\n", ptr);
// address: (nil)
printf("address: %p\tvalue: %d\n", ptr, *ptr);
// Segmentation fault (core dumped)
return 0;
}
GCC 版本
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
这是因为你是按值传递,就像传递整数、字符等一样。你不能更改按值传递的参数。在这种情况下,值是地址。要实际更改 ptr
的地址,需要传递双指针 (int **ptr
),但这需要对代码进行更多更改。
为了更好地理解这里发生的事情,将 fun()
函数的 ptr
参数重命名为 fun_ptr
void fun(int* fun_ptr)
^^^^^^^
当你调用fun()
函数时
int* ptr = NULL;
fun(ptr);
参数 ptr
的值将分配给 fun()
函数参数 fun_ptr
。类似于这个
fun_ptr = ptr;
ptr
是 NULL
,因此 fun_ptr
将被分配 NULL
。您在 fun()
函数内的 fun_ptr
中所做的任何更改都将保留在 fun()
函数块中,因为函数定义中参数声明列表中的标识符范围具有块范围,当函数 returns.
如果要更改另一个函数中的指针变量,则必须将该指针变量的地址传递给调用函数。在您的程序上下文中,它应该是
void fun(int** ptr) {
int a = 5;
*ptr = &a;
// *ptr will be the pointer passed as argument i.e. assign anything
// to *ptr means you are making changes to pointer passed as argument
.....
.....
}
int main(void)
{
int* ptr = NULL;
fun(&ptr);
.....
.....
return 0;
}
即使在进行了这些更改之后,您的程序仍将具有未定义的行为,因为它正在访问其范围之外的局部变量地址。变量 a
是一个本地(自动)非静态变量,其生命周期仅限于其范围,即声明它的块。任何在其生命周期之外访问它的尝试都会导致未定义的行为。
要解决此问题,请将内存分配给指针 ptr
并将 a
的值分配给它。或者,您可以将 a
声明为 static
变量。静态变量的生命周期是程序的整个运行。你可以这样做:
#include <stdio.h>
#include <stdlib.h>
void fun1 (int** ptr) {
static int a = 5;
*ptr = &a;
printf("fun1(): address: %p, value: %d\n", (void *)&a, a);
printf("fun1(): address: %p, value: %d\n", (void *)*ptr, **ptr);
}
void fun2 (int** ptr) {
int a = 5;
*ptr = malloc (sizeof (int));
if (*ptr == NULL) {
fprintf (stderr, "Failed to allocate memory\n");
exit (EXIT_FAILURE);
}
**ptr = a;
printf("fun2(): address: %p, value: %d\n", (void *)&a, a);
printf("fun2(): address: %p, value: %d\n", (void *)*ptr, **ptr);
}
int main (void) {
int* ptr1 = NULL;
int* ptr2 = NULL;
fun1(&ptr1);
printf("main(): address: %p\n", (void *)ptr1);
printf("main(): address: %p, value: %d\n", (void *)ptr1, *ptr1);
fun2(&ptr2);
printf("main(): address: %p\n", (void *)ptr2);
printf("main(): address: %p, value: %d\n", (void *)ptr2, *ptr2);
// Once done with dynamic allocated memory, release it
free(ptr2);
return 0;
}
这里是一个修改过的例子:
#include <stdio.h>
void fun(int p)
{
p = 5;
printf("value: %d\n", p);
}
int main(void)
{
int p = 0;
fun(p);
printf("value: %d\n", p);
return 0;
}
如果您理解为什么这个修改后的示例打印 5
然后 0
,那么您也会理解为什么您的指针版本也不起作用。因为指针也是按值传递的。
如果你不明白为什么打印5
然后0
,那么你需要看一本初级的C书,函数章节。
我不明白这个程序的行为。
根据我的理解,main() 中的指针应该具有局部变量的原始地址,该局部变量现在已被销毁。因为此时指针被分配了有效地址。
它应该只保留那个地址。为什么它会丢失?
这是程序
#include <stdio.h>
void fun(int* ptr)
{
int a = 5;
ptr = &a;
printf("address: %p, value: %d\n", &a, a);
// address: 0x7fff1aa00374, value: 5
printf("address: %p, value: %d\n", ptr, *ptr);
// address: 0x7fff1aa00374, value: 5
}
int main(void)
{
int* ptr = NULL;
fun(ptr);
printf("address: %p\n", ptr);
// address: (nil)
printf("address: %p\tvalue: %d\n", ptr, *ptr);
// Segmentation fault (core dumped)
return 0;
}
GCC 版本
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
这是因为你是按值传递,就像传递整数、字符等一样。你不能更改按值传递的参数。在这种情况下,值是地址。要实际更改 ptr
的地址,需要传递双指针 (int **ptr
),但这需要对代码进行更多更改。
为了更好地理解这里发生的事情,将 fun()
函数的 ptr
参数重命名为 fun_ptr
void fun(int* fun_ptr)
^^^^^^^
当你调用fun()
函数时
int* ptr = NULL;
fun(ptr);
参数 ptr
的值将分配给 fun()
函数参数 fun_ptr
。类似于这个
fun_ptr = ptr;
ptr
是 NULL
,因此 fun_ptr
将被分配 NULL
。您在 fun()
函数内的 fun_ptr
中所做的任何更改都将保留在 fun()
函数块中,因为函数定义中参数声明列表中的标识符范围具有块范围,当函数 returns.
如果要更改另一个函数中的指针变量,则必须将该指针变量的地址传递给调用函数。在您的程序上下文中,它应该是
void fun(int** ptr) {
int a = 5;
*ptr = &a;
// *ptr will be the pointer passed as argument i.e. assign anything
// to *ptr means you are making changes to pointer passed as argument
.....
.....
}
int main(void)
{
int* ptr = NULL;
fun(&ptr);
.....
.....
return 0;
}
即使在进行了这些更改之后,您的程序仍将具有未定义的行为,因为它正在访问其范围之外的局部变量地址。变量 a
是一个本地(自动)非静态变量,其生命周期仅限于其范围,即声明它的块。任何在其生命周期之外访问它的尝试都会导致未定义的行为。
要解决此问题,请将内存分配给指针 ptr
并将 a
的值分配给它。或者,您可以将 a
声明为 static
变量。静态变量的生命周期是程序的整个运行。你可以这样做:
#include <stdio.h>
#include <stdlib.h>
void fun1 (int** ptr) {
static int a = 5;
*ptr = &a;
printf("fun1(): address: %p, value: %d\n", (void *)&a, a);
printf("fun1(): address: %p, value: %d\n", (void *)*ptr, **ptr);
}
void fun2 (int** ptr) {
int a = 5;
*ptr = malloc (sizeof (int));
if (*ptr == NULL) {
fprintf (stderr, "Failed to allocate memory\n");
exit (EXIT_FAILURE);
}
**ptr = a;
printf("fun2(): address: %p, value: %d\n", (void *)&a, a);
printf("fun2(): address: %p, value: %d\n", (void *)*ptr, **ptr);
}
int main (void) {
int* ptr1 = NULL;
int* ptr2 = NULL;
fun1(&ptr1);
printf("main(): address: %p\n", (void *)ptr1);
printf("main(): address: %p, value: %d\n", (void *)ptr1, *ptr1);
fun2(&ptr2);
printf("main(): address: %p\n", (void *)ptr2);
printf("main(): address: %p, value: %d\n", (void *)ptr2, *ptr2);
// Once done with dynamic allocated memory, release it
free(ptr2);
return 0;
}
这里是一个修改过的例子:
#include <stdio.h>
void fun(int p)
{
p = 5;
printf("value: %d\n", p);
}
int main(void)
{
int p = 0;
fun(p);
printf("value: %d\n", p);
return 0;
}
如果您理解为什么这个修改后的示例打印 5
然后 0
,那么您也会理解为什么您的指针版本也不起作用。因为指针也是按值传递的。
如果你不明白为什么打印5
然后0
,那么你需要看一本初级的C书,函数章节。