我尝试使用连续的内存块,而不是尝试创建一个在编译时(C99
之前)未知的维度的数组,因此没有variable-length arrays
是介入此处。
我带来了以下内容:
#include <stdio.h>
#include <stdlib.h>
int main(void){
unsigned int row, col,i, j, k;
int l = 0;
printf("Give the ROW: ");
if ( scanf("%u",&row) != 1){
printf("Error, scanf ROW\n");
exit(1);
}
printf("Give the COL: ");
if ( scanf("%u",&col) != 1){
printf("Error, scanf COL\n");
exit(2);
}
int *arr = malloc(sizeof *arr * row * col); /* This doesn't compile with `-pedantic` */
if(arr == NULL){
printf("Error, malloc\n");
exit(3);
}
for ( i = 0; i < row ; i++){
for ( j = 0 ; j < col ; j++){
arr[i * col + j] = l;
l++;
}
}
for (k = 0 ; k < (row * col) ; k++){
printf("%d ",arr[k]);
}
free(arr);
}
给出了以下内容:
Give the ROW: 5
Give the COL: 5
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
现在我有一个问题:
这是正确的方法吗?
答案 0 :(得分:3)
此行中有一个隐藏错误:
arr[i * row + j] = k;
正确的指数计算是
arr[i * col + j] = k;
您没有注意到该问题的原因是row
和col
在代码中具有相同的值。例如,如果您将row
设置为6并将col
设置为3,则错误将很明显。您将为18个整数分配空间,但当i
为5且j
为2时,代码将尝试访问位于数组末尾的位置[5*6 + 2]
,并且将导致在未定义的行为中。
这里是地址计算的工作原理。下图显示了如何在内存中实际布置2D数组(row = 6和col = 3)。请注意,每行上的项数等于数组中的列数。因此,每行的起始索引是列数的倍数。在此示例中,由于列数为3,因此行从索引0,3,6,9开始... ...因此,在2D数组中给定索引[i][j]
处的元素时,1D数组中的索引是i*col + j
。
答案 1 :(得分:1)
方法还可以。尺寸在编译时是已知的。
获得15的原因是arr
它代表基地址,所以当你向arr
添加15时,它会给你包含15的块的地址,之后你会得到取消引用15。
答案 2 :(得分:1)
如果您未提前知道维度的数量(1,2,3或更多维度),那么这只是 方法你有空。如果您知道尺寸的数量,而不知道它们的值,并且您没有可用的VLA,那么这是您可用的唯一方法。
因为我从编写文档中感到厌倦了我的头脑,所以我编写了这个快速而肮脏的原型来演示如何将一维数组映射到不同维数的数组:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/**
* Compute the offset into a 1D array based on a set of dimensions
* and indices, and return the element at that offset. You must pass
* at least as many indices as number of dimensions; any extra indices
* will not be processed.
*
* Inputs:
* a -- 1D array of data
* ndims -- number of dimensions to map array onto
* dims -- dimension sizes
* ... -- index values
*
* Outputs: none
*
* Returns: value at desired index
*/
int access( const int * restrict a, size_t ndims, const size_t * restrict dims, ... )
{
va_list ap;
va_start( ap, dims ); point to first index value in argument list
size_t idx = 0;
/**
* To find the right index for a given number of dimensions,
* we need to compute
*
* d0 x d1: i * d1 + j
* d0 x d1 x d2: i * d1 * d2 + j * d1 + k
* d0 x d1 x d2 x d3: i * d1 * d2 * d3 + j * d1 * d2 + k * d1 + l
*
* The loop below computes these as
*
* i * d1 + j
* (i * d2 + j) * d1 + k
* (((i * d3 + j) * d2) + k) * d1 + l
*
* etc.
*/
for ( size_t i = 1; i < ndims; i++ )
{
idx += va_arg( ap, size_t ); // get next index argument and advance ap
idx *= dims[i];
}
idx += va_arg( ap, size_t );
va_end( ap );
return a[idx];
}
int main( void )
{
int test[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
size_t dims2x5[] = {2, 5}; // for mapping test onto a 2x5 array
size_t dims3x3[] = {3, 3}; // for mapping test onto a 3x3 array
size_t dims2x2x2[] = {2, 2, 2}; // for mapping test onto a 2x2x2 array
for ( size_t i = 0; i < dims2x5[0]; i++ )
for ( size_t j = 0; j < dims2x5[1]; j++ )
printf( "test[%zu][%zu] = %d\n", i, j, access( test, 2, dims2x5, i, j ) );
for ( size_t i = 0; i < dims3x3[0]; i++ )
for ( size_t j = 0; j < dims3x3[1]; j++ )
printf( "test[%zu][%zu] = %d\n", i, j, access( test, 2, dims3x3, i, j ) );
for ( size_t i = 0; i < dims2x2x2[0]; i++ )
for ( size_t j = 0; j < dims2x2x2[1]; j++ )
for ( size_t k = 0; k < dims2x2x2[2]; k++ )
printf( "test[%zu][%zu][%zu] = %d\n", i, j, k, access( test, 3, dims2x2x2, i, j, k ));
return 0;
}
输出:
test[0][0] = 0
test[0][1] = 1
test[0][2] = 2
test[0][3] = 3
test[0][4] = 4
test[1][0] = 5
test[1][1] = 6
test[1][2] = 7
test[1][3] = 8
test[1][4] = 9
test[0][0] = 0
test[0][1] = 1
test[0][2] = 2
test[1][0] = 3
test[1][1] = 4
test[1][2] = 5
test[2][0] = 6
test[2][1] = 7
test[2][2] = 8
test[0][0][0] = 0
test[0][0][1] = 1
test[0][1][0] = 2
test[0][1][1] = 3
test[1][0][0] = 4
test[1][0][1] = 5
test[1][1][0] = 6
test[1][1][1] = 7
这不是漂亮 - access( a, 3, dims2x2x2, i, j, k )
并不像a[i][j][k]
那样容易阅读。通过一些额外的抽象级别,你可以稍微清理一下,但总觉得有点尴尬。你自然会牺牲一些表现。但是,如果您需要能够将1D阵列映射到任意大小的N维阵列上,而您甚至不知道维度的数量,这是一种可能的解决方案。