【问题标题】:Treating one dimensional array as two dimensional at run time在运行时将一维数组视为二维数组
【发布时间】:2015-08-31 18:28:42
【问题描述】:

我有一些数据存储在一个大小为“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*/

【问题讨论】:

  • "如何在 C 中实现这样的功能?" - 因此,删除了 C++ 标记。
  • @WhozCraig:感谢编辑。

标签: c arrays


【解决方案1】:

只需将其转换为适当的数组指针类型:

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;
    }
}

即使 N 自 C99 以来仅在运行时已知,这也有效。请注意,您不需要像使用int** 那样设置索引数组。

【讨论】:

  • 是的,这是正确的方法,指向指针的指针从一开始就是错误的工具。
  • 对于 3D 数组(例如 array[X][Y][Z])会怎样?我试过int (*newArray)[X][Y] = (int (*)[X][Y]) array,它不起作用:-/
  • @ijverig 错了:它是指针中省略的最外层维度。因此,对于维度订单array[X][Y][Z],您需要删除[X]int (*newArray)[Y][Z] = (int (*)[Y][Z])array;
  • @ijverig 请注意,我们通常在 C 中使用相反的维度顺序:array[Z][Y][X],因此int (*newArray)[Y][X] = (int (*)[Y][X])array; 这样,我们在@987654334 处拥有单行的所有X 值@ 存储在一起。
【解决方案2】:

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

假设你知道 N 和 P,并且 N 是行数,你可以访问 item (i,j) 为:

array[i*N + j]

【讨论】:

  • 如果N行数,则该索引方程是错误的。如果是列数是正确的。
  • 感谢您的回复。但我想将它作为具有两个索引的二维数组访问。
【解决方案3】:

你可以这样做:

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

这将使数组“看起来”就像一个动态分配的 N 行和 J 列的二维数组,但实际上指向一维数组的行。

这样,如果您已经有对二维数组进行操作的函数,则无需重写它们以使用其他答案中描述的一维语法。

【讨论】:

  • 感谢您的回复。我想避免额外的内存分配!
  • 然后像int * newArray[N];一样在堆栈上声明它而不是mallocing。我以为您想 malloc ,因为您在回答中说 int ** 。请注意,如果您返回它,尽管您必须对其进行 malloc。
【解决方案4】:

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

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] 可用 */

【讨论】:

  • 感谢您的回复。我想避免额外的内存分配!
【解决方案5】:

您可以将一维数组转换为您想要的二维数组。这只是一块内存。

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;
}

另见问题Convert Array Question

这样做存在一些固有的不安全性,但您的问题表明 NxP=M,所以如果这是真的,它会起作用。但人们会对此皱眉头。

【讨论】:

  • 我的投票超过了当天的限制,但如果不是我会加一个这个:) 感谢这个技巧
  • 好吧,确保你明白发生了什么。 c 中的指针只是内存,因此您可以将其视为包含任何内容。但是如果你的索引有误,c 不在乎你是否读到了实际分配的末尾,这就是人们对 c/c++ 最大的抱怨,即缓冲区溢出。
  • 我的意思是另一件事是 C 在结构对齐方面有很多陷阱,有时它的放置位置并不直观,例如参见这里 stackoverflow.com/questions/23367937/memory-alignment-of-arrays 但我猜数组总是会这样连续的,因此在您的示例中转换为 *int[3] 是可以的。关键是int[12]int 边界对齐,否则偏移量始终是线性级数(如果它不那样工作,通常取消引用数组会慢很多)我认为你的技巧会好的即使struct foo[12]
  • 如果你的数据类型在两个数组定义中保持相同,你不必担心它的存储方式,因为编译器知道 int 是 x 字节,所以索引 0 是字节 0,索引 1 是字节 (1 x sizeof(int) ) 等。但是,如果您做了一些疯狂的事情,例如将数组保存到磁盘并将其重新加载到具有不同架构的机器上,该架构以不同的方式定义整数,您就会遇到问题。 (很确定 int 在不同的架构上可以有不同的大小。)
猜你喜欢
  • 2018-11-20
  • 2018-07-26
  • 2016-02-18
  • 2018-02-19
  • 2014-03-25
  • 1970-01-01
相关资源
最近更新 更多