0

我有一个关于将 c 中的数组传递给函数的问题。

当我运行这个程序时,它给了我一个分段错误

int main()
{

  char **ptr= NULL;
  test(ptr);
  printf("%s", ptr[0]);
  return 0;
}

void test(char **ptr)
{
  ptr = (char **) malloc(sizeof(char *));
  ptr[0] = (char *) malloc(sizeof(char)*5);
  strcpy(ptr[0], "abc");
  return;

}

但这工作得很好

int main()
{

  char **ptr= NULL;
  test(&ptr);
  printf("%s", ptr[0]);
  return 0;
}

void test(char ***ptr)
{
  *ptr = (char **) malloc(sizeof(char *));
  *ptr[0] = (char *) malloc(sizeof(char)*5);
  strcpy(*ptr[0], "abc");
  return;

}

有人可以解释为什么吗?

4

5 回答 5

3

您正在ptr 按值传递参数;形式参数ptrin与实际参数intest不同的对象,因此更改 的值不会反映在 中。 ptrmaintest::ptrmain::ptr

因此,您需要将指针传递给ptrinto test,并且test需要取消引用该指针以写入正确的对象。

对于任何类型T,如果要修改函数参数的值,则需要执行以下操作:

void foo( T *param )
{
  *param = ...;
 }

 void bar( )
 {
   T obj;
   foo( &obj );
 }

在这种特殊情况下,Tchar **

于 2013-08-22T19:38:01.837 回答
2

在 C 中,当您将数组传递给函数时,数组“衰减”为指针。该函数接收指向数组第一个元素的指针。它不接收数组本身。在被调用函数中对指针分配所做的任何更改都不会反映在调用函数中。

在您的第一个示例中,test()接收字符串数组,并在函数内部更改该指针指向的内容。所以 的本地副本ptr,也就是NULL,在函数内部获得分配的内存,就像ptr[0]. 但是,这些更改是本地的test()。它们不会反映在调用函数中。当test()执行完毕并返回时,ptr调用函数中的值仍然是NULL。而且您有内存泄漏,因为无法访问在test().

为了使更改反映在调用函数中,您必须传递一个指向字符串数组的指针:因此&ptr调用中的 和定义中的三级指针test()。另一种更简单的方法是:

int main()
{

    char **ptr= NULL;
    ptr = test();
    printf("%s", ptr[0]);
    return 0;
}

char** test(void)
{
      char **ptr = (char **) malloc(sizeof(char *));
      ptr[0] = (char *) malloc(sizeof(char) * 5);
      strcpy(ptr[0], "abc");
      return ptr;

}

一个澄清:我说“对被调用函数中的指针分配所做的任何更改都不会反映在调用函数中。” 这与说“对数组元素的任何更改都不会反映在调用函数中”不同。考虑一下:

int main (void) {

    int array [] = { 0, 1, 2, 3, 4 };

    test1 (array);
    printf ("%d\n", *array);

    test2 (array);
    printf ("%d\n", *array);

    return 0;
}

void test1 (int* array) {

    int new_array[] = { 3, 4, 5, 6, 7 };
    array = new_array;
    return;
}

void test2 (int* array) {

    array [0] = 5;    // or *array = 5
    array [1] = 6;
    array [2] = 7;
    array [3] = 8;
    array [4] = 9;

    return;
}

输出:

0
5

这里test1()的变化是数组指针本身指向的位置,这不会反映在调用者中。所以在调用函数中,*test依然是0。在test2()中,变化的是数组元素,反映在调用者中。因此输出的差异:*test现在是 5。所有这些都使用静态而不是动态内存分配,但原理是相同的。

于 2013-08-22T19:44:07.240 回答
1

这是因为您希望您的函数不返回值,而是修改您已经在另一个范围内创建的变量。

您希望从您的函数中传递ptr并最终得到以下结构:

ptr -> b -> []

whereb是指向数组的指针[]

在第一个示例中,您不是在修改函数内部的外部 ptr,而是它的副本。在这种情况下,要获得所需的结构,您需要您的函数返回一个 char** 以便您可以返回分配的指针。而且您不需要传递原始 ptr。

另一方面,在第二个示例中,您将指针传递给外部 ptr,因此该函数将修改外部 ptr 使用的相同内存空间。

我希望这能有所帮助。

于 2013-08-22T18:22:47.707 回答
0

扩展来自@rcrmn 的答案。

让数据“向上”传递到调用结构是一个很好的程序设计。在这种情况下main(),需要释放它从测试中收到的存储。

请注意,此代码避免了原始问题中的所有双指针,并且仍然满足要求。这是良好编程的本质,编写清晰、简单的代码来完成工作。

#include <stdio.h>
char *test();
int main()
{
  char *ptr = test();
  printf("%s", ptr);
  free(ptr);
  return 0;
}

char *test()
{
  char *ptr = (char *) malloc(sizeof(char) * 5);
  strncpy(ptr, "abc", 3);
  return ptr;
}
于 2013-08-22T20:51:31.680 回答
0

考虑下面的代码以便理解

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test(char **ptr);

int main()
{
  char *ptr= NULL;
  test(&ptr);
  printf("%s\n", ptr);
  return 0;
}

void test(char **ptr)
{
  *ptr = malloc(sizeof(char *));
  strcpy(*ptr, "abc");
  return;
}

在函数定义中所做的任何事情都必须反映在主函数中。因此,如果要更改ptr变量,则必须传递ptr变量的地址。

ptr是单指针(char ptr)变量,我们需要传递指针(&ptr)变量的地址。

在函数定义中,我们接收指针变量的地址,所以参数必须是双指针的类型。

这个概念类似于引用调用。

您的代码中也发生了类似的事情。

您需要传递双指针的地址才能更改指针变量。

双指针的地址必须由函数定义中的三指针获取。

于 2013-08-22T18:27:57.867 回答