动态内存访问仅适用于函数内部

Lun*_*din 18 c malloc parameter-passing pass-by-value dynamic-memory-allocation

此问题旨在用作此常见问题解答的规范副本:

我在一个函数内动态分配数据,一切运行良好,但只在分配发生的函数内部.当我尝试在函数外部使用相同的数据时,我会遇到崩溃或其他意外的程序行为.

这是一个MCVE:

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

void create_array (int* data, int size)
{
  data = malloc(sizeof(*data) * size);
  for(int i=0; i<size; i++)
  {
    data[i] = i;
  }

  print_array(data, size);
}

void print_array (int* data, int size)
{
  for(int i=0; i<size; i++)
  {
    printf("%d ", data[i]);
  }
  printf("\n");
}

int main (void)
{
  int* data;
  const int size = 5;

  create_array(data, size);
  print_array(data, size);  // crash here

  free(data);
}
Run Code Online (Sandbox Code Playgroud)

每当print_arraycreate_array函数内部调用时,我得到预期的输出0 1 2 3 4,但是当我调用它时main,我得到一个程序崩溃.

这是什么原因?

Lun*_*din 16

这个错误的原因是函数data使用的create_array是一个只存在于该函数内的局部变量.从中获取的分配的内存地址malloc仅存储在此本地变量中,并且永远不会返回给调用者.


考虑这个简单的例子:

void func (int x)
{
  x = 1;
  printf("%d", x);
}

...
int a;
func(a);
printf("%d", a); // bad, undefined behavior - the program might crash or print garbage
Run Code Online (Sandbox Code Playgroud)

这里,变量的副本a作为参数存储在函数内部x.这被称为pass-by-value.

何时x被修改,只有该局部变量被更改.a调用者中的变量保持不变,并且由于a未初始化,因此它将包含"垃圾"并且无法可靠地使用.


指针也不例外,这个按值传递规则.在您的示例中,指针变量data通过值传递给函数.该data函数内部指针是一个本地副本,并从指定的地址malloc是永远不会传回给调用者.

因此调用者中的指针变量保持未初始化状态,因此程序崩溃.此外,该create_array函数还创建了内存泄漏,因为在该函数执行之后,程序中不再有任何指针跟踪分配的内存块.


有两种方法可以修改函数以按预期工作.通过将局部变量的副本返回给调用者:

int* create_array (int size)
{
  int* data = malloc(sizeof(*data) * size);
  for(int i=0; i<size; i++)
  {
    data[i] = i;
  }

  print_array(data, size);

  return data;
}

int main (void)
{
  int* data;
  const int size = 5;

  data = create_array(size);
  print_array(data, size);
}
Run Code Online (Sandbox Code Playgroud)

或者通过将地址传递给调用者的指针变量并直接写入调用者变量:

void create_array (int** data, int size)
{
  int* tmp = malloc(sizeof(*tmp) * size);
  for(int i=0; i<size; i++)
  {
    tmp[i] = i;
  }

  *data = tmp;      
  print_array(*data, size);
}

int main (void)
{
  int* data;
  const int size = 5;

  create_array(&data, size);
  print_array(data, size);
}
Run Code Online (Sandbox Code Playgroud)

两种形式都没问题.