在C语言中如何从一个函数返回多个值?

3

我的原始问题是,我想编写一个函数,可以返回两个值。我知道可以通过将这两个参数的地址传递给函数,并在函数内直接计算它们的值来实现。但是在进行实验时,发生了一些奇怪的事情。在函数内获取的值无法传递到主函数:

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

void build(char *ch){
   ch = malloc(30*sizeof(char));
   strcpy(ch, "I am a good guy");
}

void main(){
   char *cm;
   build(cm);
   printf("%s\n", cm);
}

上述程序只是打印出一些垃圾信息。所以我想知道这里有什么问题。最终,我想要像这样的东西parse(char **argv, char **cmd1, char **cmd2),它可以从原始命令argv中解析出两个命令。如果有人能稍微解释一下就太好了。非常感谢。


char **ch... ... *ch = malloc... ... strcpy(*ch... ... build(&cm) ...char **ch... ... *ch = malloc... ... strcpy(*ch... ... build(&cm) ... - ta.speot.is
你需要使用指向指针(Pointer to pointer)。 - Grijesh Chauhan
这样想吧;如果一个函数需要一个参数 n,并且该函数想要给 n 赋新值 (n = ...),那么就需要一定程度的间接性,也就是说,你需要一个指向 n 的指针 (typeof_n*)。在 C 中,所有的函数参数都是按值传递的,也就是说会进行复制。因此,由于你传入了一个 char*,你需要一个指向其中一个的指针,也就是一个 char**。然后你可以写 *n = malloc(size);。另外,sizeof char 被定义为 1,所以在你的 malloc 调用中不需要它。 - Ed S.
3个回答

5

build() 使用指针ch 的值,即将指针的副本传递给函数。因此,当函数退出时,您对该值所做的任何修改都会丢失。由于您希望在调用者的上下文中看到指针的修改,因此需要传递指向指针的指针。

void build(char **ch){
   *ch = malloc(30*sizeof(char));
   strcpy(*ch, "I am a good guy");
}

此外,您不需要传递指针给函数以返回多个值。另一个选择是创建一个包含您想要作为成员返回的值的struct,然后返回该struct的实例。如果这些值相关并且将它们打包在一起是有意义的,我建议使用此方法而不是第一个方法。
以下是修复错误后代码的重新列出:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void build(char **ch){
   *ch = malloc(30 * sizeof(char));
   strcpy(*ch, "I am a good guy");
}

int main() {     // main must return int
   char *cm;
   build(&cm);   // pass pointer to pointer
   printf("%s\n", cm);
   free(cm);     // free allocated memory
   return 0;     // main's return value, not required C99 onward
}

可能有助于指出用法:build(&cm); - ta.speot.is
1
// main函数必须返回void。我相信你的意思是// main函数必须返回int。 - Ed S.

1

如果你想在函数内部使用malloc,你需要将地址传递给外部指针,因为函数内部的ch只是一个局部变量,改变它不会影响外部的cm变量。

void build(char **ch){
   *ch = malloc(30*sizeof(char));
   strcpy(*ch, "I am a good guy");
}

void main(){
   char *cm;
   build(&cm);
   printf("%s\n", cm);
}

但最好不要在函数内使用malloc,而是直接写入指针所指向的区域。这是在C语言中提供缓冲区以获取数据的常见方法。这样用户就有选择自己分配内存,或者只使用本地缓冲区,之后不需要像你在示例中忘记释放内存一样释放内存。

void build(char *ch){
   strcpy(ch, "I am a good guy");
}

void main(){
   char *cm1;
   cm1 = malloc(30*sizeof(char));
   build(cm1);
   printf("%s\n", cm1);

   char cm2[30];
   build(cm2);

   printf("%s\n", cm2);   
   free(cm1);           // don't forget this
}

哇,非常感谢。这确实是一种干净的方式。但是您能详细说明为什么我们可以通过值而不是地址传递cm1和cm2吗?谢谢。 - J Freebird
正如我所说,我们想要处理数据而不是指针,因此我们将地址传递给缓冲区/数据,而不是允许函数修改指针。调用者通常声明一个本地数组,而不是调用malloc,因为它更快,并且在作用域结束后自动结束其生命周期,减少了忘记释放内存的机会,就像你的情况一样。你看,memcpy或strcpy也是这种情况的例子。它接受数据的指针,而不是指针的地址。 - phuclv

0

这是因为在C语言中,指针的内存地址是按值传递的,所以你不能重新分配指针的内存地址。

如果你真的想这样做,你必须将指针的地址传递到构建函数中。

参见:如何在C语言中通过引用传递指针参数?


1
你不能重新分配指针所指向的内存地址的值,因为它是按值传递的。” - 当然可以。不过您无法对指针本身分配新值并期望调用者能看到它。由于您已经拥有必要的间接级别,您可以将新值分配给指针所指向的内容。” - Ed S.
我想我表达不太准确;我的意思是,由于指针是按值传递的,所以你不能使指针指向不同的内存地址。当然,你可以改变值,这就是使用指针的全部意义(双关语)。 - George
那是正确的。编辑那一部分,我会取消我的反对票。 - Ed S.
完成。 - George

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