当编译时宽度未知时,是否可以编写一个接受二维数组的函数?
将不胜感激详细的描述。
您不能传递原始二维数组,因为例程不知道如何索引特定元素。二维数组实际上是一个连续的内存段。
当您编写x[a][b]
时(当 x 是二维数组时),编译器知道查看地址 (x + a * width + b)。如果您不告诉它宽度,它就无法知道如何处理特定元素。
例如,检查http://www.dfstermole.net/OAC/harray2.html#offset(其中有一个表格显示如何找到 int[5][4] 中每个元素的线性索引)
有两种方法可以解决此限制:
1)使您的程序使用指针指针(char * )。这与 char[][] 不同。char *实际上是一个内存段,每个值都是另一个内存段的内存地址。
2)传递一个一维指针,并自己进行引用。然后,您的函数必须采用“宽度”参数,并且您可以使用上述公式来引用特定点
举一个代码示例:
#include <stdio.h>
int get2(int *x) { return x[2]; }
int main() {
int y[2][2] = {{11,12},{21,22}};
printf("%d\n", get2((int *)y));
}
这应该打印出 21,因为y
在内存中布局为 { 11, 12, 21, 22 }。
C 支持变长数组。您必须根据运行时已知的值指定宽度,该值可能是函数声明中的较早参数:
void foo(size_t width, int array[][width]);
一种方法是使用古老的“指向数组指针数组的指针”技巧以及单个连续分配:
/* Another allocation function
--------------------------- */
double ** AnotherAlloc2DTable(
size_t size1, /*[in] Nb of lines */
size_t size2 /*[in] Nb of values per line */
)
{
double ** ppValues;
size_t const size1x2 = size1*size2;
if(size1x2 / size2 != size1)
return NULL; /*size overflow*/
ppValues = malloc(sizeof(*ppValues)*size1);
if(ppValues != NULL)
{
double * pValues = malloc(sizeof(*pValues)*size1x2);
if(pValues != NULL)
{
size_t i;
/* Assign all pointers */
for(i=0 ; i<size1 ; ++i)
ppValues[i] = pValues + (i*size2);
}
else
{
/* Second allocation failed, free the first one */
free(ppValues), ppValues=NULL;
}
}/*if*/
return ppValues;
}
/* Another destruction function
---------------------------- */
void AnotherFree2DTable(double **ppValues)
{
if(ppValues != NULL)
{
free(ppValues[0]);
free(ppValues);
}
}
然后你所要做的就是将 a 传递char **
给你的函数。矩阵是连续的,可用作mat[x][y]
.
可能的访问器功能:
int get_multi(int rows, int cols, int matrix[][cols], int i, int j)
{
return matrix[i][j];
}
int get_flat(int rows, int cols, int matrix[], int i, int j)
{
return matrix[i * cols + j];
}
int get_ptr(int rows, int cols, int *matrix[], int i, int j)
{
return matrix[i][j];
}
一个实际的多维数组和一个假的:
int m_multi[5][7];
int m_flat[5 * 7];
使用访问器函数的定义明确的方法:
get_multi(5, 7, m_multi, 4, 2);
get_flat(5, 7, m_flat, 4, 2);
{
int *m_ptr[5];
for(int i = 0; i < 5; ++i)
m_ptr[i] = m_multi[i];
get_ptr(5, 7, m_ptr, 4, 2);
}
{
int *m_ptr[5];
for(int i = 0; i < 5; ++i)
m_ptr[i] = &m_flat[i * 7];
get_ptr(5, 7, m_ptr, 4, 2);
}
在实践中有效的技术上未定义的用法:
get(5, 7, (int *)m_multi, 4, 2);
[警告 - 这个答案解决了列数 - WIDTH - 已知的情况]
使用 2D 数组时,编译器需要知道数组中的列数才能计算索引。例如,如果您希望将指向p
内存范围的指针视为二维值集,则编译器无法进行必要的索引运算,除非它知道数组的每一行占用了多少空间。
通过一个具体的例子,事情变得更清楚了,比如下面的例子。这里,指针p
作为指向一维内存范围的指针传入。您(程序员)知道将其视为 2D 数组是有意义的,并且您还知道(必须知道)该数组中有多少列。有了这些知识,您就可以编写代码来创建q
,编译器将其视为具有未知行数的二维数组,其中每一行都有精确的NB
列。
当我希望编译器执行所有索引运算时,我通常会使用它(为什么编译器可以手动执行它?)。过去,我发现这种构造对于执行从一种形状到另一种形状的 2D 转置很有用 - 请注意,尽管将 MxN 数组转置为 NxM 数组的广义 2D 转置相当糟糕。
void
WorkAs2D (double *p)
{
double (*q)[NB] = (double (*)[NB]) p;
for (uint32_t i = 0; i < NB; i++)
{
for (uint32_t j = 0; j < ZZZ; j++) /* For as many rows as you have in your 2D array */
q[j][i] = ...
}
}
我相信一个不错的解决方案是使用结构。所以我有一个一维数组的例子:
结构的定义:
struct ArrayNumber {
unsigned char *array;
int size;
};
函数定义:
struct ArrayNumber calcMultiply(struct ArrayNumber nra, struct ArrayNumber nrb);
初始化结构:
struct ArrayNumber rs;
rs.array = malloc(1);
rs.array[0] = 0;
rs.size = 1;
//and adding some size:
rs.size++;
rs.array = realloc(rs.array, rs.size);
希望这可以成为您的解决方案。只需更改为二维数组。