这是从函数返回结构数组的正确方法吗?

时间:2011-05-19 14:57:08

标签: c memory-management struct malloc

正如标题所说(并建议),我是C的新手,我试图从函数中返回任意大小的结构数组。我选择使用malloc作为互联网上的某个人,他比我聪明,指出除非我分配到堆,否则当points_on_circle完成执行时数组将被销毁,并且无用的指针将会被退回。

我提交的代码曾经工作,但现在我在代码中越来越多地调用函数,我收到运行时错误./main: free(): invalid next size (normal): 0x0a00e380。我猜这是由我的黑客一起实现的数组/指针。

我还没有打电话给free,因为我正在构建的许多阵列需要在程序的整个生命周期中持续存在(我添加{{ 1}}调用剩余部分!)。

free()

我的突破性xy* points_on_circle(int amount, float radius) { xy* array = malloc(sizeof(xy) * amount); float space = (PI * 2) / amount; while (amount-- >= 0) { float theta = space * amount; array[amount].x = sin(theta) * radius; array[amount].y = cos(theta) * radius; } return array; } 结构定义如下:

xy

我如何调用该函数的示例如下:

typedef struct { float x; float y; } xy;

正确方向的指针将不胜感激。

7 个答案:

答案 0 :(得分:7)

在一个功能中分配内存并将其释放到另一个功能中充满了危险。

我会分配内存并将它(内存缓冲区)传递给函数,并带有一个参数,指示允许将多少结构写入缓冲区。

我见过有两个函数的API,一个用于获取所需内存,另一个用于在分配内存后实际获取数据。

[编辑]找到一个例子: http://msdn.microsoft.com/en-us/library/ms647005%28VS.85%29.aspx

答案 1 :(得分:6)

编辑:您正在正确地返回数组,但是......


请考虑使用1元素

制作数组
xy *outer_points = points_on_circle(1, 5.0);

功能内部发生了什么?
我们来看看......

  xy* array = malloc(sizeof(xy) * amount);

1元素分配空间。 OK!

  while (amount-- >= 0) {

1大于或等于0,因此循环执行(并且数量减少)
设置array[0]后,您将返回循环顶部

  while (amount-- >= 0) {

0大于或等于0,因此循环执行(并且数量减少)
您现在正在尝试设置array[-1],这是无效的,因为索引引用了数组之外的区域。

答案 2 :(得分:6)

我想说这个程序设计存在根本缺陷。首先,逻辑上一个正在进行计算的函数与内存分配无关,这是两个不同的东西。其次,除非分配内存的函数和释放内存的函数属于同一个程序模块,否则设计很糟糕,你可能会遇到内存泄漏。相反,请将分配留给调用者。

该代码还包含各种危险的做法。避免使用 - 和++运算符作为复杂表达式的一部分,它是导致错误的常见原因。您的代码看起来好像有一个致命的错误并且正在写出数组的界限,只是因为您正在与其他运算符混合。在C语言中从来没有任何理由这样做,所以不要这样做。

另一个危险的做法是依赖C的隐式类型转换,从int到float(平衡,又称“通常的算术转换”)。这段代码中的“PI”是什么?它是int,float还是double?代码的结果将因此而异。

这是我建议的(未经测试):

void get_points_on_circle (xy* buffer, size_t items, float radius)
{
  float space = (PI * 2.0f) / items;
  float theta;
  signed int i;

  for(i=items-1; i>=0; i--)
  {
    theta = space * i;

    buffer[i].x = sin(theta) * radius;
    buffer[i].y = cos(theta) * radius;
  }
}

答案 3 :(得分:3)

我想这个:

while (amount-- >= 0 ) {

应该是:

while ( --amount >= 0 ) {

考虑最初金额为零的情况。

答案 4 :(得分:2)

就我而言,你做的是正确的事,前提是你的来电者可以免费获得结果。

有些人喜欢在同一个地方(对称)进行内存分配和释放责任,即在你的功能之外。在这种情况下,您将传递预先分配的xy*作为参数,并在指针为空时返回缓冲区的大小:

int requiredSpace = points_on_circle(10, 10, NULL);
xy* myArray = (xy*)malloc(requiredSpace);
points_on_circle(10, 10, myArray);
free(myArray);

答案 5 :(得分:1)

你正在迭代一个元素。

您应该使用goes down to zero运算符:

while ( amount --> 0) {
    ...
}

在一个功能中分配记忆并在另一个功能中释放它就像给一个四岁的孩子一把枪。你不应该这样做。

答案 6 :(得分:1)

虽然您决定倒数和使用而不是使用临时值可以节省一些内存周期,但它在概念上更令人困惑。特别是在数学一直在这里的情况下,你只需要保存分数。

但这并不是你应该浪费少量时间的原因。这个问题是原因:你浪费了几个小时!这适用于即使是最有经验和最聪明的程序员。错误只是更复杂,超出了stackoverflow答案的范围!换句话说,彼得原则也适用于编码。

不要犯错,因为你获得了经验,你可以通过承担这些风险来挽救一两个循环。这就是为什么代码完成中的McConnell将Humility列为积极的程序员属性。

以下是您可能想到的解决方案:

xy* points_on_circle(int amount, float radius)
{
  xy* array = malloc(sizeof(xy) * amount);

  float space = (PI * 2) / amount;

  int index;

  for (index=0;index<amount;index++) {
    float theta = space * index;

    array[index].x = sin(theta) * radius;
    array[index].y = cos(theta) * radius;
  }

  return array;
}

如果你需要速度,你可以做的一件小事就是将theta设置为0,并且每次都添加'space',因为+必然比浮点数更便宜。

加速10倍或更多?

如果你需要认真的速度,来自answers.com的这个提示会给你10倍的改进,如果你做得对:

  

根据毕达哥拉斯定理,x2 + y2是   radius2。然后解决起来很简单   对于x或y,给定另一个   半径。你也不需要   计算整个圈子 - 你可以   计算一个象限,并生成   其他三个象限是对称的。   你的生成循环,为   例如,简单地从原点迭代到   半径为x乘以delta x,生成y,   并反映在其他三个   象限。你也可以计算   象限的一半,并使用两者   对称和反射生成   其他七个半象限。

当我12岁的时候,我觉得自己是热门的东西,在我的atari 800上用图形8中的sin和cos绘制一个圆圈。我的堂兄Marty(当时最近的工作形式是微软机器人)擦除了我的程序并实现了上述解决方案,如果我没记错的话,只在循环中使用加法,并在4秒而不是一分钟内绘制相同的圆圈!如果我没有受洗,我会在敬拜中屈服。太糟糕了我没有方便的代码,但我打赌一点点谷歌搜索会带来它。任何人?