【问题标题】:How can I allocate memory and return it (via a pointer-parameter) to the calling function?如何分配内存并将其(通过指针参数)返回给调用函数?
【发布时间】:2010-11-26 18:22:54
【问题描述】:

我在几个不同的函数中有一些代码,看起来像这样:

void someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (data);

  useData (data);

  return 0;
}

someFunction ()useData () 在单独的模块(*.c 文件)中定义。

问题在于,虽然 malloc 工作正常,并且分配的内存在 someFunction 中可用,但一旦函数返回,相同的内存就无法使用。

程序的示例运行可见here,输出显示各种内存地址。

有人可以向我解释一下我在这里做错了什么,以及如何让这段代码正常工作吗?


编辑:所以看来我需要使用双指针来执行此操作 - 当我实际上需要 use 双指针时,我将如何做同样的事情?所以例如数据是

int **data = NULL; //used for 2D array

然后我需要在函数调用中使用三重指针吗?

【问题讨论】:

标签: c pointers malloc


【解决方案1】:

你想使用一个指向指针的指针:

void someFunction (int **data) {
  *data = malloc (sizeof (int));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (&data);

  useData (data);

  return 0;
}

为什么?好吧,您想在主函数中更改指针data。在 C 中,如果您想更改作为参数传入的某些内容(并让该更改显示在调用者的版本中),您必须传入一个指向您想要更改的任何内容的指针。在这种情况下,“你想要改变的东西”是一个指针——所以为了能够改变那个指针,你必须使用一个指向指针的指针......

请注意,除了您的主要问题之外,代码中还有另一个错误:sizeof(data) 为您提供了存储指针所需的字节数(在 32 位操作系统上为 4 个字节,在 64 位操作系统上为 8 个字节)位操作系统),而您确实想要存储 指针指向的内容所需的字节数(int,即在大多数操作系统上为 4 个字节)。因为通常sizeof(int *)>=sizeof(int),这可能不会引起问题,但需要注意。我已经在上面的代码中更正了这一点。

这里有一些关于指针的有用问题:

How do pointer to pointers work in C?

Uses for multiple levels of pointer dereferences?

【讨论】:

  • 我建议不要称它为“双指针”——太容易与“双指针”混淆。
  • “双指针”和“三指针”其实是很常见的术语
  • 感谢 cmets -- 尽管我同意“双指针”很常见,但我已将其删除以避免混淆。
  • 我个人会使用sizeof **data 而不是sizeof (int),但除此之外,这很重要。
【解决方案2】:

一个常见的陷阱,尤其是当您从 Java 迁移到 C/C++ 时

请记住,当您传递指针时,它是按值传递的,即复制指针的值。对指针指向的数据进行更改很有用,但是对指针本身的任何更改都是本地的,因为它是一个副本!

诀窍是使用通过引用传递指针,因为您想更改它,即 malloc 它等。

**pointer --> 会吓到菜鸟 C 程序员 ;)

【讨论】:

  • 您好,我不明白“按值传递”部分。指针是地址吗?那么当您将地址传递给函数时,它怎么可能是副本?
  • 指针是一种类型,其值为内存地址。我们使用这种指针类型来指向内存中的其他对象。假设您将指针 p{val: 0x1234} 传递给函数。 (记住指针也是对象,所以它们也有一个地址,比如 0x8888) - 该函数将获取指针并能够访问 0x1234 处的对象,但是指向该地址的指针与 0x8888 处的指针不同,它是一个副本,只是具有相同的价值! - 这类似于按值传递 int。它被复制了。
【解决方案3】:

如果要修改指针,则必须将指针传递给指针。

即。 :

void someFunction (int **data) {
  *data = malloc (sizeof (int)*ARRAY_SIZE);
}

编辑: 添加了 ARRAY_SIZE,在某些时候您必须知道要分配多少个整数。

【讨论】:

  • sizeof 应该是 sizeof(**data)(或者简单地说是 sizeof(int));这个错误已经在原始代码中。
  • 不要转换 malloc 的返回值 - faq.cprogramming.com/cgi-bin/…
【解决方案4】:

那是因为指针数据是按值传递给someFunction的。

int *data = NULL;
//data is passed by value here.
someFunction (data); 
//the memory allocated inside someFunction  is not available.

指向指针或返回分配的指针可以解决问题。

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (data));
}


int*  someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
return data;
}

【讨论】:

    【解决方案5】:

    someFunction() 将其参数作为 int*。因此,当您从 main() 调用它时,会创建您传递的值的副本。无论您在函数内部修改什么都是这个副本,因此更改不会反映在外部。正如其他人建议的那样,您可以使用 int** 来获取反映在数据中的更改。其他方法是从 someFunction() 返回 int*。

    【讨论】:

      【解决方案6】:

      除了使用双指针技术外,如果只有 1 个返回参数,则需要重写如下:

       int *someFunction () {
         return (int *) malloc (sizeof (int *));
       }
      

      并使用它:

       int *data = someFunction ();
      

      【讨论】:

      • 不,我这样做只是为了简化事情 - 无法退货。
      【解决方案7】:

      这是在函数中分配内存并通过参数返回指针的一般模式:

      void myAllocator (T **p, size_t count)
      {
        *p = malloc(sizeof **p * count);
      }
      ...
      void foo(void)
      {
        T *p = NULL;
        myAllocator(&p, 100);
        ...
      }
      

      另一种方法是让指针成为函数的返回值(我的首选方法):

      T *myAllocator (size_t count)
      {
        T *p = malloc(sizeof *p * count);
        return p;
      }
      ...
      void foo(void)
      {
        T *p = myAllocator(100);
        ...
      }
      

      关于内存管理的一些注意事项:

      1. 避免内存管理问题的最佳方法是避免内存管理;除非您真的需要,否则不要使用动态内存。
      2. 除非您使用早于 1989 ANSI 标准的实现,或者您打算将代码编译为 C++,否则不要转换 malloc() 的结果。如果您忘记包含 stdlib.h 或在范围内没有 malloc() 的原型,则强制转换返回值将抑制有价值的编译器诊断。
      3. 使用被分配对象的大小而不是数据类型的大小(即sizeof *p而不是sizeof (T));如果必须更改数据类型(例如从 int 到 long 或从 float 到 double),这将为您节省一些胃痛。它还使代码在 IMO 上的可读性更好。
      4. 将内存管理函数隔离在更高级别的分配和解除分配函数后面;这些不仅可以处理分配,还可以处理初始化和错误。

      【讨论】:

      • 双间接指针方法的一个优点是它可以返回超出通过/失败的状态信息,如果指针在无效时始终为null,则可以实现“按需分配” “语义。不幸的是,即使在所有指针都具有相同表示代码的平台上,也需要使用丑陋的 malloc 调用来防止笨拙的编译器被别名绊倒(理智的编译器应该认识到像 doSomething((void**)&ptr) 这样的调用可能会修改 ptr ) 即使ptr 不是void*)。
      【解决方案8】:

      在这里,您正在尝试修改指针,即从“data == Null”到“data == 0xabcd”您分配的其他一些内存。因此,要修改您需要传递数据地址的数据,即 &data。

      void someFunction (int **data) {
        *data = (int *) malloc (sizeof (int));
      }
      

      【讨论】:

        【解决方案9】:

        回复您在其中编辑的其他问题:

        '*' 表示指向某物的指针。所以 '**' 是一个指向某物的指针,'***' 是一个指向某物的指针的指针,等等。

        'int **data'(如果 data 不是函数参数)的通常解释是指向 int 数组列表的指针(例如,'int a [100][100]')。

        所以你需要首先分配你的 int 数组(为了简单起见,我直接调用 malloc()):

        data = (int**) malloc(arrayCount); //allocate a list of int pointers
        for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
           data [i] = (int*) malloc(arrayElemCount);
        

        【讨论】:

          【解决方案10】:

          我们可以分配一个新指针并返回它,而不是使用双指针,不需要传递双指针,因为它不会在函数中的任何地方使用。

          返回void *,因此可用于任何类型的分配。

          void *someFunction (size_t size) {
              return  malloc (size);
          }
          

          并将其用作:

          int *data = someFunction (sizeof(int));
          

          【讨论】:

            【解决方案11】:

            为简单起见,我把上面的单指针参数称为p 和双指针pp(指向p)。

            在一个函数中,p所指向的对象是可以改变的,改变出来的 功能。但是,如果 p 本身发生了变化,则该变化不会 离开功能。

            不幸的是,malloc 本身的性质,通常 改变 p.这就是为什么原始代码不起作用的原因。 校正(58)使用指向p的指针pp。在更正的 函数,p 改变了,但 pp 没有改变。这样就成功了。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-09-30
              • 2021-07-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-03
              相关资源
              最近更新 更多