我该如何在C库中返回二元运算函数的结果?

时间:2012-06-11 18:00:52

标签: c performance memory-management

我正在研究一个C库,其中一部分涉及一些数学类型并对它们进行操作。每种类型都有一个工厂构造函数/析构函数,可以动态分配和释放它们。例如:

/* Example type, but illustrates situation very well. */
typdef struct {
    float x;
    float y;
    float z;
} Vector3D;

/* Constructor */
Vector* Vector3D_new(float x, float y, float z) {
     Vector3D* vector = (Vector3D*) malloc(sizeof(Vector3D));
     /* Initialization code here...*/

     return vector;
}

/* Destructor */
void Vector3D_destroy(Vector3D* vector) {
    free(vector);
}

尼斯&简单,并且还减轻了用户正确初始化的负担。

现在我主要关注的是如何处理对这些类型进行操作的函数(特别是如何返回结果值。)几乎每个二进制操作都会产生一个相同类型的新实例,因此,我需要考虑如何把这个还给用户。我可以通过 value 返回内容,但是传递指针会更受欢迎,因为它更快,与构造/析构函数方法兼容,并且不会给用户带来太多负担。

目前我通过让函数动态分配结果,然后返回指向它的指针来实现它:

/* Perform an operation, and dynamically return resultant vector */
Vector3D* addVectors(Vector3D* a, Vector3D* b) {
    Vector3D* c = Vector3D_new(
        a->x + b->x,
        a->y + b->y,
        a->z + b->z);

    return c; 
}

通过将值直接返回给用户,它具有能够链接的优点(例如,作为参数直接传递到另一个函数),例如:

/* Given three Vector3D*s : a, b, & c */
float dot = dotProduct(crossProduct(a, addVectors(b, c));

但是考虑到当前的方法,这会导致内存泄漏,因为addVectors()的结果将直接传递给crossProduct(),并且用户将没有机会{{1它(和传递到free()的{​​{1}}结果相同)。为了使这个工作,一个人必须创建一个指针来保存值,使用它,然后通过所述指针crossProduct()

dotProduct()

这样可行,但不那么直观,并且失去了我所希望的链接效果。

另一种可能性是让操作函数有3个参数;两个用于操作数,一个用于存储结果,但同样不是很直观。

我的问题是:什么是优雅& 高效在二进制操作中使用动态内存的方法?作为奖励,已经在现实世界图书馆中使用的解决方案将非常酷。有任何想法吗? :)

3 个答案:

答案 0 :(得分:2)

除了您提到的内存泄漏之外,您当前系统还存在一些其他问题:

  
      
  • 分配到堆显着比普通堆栈操作慢。
  •   
  • 每个分配也需要free() d,这意味着每个实例都需要至少 2 函数调用,其中只使用基于堆栈的设计将不需要。
  •   
  • 由于必须手动管理内存,因此会留下更多内存泄漏的空间。
  •   
  • 内存分配可能会失败!基于堆栈的系统可以减轻这种情况。
  •   
  • 使用指针需要解除引用。这比直接访问慢,并且需要更多(也许是草率)sytax。
  •   

除此之外,许多编译器缓存用于程序堆栈的内存,并且可以在堆上提供显着的改进(几乎从不缓存(如果可能的话!))

简而言之,只需依靠堆栈就可以获得最佳性能,不仅仅是性能,还有维护和清洁代码。唯一要记住的是堆栈有限的,它也可能很容易变得疯狂。将堆栈用于短期数据(在这种情况下为二进制运算结果),堆栈用于较长期的数据。

我希望这有帮助! :)

注意:此答案中的大部分信息都归功于@ Justin

答案 1 :(得分:1)

在操作员内部分配并不像看起来那么方便 这主要是因为您没有垃圾收集,也因为您不得不担心分配失败。

考虑以下代码:

Vector3D *v1,*v2,*v3;
Vector3d v4 = addVectors(v1,multiplyVectors(v2,v3));

看起来不错。
但是从multiplyVectors返回的向量会发生什么?内存泄漏。
如果分配失败会发生什么?其他一些功能崩溃。

我会在原地加入:
void addVectors(Vector3D *target, const Vector3D *src);
这相当于target += src;

答案 2 :(得分:1)

我会像

一样简单
Vector3D addVectors(Vector3D a, Vector3D b) {
    Vector3D c;
    c.x = a.x + b.x;
    c.y = a.y + b.y;
    c.z = a.z + b.z;
    return c;
}

如果调用者真的需要它,他可以自己复制它。