函数范围和指针

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;

ptrNULL,因此 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书,函数章节。