在C语言中,将char*和char**作为参数传递给函数的区别是什么?

9
我已经阅读了几篇关于在C语言中传递char *的讨论。 stackoverflow: 在C中将字符串数组作为参数传递给函数 stackoverflow: 指向指针数组的数组是如何工作的 stackoverflow: 你最讨厌程序员无知的事情是什么 drexel.edu: 字符数组 其中很多都涉及到数组的讨论,但我想避开这个话题。
我正在编写一个示例程序,以便学习在C语言中传递char *char **。这是一个关于传递char *的练习,不使用(指向)数组。同时无需考虑执行效率。 :-)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void get_args_works(int, char **, char **);
void get_args_broken(int, char **, char *);
char *get_string(int, char **);

int main(int argc, char **argv)
{
  char *string_works;
  char *string_broken;

  get_args_works(argc, argv, &string_works);
  get_args_broken(argc, argv, string_broken);

  printf("in main string_works (%p) = %s\n",string_works,string_works);
  free(string_works);

  printf("in main string_broken (%p) = %s\n",string_broken,string_broken);
  free(string_broken);
}

void get_args_works(int argc, char **argv, char **string)
{
    *string = get_string(argc, argv);
    printf("in get_args_works %p string %s\n",*string,*string);
}

void get_args_broken(int argc, char **argv, char *string)
{
  string = get_string(argc, argv);
  printf("in get_args_broken %p string %s\n",string,string);
}

char * get_string(int argc, char **argv)
{
  int i;
  char *string;
  string = malloc(40);

  // placeholder in case -s switch not found below
  strcpy(string,"-s switch not found below");

  for(i = 0; i < argc; i++)
    {
      if(argv[i][0] == '-')
        {
          switch(argv[i][1])
            {
            case 's':
              // release above malloc(40) for "-s switch not found below"
              free(string);
              // make room for storing variable
              string = malloc(strlen(argv[++i]) + 1);
              // the argv just after -s
              strcpy (string,argv[i]);
              break;
            }
        }
    }
  return string;
}

你也可以在github上查看同样的代码 上面的代码有一定自解释性。main()声明了两个char *变量,并将它们作为参数传递给各自的get_args()函数。
每个get_args()函数调用char * get_string(int, char **),使用完全相同的调用(但不同的收集返回值的方法)。 get_string()运行良好;它执行malloc()并将指针返回到调用函数。这段代码运行良好,每个get_args()函数都按我预期接收返回值。
但是,当get_args()函数返回到main()时,为什么指针的解除引用值从get_args_works()返回到主函数,但指针的值从get_args_broken()中并没有返回?
我可以看到,如果我在发送参数时取消引用指针(&string_works),它就会起作用。 但是为什么?char * string_broken不已经是一个指针了吗?为什么在发送参数时需要“额外”的取消引用呢?
我希望得到一个成功的答案,解释如何将char *作为参数发送和作为函数返回值接收的概念化方式。
2个回答

4
int get_args_broken(int argc, char **argv, char *string)
{
  string = get_string(argc, argv);
  printf("in get_args_broken %p string %s\n",string,string);
}

你只是修改了局部变量 string,这对调用者来说是不可见的。请注意,这意味着你在主函数中释放了一个野指针。
由于同样的原因,这是错误的:
int get_sum(int sum, int a, int b)
{
  sum = a + b;
}

这个参数是按值进行复制的。此外,你没有返回一个整数(如你声明的那样)。

int get_args_works(int argc, char **argv, char **string)
{
    *string = get_string(argc, argv);
    printf("in get_args_works %p string %s\n",*string,*string);
}

这是正确的(除了缺失的返回值)。你没有修改string,因为那样毫无意义。你修改的是在string中的位置上的对象在这种情况下是一个char *

编辑:如果有一个调用main函数的函数,并且您想将该函数的变量设置为不同的char **,则需要三个*的argv。例如:

void trip_main(int *argc, char ***argv)
{
  *argc = 10; 
  *argv = malloc(*argc * sizeof(char *));
}

void caller()
{
  char **argv;
  int argc;
  trip_main(&argc, &argv);
}

嗯,我想这可能是一个好的简单理解方式。它没有以任何方式涉及char *。但是为什么我们不需要将argv三重指针?嗯嗯。 - Thunder Rabbit
哎呀!!!我怎么会错过无效的返回类型??? 我已经为此问题写了好几天了……!!! 唉!!! - Thunder Rabbit
2
@ThunderRabbit:是时候学习如何从编译器中打开更多警告,或者获取一个更好的编译器了。如果您的编译器没有抱怨声明返回值但实际上没有返回值的函数,那么它可能不是完全有问题,但并不像它应该的那样有用。编译器警告是您的朋友-听取它的建议,并确保它是冗长的。对于GCC来说,一个很好的起点是“-Wall”;我通常使用“-Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition”(最后一个是因为我在处理古老的代码)。'-Wshadow'也很好用... - Jonathan Leffler
谢谢,Jon;我正在使用gcc,所以我一定会查看-W选项。 - Thunder Rabbit

1

使用指向指针(这里是get_args_works())的需求之一是从函数中修改(或返回)多个变量,因为在C语言中无法返回多个变量。

get_args_works()之所以有效,是因为您传递了指向指针和对其的引用存在于main()中。

但是,在get_args_broken()中,您只传递了一个指针。没有问题,现在您执行malloc()并将分配的内存字符串返回给get_args_broken(),仍然没有问题。但是,此时分配的内存字符串是本地的,并且main()没有对此变量的引用。因此,当您在main()中取消引用char *string_broken;时,可能会导致未定义的行为。

希望这清楚了。


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