C中的对象方法:内存泄漏

时间:2017-02-03 08:07:58

标签: c pointers object struct memory-leaks

我对如何在C中正确实现对象有疑问。

事实是我从更容易发生内存泄漏的方法返回对象,例如,永远不会返回一个对象并在参数列表中通过引用这样做吗?

extern void quaternion_get_product(Quaternion * this, Quaternion * q, Quaternion * result);

这样malloc()调用只在构造函数中完成,因此更容易控制。

我是C中这种封装的新手,所以我不确定这是否是解决我问题的方法。我只是希望我的代码可以扩展,而且我看到如果我继续这样做,内存泄漏将会出现,而且调试真的很难。通常如何接近?我的代码是否在正确的轨道上?

我的问题是,如果我有这个:

Quaternion p = *quaternion_create(1, 0, 0, 0);
Quaternion q = *quaternion_create(1, 0, 1, 0);
Quaternion r = *quaternion_create(1, 1, 1, 0);
Quaternion s = *quaternion_create(1, 1, 1, 1);

p = *quaterion_get_product(&p, &q); // Memory leak, old p memory block is not being pointed by anyone

Quaternion t = *quaternion_get_product(&q, quaternion_get_product(&s, &r)); 

嵌套函数调用时存在内存泄漏,中间内存块未被任何现有指针指向,无法调用quaternion_destroy

标题文件:

#ifndef __QUATERNIONS_H_
#define __QUATERNIONS_H_

#include <stdlib.h>

typedef struct Quaternion Quaternion;

struct Quaternion {
    float w;
    float x;
    float y;
    float z;
};

extern Quaternion *quaternion_create(float nw, float nx, float ny, float nz);
extern void quaternion_destroy(Quaternion *q);
extern Quaternion *quaternion_get_product(Quaternion *this, Quaternion *q);
extern Quaternion *quaternion_get_conjugate(Quaternion *this);
extern float quaternion_get_magnitude(Quaternion *this);
extern void quaternion_normalize(Quaternion *this);
extern Quaternion *quaternion_get_normalized(Quaternion *this);
#endif

实施文件:

#include "quaternion.h"
#include <math.h>

Quaternion *quaternion_create(float nw, float nx, float ny, float nz) {
    Quaternion *q = malloc(sizeof(Quaternion));

    q->w = nw;
    q->x = nx;
    q->y = ny;
    q->z = nz;
    return q;
}

void quaternion_destroy(Quaternion *q) {
    free(q);
}

Quaternion *quaternion_get_product(Quaternion *this, Quaternion *p) {
        Quaternion *return_q = quaternion_create(
            this->w * p->w - this->x * p->x - this->y * p->y - this->z * p->z,  // new w
            this->w * p->x + this->x * p->w + this->y * p->z - this->z * p->y,  // new x
            this->w * p->y - this->x * p->z + this->y * p->w + this->z * p->x,  // new y
            this->w * p->z + this->x * p->y - this->y * p->x + this->z * p->w
        );
        return return_q;
}

Quaternion *quaternion_get_conjugate(Quaternion *this)
{
        return quaternion_create(this->w, -this->x, -this->y, -this->z);
}

float quaternion_get_magnitude(Quaternion *this) {
        return sqrt(this->w * this->w + this->x * this->x + this->y * this->y + this->z * this->z);
}

void quaternion_normalize(Quaternion *this) {
        float m = quaternion_get_magnitude(this);
        this->w /= m;
        this->x /= m;
        this->y /= m;
        this->z /= m;
}

Quaternion *quaternion_get_normalized(Quaternion *this) {
        Quaternion *r = quaternion_create(this->w, this->x, this->y, this->z);
        quaternion_normalize(r);
        return r;
}

3 个答案:

答案 0 :(得分:3)

实际上,如果某个函数更新某些东西的两个副作用并返回一个新构造的值,它会变得更糟。比如,谁记得scanf返回一个值?

关注GNU Multi Precision Arithmetic Library我建议以下是一个合理的解决方案(实际上,它们会让内存分配变得更加令人头痛,而不是图书馆):< / p>

  1. 只有构造函数才能创建新对象。我还建议所有的建设者和#39;名称遵循相同的模式。
  2. 只有析构函数才能销毁已存在的对象。
  3. 函数接受两个输入参数(比如+的两边)和输出参数(放置结果的位置,覆盖之前对象中的所有内容),如下所示:
  4. mpz_add (a, a, b); /* a=a+b */

    通过这种方式,您将始终清楚地看到何时创建/销毁对象,并确保没有泄漏或双重释放。当然,这会阻止你进行链接#34;多个操作一起使您可以手动管理临时变量以获得中间结果。但是,我相信您仍然必须在C中手动执行此操作,因为即使编译器也不太了解动态分配的变量的生命周期。

    实际上,如果我们不离开。 3并添加&#34;库不管理内存&#34;条款,我们将得到更容易出错的解决方案(从库的角度来看),这将要求库的用户管理他们想要的内存。这样你也不会使用malloc / free内存分配来锁定用户,这是一件好事。

答案 1 :(得分:1)

无需动态分配您的季度。四元数具有固定的大小,因此您只能使用普通的四元数。如果进行整数计算,则只使用int s而不为每个int动态分配空间。

不使用malloc/free(未经测试的代码)的想法

Quaternion quaternion_create(float nw, float nx, float ny, float nz) {
    Quaternion q;

    q.w = nw;
    q.x = nx;
    q.y = ny;
    q.z = nz;
    return q;
}

Quaternion quaternion_get_product(Quaternion *this, Quaternion *p) {
    Quaternion return_q = quaternion_create(
        this->w * p->w - this->x * p->x - this->y * p->y - this->z * p->z,  // new w
        this->w * p->x + this->x * p->w + this->y * p->z - this->z * p->y,  // new x
        this->w * p->y - this->x * p->z + this->y * p->w + this->z * p->x,  // new y
        this->w * p->z + this->x * p->y - this->y * p->x + this->z * p->w
    );
    return return_q;
}

用法

Quaternion p = quaternion_create(1, 0, 0, 0);
Quaternion q = quaternion_create(1, 0, 1, 0);
Quaternion r = quaternion_create(1, 1, 1, 0);
Quaternion s = quaternion_create(1, 1, 1, 1);

p = quaterion_get_product(&p, &q);

Quaternion t = quaternion_get_product(&q, quaternion_get_product(&s, &r)); 

没有mallocfree,因此没有可能的内存泄漏,性能会更好。

答案 2 :(得分:1)

在我看来你说错了。

您还需要检查malloc()是否返回NULL并处理该失败(例如通过显示内存不足错误消息并退出,如果失败,因为这通常是不可恢复的)。

<强>更新

由于您需要释放中间结果,因此您必须采取更多操作来保留指针。

更新2

@MichaelWalz方法很棒。如果你不需要,为什么要处理所有的分配和指针管理?但是,如果你使用指针并分配内存,你必须保留指针,并确保你释放东西并传递它们/仔细重用它们。我已经更新了我的例子来处理嵌套调用。

更新3

我不得不从函数调用参数中删除&amp; s,因为你传入了指针,这些指针已经是你想要指向的地址了。您没有正确分配函数的输出。请注意固定示例中指针分配的不同之处。

Quaternion p = *quaternion_create(1, 0, 0, 0);
Quaternion q = *quaternion_create(1, 0, 1, 0);
Quaternion r = *quaternion_create(1, 1, 1, 0);
Quaternion s = *quaternion_create(1, 1, 1, 1);

p = *quaterion_get_product(&p, &q); // Memory leak, old p memory block is not being pointed by anyone

Quaternion t = *quaternion_get_product(&q, quaternion_get_product(&s, &r)); 
Quaternion *p = quaternion_create(1, 0, 0, 0);
Quaternion *q = quaternion_create(1, 0, 1, 0);
Quaternion *r = quaternion_create(1, 1, 1, 0);
Quaternion *s = quaternion_create(1, 1, 1, 1);


Quaternian *tmp = quaterion_get_product(p, q); 
quaternian_destroy(p);
p = tmp;
tmp = quaternion_get_product(s, r)
Quaternion *t = quaternion_get_product(q, tmp); 
quaternian_destroy(tmp);