【问题标题】:alternative to multidimensional array in c替代c中的多维数组
【发布时间】:2012-01-07 00:31:19
【问题描述】:

我有以下代码:

 #define FIRST_COUNT 100
 #define X_COUNT 250
 #define Y_COUNT 310
 #define z_COUNT 40

struct s_tsp {

     short abc[FIRST_COUNT][X_COUNT][Y_COUNT][Z_COUNT];
};

struct s_tsp xyz;

我需要像这样遍历数据:

for (int i = 0; i < FIRST_COUNT; ++i)
    for (int j = 0; j < X_COUNT; ++j)
          for (int k = 0; k < Y_COUNT; ++k)
                for (int n = 0; n < Z_COUNT; ++n)
                      doSomething(xyz, i, j, k, n);

我试图想出一种更优雅、更少脑死的方法。 (我知道这种多维数组在 cpu 使用方面效率低下,但这在这种情况下无关紧要。)我在这里构造事物的方式有更好的方法吗?

【问题讨论】:

  • 你想用这些数据做什么?在不知道自己在做什么的情况下,很难告诉你该尝试什么。

标签: c data-structures multidimensional-array


【解决方案1】:

如果您需要一个 4D 数组,那么这就是您所需要的。可以将其“展平”为一维 malloc()ed 'array',但这并不那么干净:

abc = malloc(sizeof(short)*FIRST_COUNT*X_COUNT*Y_COUNT*Z_COUNT);

访问也更加困难:

*(abc + FIRST_COUNT*X_COUNT*Y_COUNT*i + FIRST_COUNT*X_COUNT*j + FIRST_COUNT*k + n)

所以这显然有点痛苦。

但你确实有这样的优势,如果你需要简单地迭代每个元素,你可以这样做:

for (int i = 0; i < FIRST_COUNT*X_COUNT*Y_COUNT*Z_COUNT; i++) {
    doWhateverWith *(abc+i);
}

显然,这种方法对于大多数用途来说非常难看,对于一种访问方式来说更简洁一些。它也更节省内存,只需要一个指针解引用而不是 4 个。

【讨论】:

  • 您总是可以只编写一个带有访问函数的小模块并将类型包装在一个结构中以获得干净的 API。
  • 是的。如果你是一个非常糟糕的人,你甚至可以使用宏来做到这一点。
【解决方案2】:

注意:本文中使用的示例仅用于解释概念。因此示例可能不完整,可能缺少错误处理等。

C中使用多维数组时,有以下两种可能的方式。

数组扁平化

C 中,数组被实现为一个连续的内存块。此信息可用于操作存储在数组中的值,并允许快速访问特定的数组位置。

例如,

int arr[10][10];
int *ptr = (int *)arr ;  

ptr[11] = 10; 
// this is equivalent to arr[1][0] = 10; assign a 2D array 
// and manipulate now as a single dimensional array. 

利用数组连续性的技术被称为flattening of arrays

参差不齐的数组

现在,考虑以下示例。

char **list;
list[0] = "United States of America";
list[1] = "India";
list[2] = "United Kingdom";
for(int i=0; i< 3 ;i++)
    printf(" %d ",strlen(list[i]));
// prints 24 5 14

这种类型的实现称为不规则数组,在使用可变大小字符串的地方很有用。流行的方法是在每个维度上都有dynamic-memory-allocation

注意:命令行参数 (char *argv[]) 仅作为不规则数组传递。

比较扁平和不规则数组

现在,让我们考虑以下代码 sn-p,它比较 flattenedragged 数组。

/* Note: lacks error handling */
int flattened[30][20][10];
int ***ragged;
int i,j,numElements=0,numPointers=1;
ragged = (int ***) malloc(sizeof(int **) * 30);
numPointers += 30;
for( i=0; i<30; i++) {
    ragged[i] = (int **)malloc(sizeof(int*) * 20);
    numPointers += 20;
    for(j=0; j<20; j++) {
        ragged[i][j]=(int*)malloc(sizeof(int) * 10);
        numElements += 10;
    }
}

printf("Number of elements = %d",numElements);
printf("Number of pointers = %d",numPointers);

// it prints
// Number of elements = 6000
// Number of pointers = 631 

从上面的例子中,ragged 数组需要631-pointers,换句话说,631 * sizeof(int *) 需要额外的内存位置来指向6000 整数。而flattened 数组只需要一个基指针:即数组的名称足以指向连续的6000 内存位置。

但 OTOH,ragged 数组很灵活。在不知道所需内存位置的确切数量的情况下,您不能奢侈地为最坏的情况分配内存。同样,在某些情况下,所需的内存空间的确切数量仅在运行时才知道。在这种情况下,ragged 数组就派上用场了。

数组的行优先和列优先

C 遵循row-major 对多维数组的排序。 Flattening 的数组可以看作是由于C 中的这一方面的影响。 C 的row-major 顺序的意义在于它适合在编程中进行大多数访问的自然方式。例如,让我们看一个遍历N * M 2D 矩阵的示例,

for(i=0; i<N; i++) {
    for(j=0; j<M; j++)
        printf(“%d ”, matrix[i][j]);
    printf("\n");
}

矩阵中的每一行都是通过快速改变列来逐一访问的。 C 数组以这种自然的方式排列在内存中。相反,请考虑以下示例,

for(i=0; i<M; i++) {
    for(j=0; j<N; j++)
        printf(“%d ”, matrix[j][i]);
    printf("\n");
}

这会比行索引更频繁地更改列索引。正因为如此,这两个代码sn-p在效率上存在很大差异。是的,第一个比第二个效率更高!

因为第一个以 C 的自然顺序(row-major 顺序)访问数组,因此它更快,而第二个需要更多时间来跳转。随着维度数量和元素大小的增加,性能差异会越来越大。

所以在C 中处理多维数组时,最好考虑以上细节!

【讨论】:

  • 您的一些代码存在重大问题,包括使用未初始化的变量和未定义的类型转换。
  • @dreamlax 是的,sn-p 代码不完整。它们旨在解释这些想法和概念。所以正如我所提到的,它缺乏错误处理,free()ing 分配的内存和未初始化的内存。
猜你喜欢
  • 2016-07-01
  • 2011-08-17
  • 2013-01-23
  • 2011-08-23
  • 2015-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多