我是否将数组所有权从库函数传递给调用者?

时间:2013-01-17 21:13:17

标签: c pointers malloc heap ownership

我是从Python来到C的。 Python有一种令人愉快的简单白手套方法来操纵字符串。我在C中使用的数组越多,我认为拥有某些功能的方便程度就越高。我不是每次需要执行特定操作时编写循环来执行此操作,而是决定创建一个库来执行此操作。

所以,假设我有一个库,调用看起来像这样:

char* new_array = meatSlicer(old_array, element_start);

我将指针传递给我想要更改的数组,期望指针返回,并指示要切片的元素。

如果meatSlicer(是的,我是一个糟糕命名的傻瓜)返回指向在切片器内本地生成的数组的指针,指针将是一个错误的指针。所以,在meatSlicer()内我有这个:

    ... manipulation before the below ...

    char *heap_the_Array;     /* put it on the heap to pass it back to caller */
    heap_the_Array = malloc((size + 1) * sizeof(char));

    int i;                
    for (i = 0; i <= (size + 1); i++){           /* make it so... again */

            heap_the_Array[i] = newArray[i];     /* newArray is the local */

    }

    return heap_the_Array;                       /* return pointer */

我的问题是,我是否正确地将所有权归还给调用者函数,以便它可以free()新数组?将指针传递给堆上的数组是否足够?

7 个答案:

答案 0 :(得分:5)

是的,将局部变量复制到malloc - 内存区域的效果很好。您可以使用memcpy调用替换循环以减小代码大小。写

memcpy(heap_the_Array, newArray, size+1);

而不是

int i;                
for (i = 0; i <= (size + 1); i++){           /* make it so... again */
        heap_the_Array[i] = newArray[i];     /* newArray is the local */
}

答案 1 :(得分:3)

是的,您正确地将所有权转移到来电者功能。 C程序员并不经常使用这种所有权方法,但它有时会发生(strdup和GNUish asprintf是众所周知的例子)。

您也可以从一开始就使用堆分配的数组,无需复制。

顺便说一下,在复制时,代码中存在一个错误:for (i = 0; i <= (size + 1); i++)的主体被调用size+2次。正如另一个答案暗示的那样,使用带有size参数的memcpy确实比自己做的更容易出错。

最后一件事:sizeof(char)在ANSI C中总是1,请不要乘以它。

答案 2 :(得分:2)

是的,你的meatSlicer已经完成了heap_the_Array,它现在属于调用者。所以调用者是现在唯一可以(并且必须)删除该数组的人,除非他将所有权转让给其他人。这种所有权的想法必须由你以某种方式定义,并且你必须与它保持一致,以避免麻烦并避免被内存泄漏怪物切掉。

答案 3 :(得分:1)

已经清楚地解释了所有权如何转移。但是,我想提出一种“更传统”的方法。你的代码看起来非常像我在Python中很乐意做的事情 - 但C不是Python(而Python不是C!),学习一门新语言的一部分就是学习“用这种语言完成的事情”。对于刚刚学习C的程序员来说,有一种趋势是跳到那里和所有地方调用malloc。尽量不这样做。

不是让函数分配数组,而是在调用代码中传入一个数组,该数组具有您要复制的内容的空间。然后返回你实际获得的数量,例如:

 TYPE new_array[some_size];
 int max_size = sizeof(new_array) / sizeof(new_array[0]);
 int actual_size;

 actual_size = meatSlicer(old_array, new_array, element_start, max_size);

如果你想使用malloc来创建new_array,那么你当然可以这样做:

 TYPE new_array = malloc(some_size * sizeof(TYPE));
 int max_size = some_size;
 int actual_size;

 if (new_array == NULL) panic();    // Do something useful here. 

 actual_size = meatSlicer(old_array, new_array, element_start, max_size);

 ... 

 free(new_array);

我更喜欢使用固定大小的数组,因为开销较少,而且“记得以后免费”的需要较少 - 后者是代码中的常见问题,特别是当代码变得更复杂时涉及多个调用级别 - 例如,如果在一个函数中分配多个项目,则需要记住如果稍后分配失败则清理第一个项目。让生活变得复杂......

答案 4 :(得分:0)

是的,只有堆栈分配的变量在函数返回时自动“释放”,并且您已在堆上正确分配了一个数组,并正确地返回了指向它的指针。

具体来说,指针变量heap_the_Array在堆栈上分配,并返回该值的副本。

答案 5 :(得分:0)

虽然你可以返回指向堆分配对象的指针,但这并不意味着你可以忘记终止字符数组的NULL:)。

它会工作,只是不要忘记在混合中添加尾部'\ 0',并且在完成后不要忘记free() char数组。

答案 6 :(得分:0)

假设您的数组是字符串,您可以用一行替换您的错误代码。

return strdup(newArray);