将空指针传递给函数

3

这个变量num仍然为0。我怀疑是因为我传递了一个NULL指针,但我只能按照主函数(和参数)的要求进行操作。我应该如何调整辅助函数的内容以正确更新num?

int main(void) {
    int * num_results = NULL;
    int num = 4;
    set(num_results, num);
    printf(“%d\n”, *num_results);
    return 0;
}

void set(int * results,num){
    num_results = malloc(sizeof(int));
    *num_results = num;
}

3
C还是C++?它们是两种不同的语言,尤其在一种语言拥有可直接帮助解决问题的特性,而另一种语言缺少该特性的情况下,这尤为重要。 - Some programmer dude
然而,由于代码本身是非常简单的C语言,而且这也是你在标题中明确提到的,因此你应该搜索并研究如何在C语言中模拟按引用传递。 - Some programmer dude
你展示的代码无法编译 - 这使得很难有用地分解来解释出现了什么问题。 - Jonathan Leffler
当你说“变量num保持为0”时,你的意思是num_results,对吗?因为那是你要打印的内容。 - Mikael
5个回答

7
我猜测你的问题实际上是想通过将指针传递给函数set来将num_results更改为num,以下是你可能犯的几个错误:
1. 你可能已经在完整代码中使用了这些内容,但不要忘记包含#include <stdio.h>以使用printf()#include <stdlib.h>以使用malloc()。 2. 你的函数set()应该在main()之前声明。你可以通过在main()之前声明一个原型或简单地在main()之前定义set()函数。
现在让我们来看看你的解决方案:你想将num_results作为参数传递给一个函数,分配一些内存并将地址分配给num_results,然后更新值为num中的值。
当你将一个int *作为参数传递时,我猜你已经知道通过传递int,你只是在传递int变量中所包含的内容的副本。这与int *的工作方式相同,你没有传递对num_results的引用以便你可以更新指针的地址,而是传递了当前地址NULL副本,它不会被修改。可能会被修改的是当前地址内部的内容,但是当前地址是NULL,因此实际上不能被修改。
由于你想要分配内存并将其地址赋给num_results,所以你必须传递指向指针的指针,这样你就可以传递int*变量所保存的地址,并且你可以真正地改变它。
这样,您的函数应该是 void set(int ** results, int num),并且您应该使用 set(&num_results, num) 调用它,因此您正在传递对 num_results 的引用,您可以更改它指向的地址,当前为 NULL,然后是 malloc() 返回的地址。
您还需要更改函数的主体,因为现在您正在使用一个 int **,您想将新地址分配给 *results,因为 results == &num_results,并将 num 分配给 **results,因为 *results = num_results
希望我的解释不会很混乱,希望有人能通过另一个答案或编辑我的答案来更好地解释它。最终代码:
#include <stdio.h>
#include <stdlib.h>

void set(int ** results, int num){
    *results = malloc(sizeof(int));
    **results = num;
}

int main(void) {
    int * num_results = NULL;
    int num = 4;
    set(&num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

我还假设OP真的想在set函数内分配内存。 - Mikael

1
我该如何调整辅助函数的内容,以正确更新num?
这似乎是错误的问题,因为您的set()函数似乎旨在将num的值复制到其他地方,而不是更新num本身。
你面临的问题之一是在哪里复制它。目前,您为其动态分配了一些内存,并将其复制到那里。就这方面而言,这很好,但是分配的内存指针没有传递回调用者。这是因为所有C函数都通过值传递参数,并且特别是,您的main()通过值传递set()的第一个参数(类型为int *的值)。修改函数的副本不会影响其调用者的副本,这就是值传递的全部内容。
你可能忽略了一个简单的事实:动态分配并不是获得有效指针值的唯一方法,甚至不是最重要或最常见的方法。当你看到一个指针时,你不应该自动去找malloc()。你可以很容易地从数组中获得一个指针,但更重要的是,你可以通过应用取地址运算符(&)获得一个指针值。
为了使你的set()函数能够在不使用全局变量或更改其签名的情况下让其调用者看到其工作,调用者必须将一个有效的指向调用者可以访问的对象的指针作为第一个参数传递,例如它自己的本地变量之一。指针按值传递,所以函数会得到一个副本,但它是一个副本:它指向与调用者的指针相同的对象。因此,set()函数可以通过其指向对象的指针副本来修改指向的对象。
假设你的main()声明了自己的本地变量result,一个int。你认为它可以用&result做什么?

1
set(num_results, num);

set()中向results变量传递NULL时,当您为results分配内存时,NULL将被有效的内存地址所替换,但需要注意的是,它仅作为set()的本地变量保存在results变量中。 您应该将num_results的地址传递给set(),以便在main()中保留在set()中分配的内存,或者只需在主函数中为num_results分配内存,然后像下面一样将其传递给set()
#include <stdio.h>
int main() {
    int *num_results = NULL;
    int num = 4;
    num_results = malloc(sizeof(int));
    set(num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

void set(int *results,int num){
    *results = num;
}

另一个例子是:

#include <stdio.h>
#include <stdlib.h>
void set(int **r, int num);
int main() {
    int *num_results = NULL;
    int num = 4;
    /*num_results = malloc(sizeof(int));*/
    set(&num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

void set(int **results,int num){
    *results = malloc(sizeof(int));
    *(*results) = num;
}

0
在你的辅助函数中,你应该使用第一个参数名results而不是num_results。你的辅助函数应该像这样:
void set(int * results, int num){
    results = malloc(sizeof(int));
    *results = num;
}

0

你的代码无法编译。编译器错误消息会告诉你如何修复它们。这里有一个已经修复好的。

#include<stdlib.h>
#include<stdio.h>

int * num_results = NULL;

void set(int * results, int num){
    num_results = malloc(sizeof(int));
    *num_results = num;
}

int main(void) {
    int num = 4;
    set(num_results, num);
    printf("%d\n", *num_results);
    return 0;
}

为了学习,你可以在这里查看编译错误信息,并尝试自己修复它:https://segfault.stensal.com/a/XWfv8rxCJHVtAuRQ

免责声明:我建立stensal.io作为一个捕获内存问题的工具。


3
仅仅找到让代码运行的方法并不能帮助提问者理解他哪里出了错。 - Mikael
3
你说得对,原始代码无法编译。不清楚这样做是如何改进的,或者你的设计决策(例如忽略set()函数中的results参数)是如何合理化的。 - Jonathan Leffler
完全同意你的观点。 - stensal
请注意,在回答中展示工作站点并进行推广时,您需要披露自己的隶属关系。您可以将此信息添加到个人资料中,并应在每个答案中注明。请查看帮助中心获取更多信息(例如Promotion—如何避免成为垃圾邮件发送者)。 - Jonathan Leffler
是的,这是我的网站,今天刚发布。我看到很多人一直问为什么我的代码会导致seg-fault,而这些原因显然可以自动报告。非常感谢您的任何反馈。个人资料已更新。 - stensal
在您使用URL的帖子中,请包含免责声明,这会使事情更加清晰。但是不要专门寻找帖子来发布您的工具链接;此处也适用此帖子中的指南。 - Martijn Pieters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接