将函数返回的指针作为输入传递给其他函数时如何避免内存泄漏?
How to avoid memory leak when passing function-returned pointer as input to other function?
我正在尝试编写一个简单的 C 矩阵库,它动态分配所需的内存并允许 "nest" 函数调用(如 fun1(fun2(x))
)以避免声明太多临时变量执行长时间操作时。但是,我无法摆脱因从未释放函数内部创建的结构而导致的内存泄漏。下面的例子清楚地表明了这一点。关于如何在不声明其他临时变量的情况下解决该问题的任何建议?
非常感谢
#include <stdio.h>
#include <stdlib.h>
struct _MatrixF{
uint8_t rows;
uint8_t cols;
float* mat;
};
typedef struct _MatrixF* Matrix;
Matrix newMatrix(uint8_t rows, uint8_t cols){
Matrix matrix = malloc(sizeof(struct _MatrixF));
matrix->rows = rows;
matrix->cols = cols;
matrix->mat = malloc(rows * cols * sizeof(float));
return matrix;
}
Matrix matIdentity(uint8_t rows, uint8_t cols){
Matrix matrix = newMatrix(rows, cols);
uint16_t ii;
for (ii = 0; ii < (cols * rows); ii++)
matrix->mat[ii] = (((ii / cols) == (ii % cols))? 1.0f : 0.0f);
return matrix;
}
Matrix matAdd(Matrix lhs, Matrix rhs){
Matrix m = newMatrix(lhs->rows, lhs->cols);
uint16_t ii;
for (ii = 0; ii < (lhs->cols * lhs->rows); ii++) {
m->mat[ii] = lhs->mat[ii] + rhs->mat[ii];
}
return m;
}
Matrix matMult(Matrix lhs, Matrix rhs){
uint8_t i, j, k;
Matrix result = newMatrix(lhs->rows, rhs->cols);
for (i=0; i < lhs->rows; i++)
for (j=0; j < rhs->cols; j++)
for (k=0; k < lhs->cols; k++)
MAT(result, i, j) += MAT(lhs, i, k) * MAT(rhs, k, j);
return result;
}
void mprint(Matrix m){
printf("%dx%d\n", m->rows, m->cols);
for(int i=0; i<m->rows; i++) {
for (int j = 0; j<m->cols; j++)
printf("%4.6f\t",(float) m->mat[(i) * m->cols + (j)]);
printf("\n");
}
}
int main(int argc, const char * argv[]) {
Matrix A = matIdentity(4, 3);
Matrix B = matIdentity(3, 2);
Matrix C = matIdentity(2, 4);
Matrix D = matIdentity(4, 4);
uint16_t len = 64000;
while (len--){
Matrix E = matAdd(D, matAdd(D, matMult(matMult(A, B),C)));
mprint(matMult(A, B));
mprint(A);
mprint(E);
}
return 0;
}
C 在自动处理这个问题上没有提供太多帮助。您可以存储指向临时对象的指针,并在每次迭代结束时释放它们:
while (len--){
Matrix t1, t2, t3, t4;
Matrix E = matAdd(D, t1=matAdd(D, t2=matMult(t3=matMult(A, B),C)));
mprint(t4=matMult(A, B));
mprint(A);
mprint(E);
matFree(t1);
matFree(t2);
matFree(t3);
matFree(t4);
}
如果矩阵很小,按值传递它们。您将必须静态定义大小,并为每个(有用的)大小准备不同的结构和功能集。这将更快(没有分配,没有指针追逐)和更安全(没有泄漏,释放后没有使用,没有空指针)。
struct mat4 {
float mat[4*4];
};
struct mat4 matIdentity4(void) {
struct mat4 identity = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
};
return identity;
}
...
struct mat3 {
float mat[3*3];
};
...
如果大小变化太大但仍然很小,您仍然可以按值传递它们。您只需要选择宽度和高度的上限:
enum {
MAX_MATRIX_SIZE = 16,
};
struct Matrix {
int rows;
int cols;
float mat[MAX_MATRIX_SIZE*MAX_MATRIX_SIZE];
};
如果矩阵很大,你不想随意分配和复制它们。意外的临时矩阵可能会使您超出可用内存限制。让用户显式分配和释放它们。您可能还希望您的操作改变它们的参数之一(到安全内存、安全缓存和简化清理):
struct Matrix *result = matNew(10,10);
struct Matrix *tmp = matNew(10,10);
matSetToIdentity(result);
fun1(tmp);
matAdd(result, tmp);
fun2(tmp);
matAdd(result, tmp);
matPrint(result);
matFree(tmp);
matFree(result);
如果内存不是问题,您可以保留对已分配矩阵的引用,并在不再需要它们时立即释放它们:
struct Matrix{
struct Matrix* next; // List embedded in elements.
// See: https://isis.poly.edu/kulesh/stuff/src/klist/
int rows;
int cols;
float mat[];
};
struct MatrixPool {
struct Matrix* last;
};
struct Matrix *matIdentity(struct MatrixPool *pool, int rows, int cols) {
struct Matrix* matrix = malloc(sizeof(struct Matrix)
+sizeof(float)*rows*cols);
matrix->next = pool->last;
matrix->rows = rows;
matrix->cols = cols;
matrix->mat = ...;
pool->last = matrix;
return matrix;
}
void matrixPoolFree(struct MatrixPool *pool) {
while(pool->last != NULL) {
struct Matrix* matrix = pool->last;
pool->last = matrix->next;
free(matrix);
}
}
int main() {
struct MatrixPool pool = {0};
struct Matrix* x = matIdentity(&pool, 10, 10);
struct Matrix* y = fun1(&pool, fun2(&pool, x));
mprint(y);
matrixPoolFree(&pool);
}
接下来的两个选项仅供参考。请不要在实际代码中使用它们。使用带有垃圾收集器(Java、Python)或 RAII(C++、Rust)的语言会更明智。
如果您不想在任何地方定义和传递 pool
,您可以使用宏和 alloca
(或者可能是可变长度数组)来实现自动化:
#include <stdio.h>
#include <stdlib.h>
struct Matrix{
int rows;
int cols;
float mat[];
};
#define allocaMatrix(w, h) alloca(sizeof(struct Matrix)+w*h*sizeof(float))
struct Matrix *aux_matIdentity(struct Matrix *mat, int w, int h) {
mat->rows = w;
mat->cols = h;
for(int y=0;y<mat->cols;y++) {
for(int x=0;x<mat->rows;x++) {
mat->mat[y*mat->rows+x] = x==y;
}
}
return mat;
}
#define matIdentity(w,h) aux_matIdentity(allocaMatrix(w,h),w,h)
struct Matrix *aux_matAdd(struct Matrix *result,struct Matrix *left, struct Matrix *right) {
result->rows = left->rows;
result->cols = left->cols;
for(int y=0;y<result->cols;y++) {
for(int x=0;x<result->rows;x++) {
result->mat[y*result->rows+x] = left->mat[y*result->rows+x] + right->mat[y*result->rows+x];
}
}
return result;
}
#define matAdd(left,right) (aux_matAdd(allocaMatrix(left->rows,left->cols), left, right))
void matPrint(struct Matrix *mat) {
for(int y=0;y<mat->cols;y++) {
for(int x=0;x<mat->rows;x++) {
printf("%.1f%c",mat->mat[y*mat->rows+x], x==mat->rows-1?'\n':' ');
}
}
}
int main() {
int n = 16;
struct Matrix *mat1 = matIdentity(n,n);
struct Matrix *mat2 = matIdentity(n,n);
matPrint(matAdd(mat1, mat2));
}
这将为您节省一些击键次数,但这些对象的生命周期变得非常不明显。你不能 return then 从一个函数,如果你分配了很多(例如在一个循环中)或者只是一个大的,你可以粉碎堆栈。
如果你想真正自由地传递这些指针,你可以bastardize garbage collector into C。
我正在尝试编写一个简单的 C 矩阵库,它动态分配所需的内存并允许 "nest" 函数调用(如 fun1(fun2(x))
)以避免声明太多临时变量执行长时间操作时。但是,我无法摆脱因从未释放函数内部创建的结构而导致的内存泄漏。下面的例子清楚地表明了这一点。关于如何在不声明其他临时变量的情况下解决该问题的任何建议?
非常感谢
#include <stdio.h>
#include <stdlib.h>
struct _MatrixF{
uint8_t rows;
uint8_t cols;
float* mat;
};
typedef struct _MatrixF* Matrix;
Matrix newMatrix(uint8_t rows, uint8_t cols){
Matrix matrix = malloc(sizeof(struct _MatrixF));
matrix->rows = rows;
matrix->cols = cols;
matrix->mat = malloc(rows * cols * sizeof(float));
return matrix;
}
Matrix matIdentity(uint8_t rows, uint8_t cols){
Matrix matrix = newMatrix(rows, cols);
uint16_t ii;
for (ii = 0; ii < (cols * rows); ii++)
matrix->mat[ii] = (((ii / cols) == (ii % cols))? 1.0f : 0.0f);
return matrix;
}
Matrix matAdd(Matrix lhs, Matrix rhs){
Matrix m = newMatrix(lhs->rows, lhs->cols);
uint16_t ii;
for (ii = 0; ii < (lhs->cols * lhs->rows); ii++) {
m->mat[ii] = lhs->mat[ii] + rhs->mat[ii];
}
return m;
}
Matrix matMult(Matrix lhs, Matrix rhs){
uint8_t i, j, k;
Matrix result = newMatrix(lhs->rows, rhs->cols);
for (i=0; i < lhs->rows; i++)
for (j=0; j < rhs->cols; j++)
for (k=0; k < lhs->cols; k++)
MAT(result, i, j) += MAT(lhs, i, k) * MAT(rhs, k, j);
return result;
}
void mprint(Matrix m){
printf("%dx%d\n", m->rows, m->cols);
for(int i=0; i<m->rows; i++) {
for (int j = 0; j<m->cols; j++)
printf("%4.6f\t",(float) m->mat[(i) * m->cols + (j)]);
printf("\n");
}
}
int main(int argc, const char * argv[]) {
Matrix A = matIdentity(4, 3);
Matrix B = matIdentity(3, 2);
Matrix C = matIdentity(2, 4);
Matrix D = matIdentity(4, 4);
uint16_t len = 64000;
while (len--){
Matrix E = matAdd(D, matAdd(D, matMult(matMult(A, B),C)));
mprint(matMult(A, B));
mprint(A);
mprint(E);
}
return 0;
}
C 在自动处理这个问题上没有提供太多帮助。您可以存储指向临时对象的指针,并在每次迭代结束时释放它们:
while (len--){
Matrix t1, t2, t3, t4;
Matrix E = matAdd(D, t1=matAdd(D, t2=matMult(t3=matMult(A, B),C)));
mprint(t4=matMult(A, B));
mprint(A);
mprint(E);
matFree(t1);
matFree(t2);
matFree(t3);
matFree(t4);
}
如果矩阵很小,按值传递它们。您将必须静态定义大小,并为每个(有用的)大小准备不同的结构和功能集。这将更快(没有分配,没有指针追逐)和更安全(没有泄漏,释放后没有使用,没有空指针)。
struct mat4 {
float mat[4*4];
};
struct mat4 matIdentity4(void) {
struct mat4 identity = {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1,
};
return identity;
}
...
struct mat3 {
float mat[3*3];
};
...
如果大小变化太大但仍然很小,您仍然可以按值传递它们。您只需要选择宽度和高度的上限:
enum {
MAX_MATRIX_SIZE = 16,
};
struct Matrix {
int rows;
int cols;
float mat[MAX_MATRIX_SIZE*MAX_MATRIX_SIZE];
};
如果矩阵很大,你不想随意分配和复制它们。意外的临时矩阵可能会使您超出可用内存限制。让用户显式分配和释放它们。您可能还希望您的操作改变它们的参数之一(到安全内存、安全缓存和简化清理):
struct Matrix *result = matNew(10,10);
struct Matrix *tmp = matNew(10,10);
matSetToIdentity(result);
fun1(tmp);
matAdd(result, tmp);
fun2(tmp);
matAdd(result, tmp);
matPrint(result);
matFree(tmp);
matFree(result);
如果内存不是问题,您可以保留对已分配矩阵的引用,并在不再需要它们时立即释放它们:
struct Matrix{
struct Matrix* next; // List embedded in elements.
// See: https://isis.poly.edu/kulesh/stuff/src/klist/
int rows;
int cols;
float mat[];
};
struct MatrixPool {
struct Matrix* last;
};
struct Matrix *matIdentity(struct MatrixPool *pool, int rows, int cols) {
struct Matrix* matrix = malloc(sizeof(struct Matrix)
+sizeof(float)*rows*cols);
matrix->next = pool->last;
matrix->rows = rows;
matrix->cols = cols;
matrix->mat = ...;
pool->last = matrix;
return matrix;
}
void matrixPoolFree(struct MatrixPool *pool) {
while(pool->last != NULL) {
struct Matrix* matrix = pool->last;
pool->last = matrix->next;
free(matrix);
}
}
int main() {
struct MatrixPool pool = {0};
struct Matrix* x = matIdentity(&pool, 10, 10);
struct Matrix* y = fun1(&pool, fun2(&pool, x));
mprint(y);
matrixPoolFree(&pool);
}
接下来的两个选项仅供参考。请不要在实际代码中使用它们。使用带有垃圾收集器(Java、Python)或 RAII(C++、Rust)的语言会更明智。
如果您不想在任何地方定义和传递 pool
,您可以使用宏和 alloca
(或者可能是可变长度数组)来实现自动化:
#include <stdio.h>
#include <stdlib.h>
struct Matrix{
int rows;
int cols;
float mat[];
};
#define allocaMatrix(w, h) alloca(sizeof(struct Matrix)+w*h*sizeof(float))
struct Matrix *aux_matIdentity(struct Matrix *mat, int w, int h) {
mat->rows = w;
mat->cols = h;
for(int y=0;y<mat->cols;y++) {
for(int x=0;x<mat->rows;x++) {
mat->mat[y*mat->rows+x] = x==y;
}
}
return mat;
}
#define matIdentity(w,h) aux_matIdentity(allocaMatrix(w,h),w,h)
struct Matrix *aux_matAdd(struct Matrix *result,struct Matrix *left, struct Matrix *right) {
result->rows = left->rows;
result->cols = left->cols;
for(int y=0;y<result->cols;y++) {
for(int x=0;x<result->rows;x++) {
result->mat[y*result->rows+x] = left->mat[y*result->rows+x] + right->mat[y*result->rows+x];
}
}
return result;
}
#define matAdd(left,right) (aux_matAdd(allocaMatrix(left->rows,left->cols), left, right))
void matPrint(struct Matrix *mat) {
for(int y=0;y<mat->cols;y++) {
for(int x=0;x<mat->rows;x++) {
printf("%.1f%c",mat->mat[y*mat->rows+x], x==mat->rows-1?'\n':' ');
}
}
}
int main() {
int n = 16;
struct Matrix *mat1 = matIdentity(n,n);
struct Matrix *mat2 = matIdentity(n,n);
matPrint(matAdd(mat1, mat2));
}
这将为您节省一些击键次数,但这些对象的生命周期变得非常不明显。你不能 return then 从一个函数,如果你分配了很多(例如在一个循环中)或者只是一个大的,你可以粉碎堆栈。
如果你想真正自由地传递这些指针,你可以bastardize garbage collector into C。