运行时将一维数组视为二维数组的处理方法

3

我有一些数据存储在一个大小为M的一维数组中。现在我需要将它视为一个二维数组,其维数为NxP,其中N和P的乘积等于M。我只知道运行时的N和P值。如何在C语言中实现这样的函数?

int array[M]; /* one dimensional array where some data is stored*/
int** newArray; /* the dimension of newArray should be NxP such that we can access the data in 'array' as a two-dimensional array*/

1
我该如何在C语言中实现这样的函数?- 因此,移除了C++标签。 - WhozCraig
@ WhozCraig:感谢您的编辑。 - user4661268
5个回答

5

只需将其转换为相应的数组指针类型:

int (*newArray)[N] = (int (*)[N])array;

之后,您可以通过以下方式访问数组:

for(int y = 0; y < P; y++) {
    for(int x = 0; x < N; x++) {
        array[y][x] = 42;
    }
}

这相当于以下的索引方式:
for(int y = 0; y < P; y++) {
    for(int x = 0; x < N; x++) {
        newArray[y*N + x] = 42;
    }
}

这种方法适用于C99,即使N只在运行时才知道。请注意,您不需要设置一个索引数组,就像使用int**那样做。


1
是的,这是正确的方法,指向指针只是从一开始就选择了错误的工具。 - Jens Gustedt
对于一个三维数组(例如 array[X][Y][Z]),这该怎么办呢? 我尝试了 int (*newArray)[X][Y] = (int (*)[X][Y]) array,但它不起作用 :-/ - ijverig
@ijverig 这是错误的:指针省略的是最外层的维度。因此,对于维度顺序为 array[X][Y][Z] 的数组,您需要去掉 [X]int (*newArray)[Y][Z] = (int (*)[Y][Z])array; - cmaster - reinstate monica
请注意,在 C 语言中,我们通常使用相反的维度顺序:array[Z][Y][X],因此 int (*newArray)[Y][X] = (int (*)[Y][X])array;。这样,我们可以将单行的所有 X 值存储在 array[z][y] 中。 - cmaster - reinstate monica

2

您不需要定义一个新的数组。您可以使用现有的数组。

假设您知道N和P,且N是行数,则可以通过以下方式访问第(i,j)项:

array[i*N + j]

如果N行数,那么该索引方程式是错误的。如果它是列数,那么它是正确的。 - WhozCraig
谢谢回复。但我想用两个索引访问它,就像二维数组一样。 - user4661268

1
你可以像这样做:

int ** newArray = malloc(sizeof(int*) * N);
for (i = 0; i < N; ++i) {
  newArray[i] = array[i * J];
}

这将创建一个数组,它“看起来”就像是一个动态分配的N行J列的二维数组,但实际上指向1D数组的行。
这样,如果您已经有了操作2D数组的函数,您就不需要重新编写它们以使用其他答案中描述的1D语法。

谢谢回复。我想要避免额外的内存分配! - user4661268
然后像这样在堆栈上声明它:int * newArray[N]; 而不是使用malloc。我假设你想要使用malloc,因为你在你的回答中说了 int **。请注意,如果你返回它,你将不得不使用malloc。 - Chris Beck

1

运行时会使这变得有点困难,但是 :-

newArray = malloc( sizeof( int*) * N ); /* create an array of pointers.
{ 
     size_t i;
     for( i = 0; i < N; i++ ) {
         newArray[i] = &array[ i* P];
     }
}

/* 现在 newArray[i][j] 可用 */


谢谢回复。我想避免额外的内存分配! - user4661268

0
你可以将1维数组直接转换为你想要的2维数组。它只是一块内存区域。
int _tmain(int argc, _TCHAR* argv[])
{
    int oneDArray[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    int(*twoDArray)[3] = (int(*)[3])&oneDArray[0]; // This is the magic line!

    for (int i = 0; i < 4; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("i: %d j: %d value: %d\r\n", i, j, twoDArray[i][j]);
        }
    }

    _getch();

    return 0;
}

还可以查看转换数组问题的问题

这样做存在一些固有的不安全性,但是您的问题说明NxP=M,如果这是真的,那么它可以工作。但人们会对此持反感态度。


我的投票限制今天已经超过了,但如果没有的话,我会点赞的 :) 感谢这个技巧。 - Chris Beck
确保你理解正在发生的事情。在C语言中,指针只是内存,因此你可以像它包含任何内容一样对待它。但是,如果你的索引错误,C语言不会关心你是否读取了实际分配的末尾之后的内容,这就导致了人们对C/C++最大的抱怨,即缓冲区溢出。 - AngularRat
我的意思是,C语言在结构体对齐方面存在许多陷阱,有时候不太直观,比如这里:https://dev59.com/0mAg5IYBdhLWcg3wo8L2 但我想对于数组来说,它总是连续的,所以在你的例子中将其转换为*int[3]是可以的。关键是int[12]会对齐到int边界,否则偏移量总是线性递增的(如果不是这样的话,一般情况下解引用数组会变得更慢)。我认为即使是struct foo[12],你的技巧也是可行的。 - Chris Beck
如果在两个数组定义中数据类型保持不变,那么您不必担心它的存储方式,因为编译器知道 int 是 x 个字节,所以索引 0 是字节 0,索引 1 是字节 (1 x sizeof(int)),依此类推。但是,如果您像疯了一样将数组保存到磁盘并在具有不同架构的计算机上重新加载它,则会出现问题。(相当确定在不同的架构上 int 的大小可能不同。) - AngularRat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接