[英]Is this the correct way to return an array of structs from a function?
正如标题所说(并建议),我是C的新手,我试图从函数中返回任意大小的结构数组。 我选择使用malloc
作为互联网上的某个人,比我聪明,指出除非我分配给堆,否则当points_on_circle
完成执行时,数组将被销毁,并返回一个无用的指针。
我提交的代码曾经工作,但现在我在我的代码中越来越多地调用函数,我得到一个运行时错误./main: free(): invalid next size (normal): 0x0a00e380
。 我猜这是由我的黑客一起实现的数组/指针。
我还没有free
调用,因为我正在构建的许多数组都需要在程序的整个生命周期中持续存在(我将向其余部分添加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;
我如何调用该函数的示例如下:
xy * outer_points = points_on_circle(360, 5.0);
for(;i<360;i++) {
//outer_points[i].x
//outer_points[i].y
}
可以理解正确方向的指针 。
在一个功能中分配内存并将其释放到另一个功能中充满了危险。
我将分配内存并将其(内存缓冲区)传递给函数,并带有一个参数,指示允许将多少结构写入缓冲区。
我见过有两个函数的API,一个用于获取所需内存,另一个用于在分配内存后实际获取数据。
[编辑]找到一个例子: http : //msdn.microsoft.com/en-us/library/ms647005%28VS.85%29.aspx
编辑: 您正在正确返回数组 ,但......
考虑你正在制作一个包含1
元素的数组
xy *outer_points = points_on_circle(1, 5.0);
功能内部会发生什么?
让我们检查 ...
xy* array = malloc(sizeof(xy) * amount);
为1
元素分配空间。 好!
while (amount-- >= 0) {
1
大于或等于0
因此循环执行(并且数量减少)
设置array[0]
,返回循环顶部
while (amount-- >= 0) {
0
大于或等于0
因此循环执行(并且数量减少)
您现在正在尝试设置array[-1]
,这是无效的,因为索引引用了数组之外的区域。
我会说这个程序设计存在根本缺陷。 首先,逻辑上一个正在进行计算的函数与内存分配无关,这是两个不同的东西。 其次,除非分配内存的函数和释放内存的函数属于同一个程序模块,否则设计很糟糕,你可能会遇到内存泄漏。 相反,将分配留给调用者。
该代码还包含各种危险的做法。 避免使用 - 和++运算符作为复杂表达式的一部分,它是导致错误的常见原因。 您的代码看起来好像有一个致命的错误并且正在写出数组的界限,只是因为您正在与其他运算符混合。 在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;
}
}
我认为这:
while (amount-- >= 0 ) {
应该:
while ( --amount >= 0 ) {
考虑最初金额为零的情况。
就我而言,你做的是正确的事情,当然你的呼叫者可以免费获得结果。
有些人喜欢在同一个地方(对称)进行内存分配和释放责任,即在你的功能之外。 在这种情况下,您将传递预先分配的xy*
作为参数,并在指针为null时返回缓冲区的大小:
int requiredSpace = points_on_circle(10, 10, NULL);
xy* myArray = (xy*)malloc(requiredSpace);
points_on_circle(10, 10, myArray);
free(myArray);
虽然您决定倒数和使用而不是使用临时值可以节省一些内存周期,但它在概念上更令人困惑。 特别是在数学一直在这里的情况下,你只需要保存分数。
但这并不是你应该浪费少量时间的原因。 这个问题是原因:你浪费了几个小时! 这适用于即使是最有经验和最聪明的程序员。 错误只是更复杂,超出了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',因为+必然比浮点数低*。
如果您需要严格的速度,来自answers.com的这个提示将为您提供10倍的改进,如果你做得对:
根据毕达哥拉斯定理,x2 + y2是radius2。 然后,对于x或y,求解另一个以及半径是简单的。 您也不需要计算整个圆圈 - 您可以计算一个象限,并通过对称生成其他三个象限。 例如,您生成循环将简单地从原点到半径迭代为x乘以delta x,生成y,并在其他三个象限中反映出来。 您还可以计算象限的一半,并使用对称和反射来生成其他七个半象限。
当我12岁的时候,我觉得自己很酷,在我的atari 800上用图形8中的sin和cos绘制一个圆圈。我的堂兄Marty(当时最近的工作形式是微软机器人)擦除了我的程序并实现了上述解决方案,仅使用了加法如果我没记错的话,在循环中,并在4秒而不是一分钟内绘制相同的圆圈! 如果我没有受洗,我会在敬拜中屈服。 太糟糕了我没有方便的代码,但我打赌一点点谷歌搜索会带来它。 任何人?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.