如何修改已传递给 C 函数的指针?

发布于 2025-01-09 00:34:31 字数 992 浏览 1 评论 0原文

因此,我有一些代码,类似于下面的代码,将结构添加到结构列表中:

void barPush(BarList * list,Bar * bar)
{
    // if there is no move to add, then we are done
    if (bar == NULL) return;//EMPTY_LIST;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // and set list to be equal to the new head of the list
    list = newNode; // This line works, but list only changes inside of this function
}

这些结构定义如下:

typedef struct Bar
{
    // this isn't too important
} Bar;

#define EMPTY_LIST NULL

typedef struct BarList
{
    Bar * val;
    struct  BarList * nextBar;
} BarList;

然后在另一个文件中我执行如下操作:

BarList * l;

l = EMPTY_LIST;
barPush(l,&b1); // b1 and b2 are just Bar's
barPush(l,&b2);

但是,在此之后, l 仍然指向到 EMPTY_LIST,而不是 barPush 内部创建的修改版本。如果我想修改它,我是否必须将列表作为指向指针的指针传递,或者是否需要其他一些黑暗咒语?

So, I have some code, kind of like the following, to add a struct to a list of structs:

void barPush(BarList * list,Bar * bar)
{
    // if there is no move to add, then we are done
    if (bar == NULL) return;//EMPTY_LIST;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // and set list to be equal to the new head of the list
    list = newNode; // This line works, but list only changes inside of this function
}

These structures are defined as follows:

typedef struct Bar
{
    // this isn't too important
} Bar;

#define EMPTY_LIST NULL

typedef struct BarList
{
    Bar * val;
    struct  BarList * nextBar;
} BarList;

and then in another file I do something like the following:

BarList * l;

l = EMPTY_LIST;
barPush(l,&b1); // b1 and b2 are just Bar's
barPush(l,&b2);

However, after this, l still points to EMPTY_LIST, not the modified version created inside of barPush. Do I have to pass list in as a pointer to a pointer if I want to modify it, or is there some other dark incantation required?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

夕色琉璃 2025-01-16 00:34:32

修改另一个函数中的指针需要一个称为多重间接的概念,我将在稍后解释它,鉴于@geofftnz使用多重间接,剧透解决方案。我想做的是尽力解释 C 中的多重间接。

考虑以下两个程序,我将逐步介绍代码。

以下程序未使用多重间接寻址,因此失败。

有错误的程序:

// filename: noIndirection.c
#include <stdio.h>
#include <stdlib.h>

void allocater(int *ptrTempAllctr)
{
    ptrTempAllctr = malloc(sizeof(int));
    if (ptrTempAllctr == NULL) {
        perror("in allocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main() 
{
    int *ptrMain = NULL;
    allocater(ptrMain);
    if (ptrMain == NULL) {
        printf("ptrMain is points to NULL\n");
        return 1;
    }
    //free(ptrMain);  // we don't have to free because it will be invalid free.
    return 0;
}

考虑上面的程序(noIndirection.c),它有一个变量ptrMain,它是一个指向 int 的指针。
如果它被传递给一个函数,在函数作用域(主体)中会创建一个临时指针变量,因为函数的参数是临时变量,当它们超出作用域时它们会被删除。

临时指针变量 ptrTempAllctr (它是一个参数)将指向调用者 (main) 函数的变量 ptrMain(它指向NULL) 在作为参数传递给函数时指向。

如果我们使用malloc()或将另一个指针分配给临时变量ptrTempAllctr,那么它将指向它,但caller(main ) 作为参数传递给函数 allocater() 的函数仍然指向函数调用之前所指向的相同数据(即 NULL)。

当被调用的 (allocater()) 函数超出范围时,临时指针变量将从堆栈中弹出,并且内存未分配,最终导致内存泄漏。
为了绕过这个限制,我们需要使用多重间接。

多重间接:

Multiple indirection when we use of pointer/s to pointer/s in varying level(with multiple `*`) eg: `int **pp, int ***ppp`, etc.

我们使用address-of(&)运算符来分配它们。

多个间接指针类型变量的作用是,允许我们做的是
指向指针变量本身的指针,用于修复上述程序。
这允许我们将 ptrMain 的地址传递给 allocater()
使用此调用

allocater(&ptrMain);

因此上述程序 noIndirection.c 不允许我们这样做,请参阅程序 withIndirection.c 来实现这种多重间接寻址。

在这种情况下,我们需要指向 int 指针(int **ptrMain)的指针作为 allocater() 函数的函数参数来解决上述错误程序(noIndirection.c)。

这在下面的程序中被使用。

下面的程序使用多重间接来解决前面程序中的错误。

// filename: withIndirection.c
#include <stdio.h>
#include <stdlib.h>

void trueAllocater(int **ptrTrueAllocater)
{
    *ptrTrueAllocater = (int *) malloc(sizeof(int));
    if (ptrTrueAllocater == NULL) {
        perror("in trueAllocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main(void) 
{
    int *ptrMain = NULL;
    trueAllocater(&ptrMain);
    if (ptrMain == NULL) {
        printf("memory not allocated\n");
        return EXIT_FAILURE;
    }

    printf("memory allocated and assigned to ptrMain");
    printf(" from trueAllocater\n");

    free(ptrMain);
    return EXIT_SUCCESS;
}

从现在开始,请参阅程序withIndirection.c以供参考。

为了解决我们的问题,我们需要将指针变量 ptrMain (trueAllocater(&ptrMain);) 的地址传递给 trueAllocater,以便更改 ptrMain 稍后需要在 trueAllocater() 或其他函数中指向它,
为此,函数需要接受具有正确间接级别的间接指针,
这是根据我当前对传递的变量的理解,在参数声明中添加另一个 * 。

也就是说,我们需要将 trueAllocater() 函数参数设置为 withIndirection.cint * 的 int ** > 与 noIndirection.c 相反
因此间接级别将得到满足。

当调用者的参数变量ptrMain的实际地址被传递给函数时。中的临时 ptrTrueAllocator 参数变量
函数指向调用者(main)函数中指针变量ptrMain的地址,而不是指针变量ptrMain(这是NULL)指向函数(main)中。

如果我们取消引用 ptrTrueAllocater 变量,则 ptrMain 指向的地址将被显示,因为 ptrTrueAllocater 临时变量指向调用者(main) ptrMain 变量本身不是它的内容。

解引用的 ptrTrueAllocater 变量的内容将是调用者 (main) 的变量 (ptrMain) 所指向的数据的地址,
因此我们必须进行额外的取消引用才能获得最终数据。

因此我们必须取消引用一次才能获取它指向的 ptrMain 的地址,以便更改需要指向的位置并取消引用
两次以获取 ptrMain 指向的实际数据,该数据为 NULL

@PaulWicks,您打算更改,因此您必须取消引用一次才能分配或更改其指向的位置。

使用指针进行多重间接寻址的目的是创建多维数组并传递需要指向某些内容的指针参数。

我们需要根据我们必须操作的类型来更改变量,如下所示,

声明中每次添加 * 都会增加指针间接级别
每次取消引用都会降低指针间接级别,即接近数据。

我们可以通过将地址返回给分配给所需指针变量的调用函数来解决这个问题。

是的,我们可以使用这种多重间接变量语法来创建一个或
多维数组。如果初学者花时间的话,一开始这会让他们感到困惑
阅读大量代码,他们将能够找到它们之间的区别。

如果我错了,请纠正我,请反馈并让我知道是什么
多重间接指针的其他用途。
为我糟糕的英语道歉。
这些资源帮助我理解多重间接。
https://boredzo.org/pointers/#function_pointers
https://cseweb.ucsd.edu/~ricko/rt_lt.rule.html< /a>

Modifying a pointer in another function requires a concept called multiple indirection, I will explain it in later, spoiler solution given @geofftnz uses multiple indirection. What I am trying to do is try my best to explain multiple indirection in C.

Consider the following two programs, I will walk through the code.

The following program does not use multiple indirection, so it fails.

program with error:

// filename: noIndirection.c
#include <stdio.h>
#include <stdlib.h>

void allocater(int *ptrTempAllctr)
{
    ptrTempAllctr = malloc(sizeof(int));
    if (ptrTempAllctr == NULL) {
        perror("in allocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main() 
{
    int *ptrMain = NULL;
    allocater(ptrMain);
    if (ptrMain == NULL) {
        printf("ptrMain is points to NULL\n");
        return 1;
    }
    //free(ptrMain);  // we don't have to free because it will be invalid free.
    return 0;
}

consider the above program(noIndirection.c), which has a variable ptrMain it is a pointer points to an int.
If it was passed to a function, in the function scope(body) a temporary pointer variable is created because arguments of the function are temporary variables, they get deleted when they go out of scope.

The temporary pointer variable ptrTempAllctr (which is a argument) will point to what ever the caller(main) function's variable ptrMain(which is pointing to NULL) pointed when it was passed as argument to the function.

If we use malloc() or assign another pointer to the temporary variable ptrTempAllctr then it will point to it but the pointer variable in caller(main) function that was passed as argument to the to function allocater() still points to the same data(which is NULL) which it was pointed before the function call.

When the called (allocater()) function goes out of scope the temporary pointer variable is popped from stack and the memory left unallocated we end up memory leak.
To bypass this limitation we need use multiple indirection.

MULTIPLE INDIRECTION:

Multiple indirection when we use of pointer/s to pointer/s in varying level(with multiple `*`) eg: `int **pp, int ***ppp`, etc.

and we assign them using address-of(&) operator.

what multiple indirection pointer type variables does is, allows us to make is
a pointer to a pointer variable itself for fixing the above program.
This allows us to pass the address of the ptrMain to allocater()
using this call

allocater(&ptrMain);

thus the above program noIndirection.c doesn't allow us do this, see the program withIndirection.c to implement this multiple indirection.

We need pointer to int pointer(int **ptrMain) as function argument for allocater() function in this case to solve the above buggy program(noIndirection.c).

This was used in the following program.

The following program uses multiple indirection to solve the bug in previous program.

// filename: withIndirection.c
#include <stdio.h>
#include <stdlib.h>

void trueAllocater(int **ptrTrueAllocater)
{
    *ptrTrueAllocater = (int *) malloc(sizeof(int));
    if (ptrTrueAllocater == NULL) {
        perror("in trueAllocater() memory allocation error");
        exit(EXIT_FAILURE);
    }
}

int main(void) 
{
    int *ptrMain = NULL;
    trueAllocater(&ptrMain);
    if (ptrMain == NULL) {
        printf("memory not allocated\n");
        return EXIT_FAILURE;
    }

    printf("memory allocated and assigned to ptrMain");
    printf(" from trueAllocater\n");

    free(ptrMain);
    return EXIT_SUCCESS;
}

see the program withIndirection.c for reference from now.

To solve our problem we need pass the address of the pointer variable ptrMain (trueAllocater(&ptrMain);) to the trueAllocater, in-order to change ptrMain where it needs to be pointing later on in trueAllocater() or another function,
to do this the function needs to accept indirection pointer with correct level of indirection,
which is to add another * added to the argument declaration to my current understanding for the variables that are passed.

By means we need to have the trueAllocater() function argument as int ** from int * in withIndirection.c as opposed to noIndirection.c
so the indirection level will be statisfied.

When the address of the caller's argument variable ptrMain's actual address was passed to the function. the temprary ptrTrueAllocater argument variable in
function is pointing to the address of pointer variable ptrMain's address in caller(main) function not what pointer variable ptrMain(which is NULL in the program) points to in function(main).

If we dereference the ptrTrueAllocater variable the address which ptrMain points to will be revealed because the ptrTrueAllocater temporary variable is pointing to the caller(main) ptrMain variable itself not it contents.

The contents of the dereferenced ptrTrueAllocater variable will be the address of the data which was pointed by the caller(main)'s variable(ptrMain),
so we have to do one additional dereference to get the final data.

so we have to dereference once to get the address of the ptrMain which it points in-order to change the where ptrMain needs to be pointed and dereference
twice to get the actual data pointed by the ptrMain which is NULL.

@PaulWicks you intented to change so you have to dereference once to allocate or change where its pointing.

The intent of multiple indirecting using pointers is to create multi-dimensional array and passing pointer arguments that need to be pointed to something.

We need to change the variable according to the types we have to manipulate like the following,

every addition of * in declaration will increase pointer indirection level
and every dereference will decrease the pointer indirection level that is will get close to data.

We can solve this problem by returning the address to the caller function assigning to the required pointer variable.

yes, we can use this multi indirection variable syntax for creating one or
multi dimensional arrays. This will confuse beginners at first, if they put time to
read lots of code they'll able to find the difference between them.

Please correct me if I am wrong, please give feedback and let me know what are
the other uses of multiple indirection pointers.
apologies for my bad english.
these are the resources helped me to understand multiple indirections.
https://boredzo.org/pointers/#function_pointers
https://cseweb.ucsd.edu/~ricko/rt_lt.rule.html

酸甜透明夹心 2025-01-16 00:34:32
main() {
    node *x;
    newNode(&x);

}

void newNode(node **a)
{
    *a = (node *)malloc(sizeof(node));
    //.........
}
main() {
    node *x;
    newNode(&x);

}

void newNode(node **a)
{
    *a = (node *)malloc(sizeof(node));
    //.........
}
和影子一齐双人舞 2025-01-16 00:34:31

如果你想这样做,你需要传入一个指向指针的指针。

void barPush(BarList ** list,Bar * bar)
{
    if (list == NULL) return; // need to pass in the pointer to your pointer to your list.

    // if there is no move to add, then we are done
    if (bar == NULL) return;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = *list;

    // and set the contents of the pointer to the pointer to the head of the list 
    // (ie: the pointer the the head of the list) to the new node.
    *list = newNode; 
}

然后像这样使用它:

BarList * l;

l = EMPTY_LIST;
barPush(&l,&b1); // b1 and b2 are just Bar's
barPush(&l,&b2);

Jonathan Leffler 建议在评论中返回列表的新头:

BarList *barPush(BarList *list,Bar *bar)
{
    // if there is no move to add, then we are done - return unmodified list.
    if (bar == NULL) return list;  

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // return the new head of the list.
    return newNode; 
}

用法变为:

BarList * l;

l = EMPTY_LIST;
l = barPush(l,&b1); // b1 and b2 are just Bar's
l = barPush(l,&b2);

You need to pass in a pointer to a pointer if you want to do this.

void barPush(BarList ** list,Bar * bar)
{
    if (list == NULL) return; // need to pass in the pointer to your pointer to your list.

    // if there is no move to add, then we are done
    if (bar == NULL) return;

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = *list;

    // and set the contents of the pointer to the pointer to the head of the list 
    // (ie: the pointer the the head of the list) to the new node.
    *list = newNode; 
}

Then use it like this:

BarList * l;

l = EMPTY_LIST;
barPush(&l,&b1); // b1 and b2 are just Bar's
barPush(&l,&b2);

Jonathan Leffler suggested returning the new head of the list in the comments:

BarList *barPush(BarList *list,Bar *bar)
{
    // if there is no move to add, then we are done - return unmodified list.
    if (bar == NULL) return list;  

    // allocate space for the new node
    BarList * newNode = malloc(sizeof(BarList));

    // assign the right values
    newNode->val = bar;
    newNode->nextBar = list;

    // return the new head of the list.
    return newNode; 
}

Usage becomes:

BarList * l;

l = EMPTY_LIST;
l = barPush(l,&b1); // b1 and b2 are just Bar's
l = barPush(l,&b2);
打小就很酷 2025-01-16 00:34:31

通用答案:传递一个指向您想要更改的内容的指针。

在这种情况下,它将是一个指向您要更改的指针的指针。

Generic answer: Pass a pointer to the thing you want to change.

In this case, it would be a pointer to the pointer you want to change.

我很坚强 2025-01-16 00:34:31

请记住,在 C 中,一切都是按值传递的。

您传递一个指针到一个指针,就像这样

int myFunction(int** param1, int** param2) {

// now I can change the ACTUAL pointer - kind of like passing a pointer by reference 

}

Remember, in C, EVERYTHING is passed by value.

You pass in a pointer to a pointer, like this

int myFunction(int** param1, int** param2) {

// now I can change the ACTUAL pointer - kind of like passing a pointer by reference 

}
一场春暖 2025-01-16 00:34:31

这是一个经典问题。要么返回分配的节点,要么使用指针的指针。在 C 中,您应该将指向 X 的指针传递给您想要修改 X 的函数。在这种情况下,由于您想要修改指针,因此应该将指针传递给指针。

This is a classic problem. Either return the allocated node or use a pointer of pointer. In C, you should pass a pointer to a X to a function where you want your X to be modified. In this case, since you want a pointer to be modified, you ought to pass a pointer to a pointer.

秋凉 2025-01-16 00:34:31

是的,你必须传入一个指向指针的指针。 C 按值传递参数,而不是按引用传递参数。

Yes, you have to pass in a pointer to the pointer. C passes arguments by value, not by reference.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文