是否应该避免强制执行一次 malloc 调用的内存分配实践?

问题描述 投票:0回答:1

我想知道为与结构相关的所有数据分配内存是否是一个好的做法。因此,假设我们有以下结构,其目的是存储浮点数组的数组:

#include <stdlib.h>

typedef struct Arrays {
    int n;
    int* l;
    double** data;
} Arrays;

可以像这样初始化一个示例:

Arrays* UsualInitialization(int n) {
    // Constructs an array A of length n containing the arrays A[i] = [1,2,...,i]
    Arrays* A = (Arrays*) malloc(sizeof(Arrays*));
    A->n = n;
    A->l = (int*) malloc(n * sizeof(int));
    A->data = (double**) malloc(n * sizeof(double*));
    for (int i = 0; i < n; i++) {
        A->l[i] = i + 1;
        A->data[i] = (double*) malloc((i + 1) * sizeof(double));
        for (int j = 0; j < i + 1; j++)
            A->data[i][j] = j + 1;
    }

    return A;
}

每个指针都有一个对应的 malloc 调用。但我们也可以这样做:

Arrays* BlockInitialization(int n) {
    // Constructs an array A of length n containing the arrays A[i] = [1,2,...,i]
    Arrays* A = (Arrays*) malloc(sizeof(Arrays) + 
                    n * sizeof(int) + 
                    n * sizeof(double*) +
                    (n * (n+1) / 2) * sizeof(double)
    );
    
    A->n = n;
    A->l = (int*) (A + 1);
    A->data = (double**) (A->l + n);
    A->data[0] = (double*) (A->data + n);

    for (int i = 0; i < n; i++) {
        A->l[i] = i + 1;
        for (int j = 0; j < i + 1; j++) 
            A->data[i][j] = j + 1;
        if (i < n - 1)
            A->data[i+1] = A->data[i] + i + 1;
    }

    return A;
}

这里有一个 malloc 调用,所有其他指针都是通过指针算术和转换从第一个调用获得的。我认为这在某些情况下可能会更有效,因为所有分配的内存都在一个块中。

那么,我想问:这种做法是否会导致未定义的行为?它会影响性能吗(在某些情况下)?是不是让代码变得更加晦涩难懂?

我在下面留下了一个 print 方法和一个 main 方法,表明两种初始化给出了相同的结果(至少在我的机器上)。

void PrintArrays(Arrays* A) {
    printf("Variable contains %d arrays: \n", A->n);
    for (int i = 0; i < A->n; i++) {
        printf("Array %d (of length %d): [", i, A->l[i]);
        for (int j = 0; j < A->l[i]; j++) {
            printf("%f", A->data[i][j]);
            if (j < A->l[i] - 1)  printf(", ");
        }
        printf("] \n");
    }
}

int main() {
    Arrays* A = BlockInitialization(10);
    PrintArrays(A);

    Arrays* B = UsualInitialization(10);
    PrintArrays(B);

    return 0;
}
c performance malloc
1个回答
0
投票

那么,我想问:这种做法是否会导致未定义的行为?

如果你做得正确就不会。

这里唯一明显的问题是,对于奇数

n
,最终的指针和双打可能会错位。您需要使用
alignof
来计算连续成员之间需要多少填充。

它会影响性能吗(在某些情况下)?

是的,它可以在受益于缓存友好的内存布局的平台上使性能更好

如果您丢失所有指针,展平二维数组并提供简单的访问器来获取/索引数组,可能会更好,因为即使看起来冗余的偏移量计算通常也比多个指针取消引用更快。

这会让代码变得更加晦涩吗?

是的,但是如果晦涩的位被封装在两个函数(分配器和释放器)中,你可以很好地注释它,彻底测试它,然后忘记它。

© www.soinside.com 2019 - 2024. All rights reserved.