动态内存访问只能在函数内部工作

21
这个问题旨在作为常见问题的规范副本使用:
我正在函数内动态分配数据,一切都运行正常,但只限于分配发生的函数内部。当我尝试在函数外部使用相同的数据时,程序会崩溃或出现其他意外的程序行为。
这里有一个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);
}

每当从create_array函数内部调用print_array时,我得到了期望的输出0 1 2 3 4,但是当我从main中调用它时,程序崩溃了。
这是什么原因?

5
差点因为你犯了如此愚蠢的错误而对你点踩 :) - Jean-François Fabre
1
@Jean-FrançoisFabre,不幸的是我还没有找到一种方法来使问题成为社区维基,只有答案可以。我已经联系了管理员,希望它能尽快转换为社区维基。 - Lundin
2
我认为可以更好地托管在文档 Beta 上。 - LPs
4
@RestlessC0bra 假阳性 = 工具错误 = 工具失灵。VS2015 是一款 C++ 编译器。在 C 模式下,它非常失灵,人人皆知。它不符合 C 标准,也不符合 1999 年以前的 C 标准,也不符合古老的 C90/ANSI 标准。它还因为 Microsoft 认为他们应该独自决定语言特性的好坏,而不是 C 标准委员会,所以著名地对完全正常的 C 代码发出警告。 - Lundin
1
@Lundin 如果可以的话,我会给你的评论提供悬赏奖励! - Matthieu
显示剩余2条评论
1个回答

19

这个bug的原因是create_array函数中使用的data变量是一个仅存在于该函数内部的局部变量。从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
在这里,变量a的一个副本作为参数x存储在函数内部。这被称为传值调用。
当修改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);
}

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

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);
}

任何一种形式都可以。


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