在C语言中如何引用二维数组的列?

4

有没有一种简单的方法,在普通的C(而不是C ++或C#)中将2-D数组中的列作为单独的1-D数组引用?这很容易为行执行。假设我有2个函数:

double doSomethingWithARow( double theRow[3] );
double doSomethingWithACol( double theCol[100] );

然后,我可以像这样使用第一个:
double matrix[100][3];
double result;

// pass a single row to a function as an array
// this essentially passes the 3-element array at row 48 to the function
for( int i=0; i < 100; i++ )
{
   result = doSomethingWithARow( matrix[i] );
}

我希望有一种简便的方法来访问列。
for( int j=0; j < 3; j++ )
{
   result = doSomethingWithACol( ??????????? );
}

到目前为止,我唯一想到的方法是将矩阵转换为交换行和列。但这段代码应该在内存和速度方面尽可能高效。在C语言中引用指针的方式非常复杂,似乎应该有一种方法可以做到这一点。

5个回答

5

好的,您需要传递一行的大小和行数:

 double doSomethingWithACol(double *matrix, size_t colID, size_t rowSize, size_t nRows);

现在你可以利用矩阵[i][j] = 矩阵 + i * 行大小 + j;

或者,您也可以使用以下签名:

 double doSomethingWithACol(double *colPtr, size_t rowSize, size_t nRows);

在这里,您需要传递指向要处理的列的第一个元素的指针,而不是指向第一行的指针。


示例代码:此代码对第二列中的元素求和(使用gcc -o main -Wall -Wextra -pedantic -std=c99 test.c编译):

#include <stdio.h>
#include <stdlib.h>

double colSum1(double *matrix, size_t colID, size_t rowSize, size_t nRows)
{
  double *c = NULL, *end = matrix + colID + (nRows * rowSize);
  double sum = 0;

  for (c = matrix + colID; c < end; c += rowSize) {
    sum += *c;
  }

  return sum;
}

double colSum2(double *colPtr, size_t rowSize, size_t nRows)
{
  double *end = colPtr + (nRows * rowSize);
  double sum = 0;

  for (; colPtr < end; colPtr += rowSize) {
    sum += *colPtr;
  }

  return sum;
}

int
main(void)
{
  double matrix[4][3] = {
    {0,  1, 2},
    {3,  4, 5},
    {6,  7, 8},
    {9, 10, 11}
  };

  printf("%f\n", colSum1(*matrix, 1, 3, 4));
  printf("%f\n", colSum2(&matrix[0][1], 3, 4));
  printf("%f\n", colSum2(matrix[0] + 1, 3, 4));

  return EXIT_SUCCESS;
}

这不会做你想要的事情。传递一个double **是完全错误的。它会导致应用程序崩溃。我在我的答案中会解释更多。 - Brian R. Bondy
双星号**不是必需的;我在编写示例代码时注意到了这一点。然而,它与段错误无关。 - Stephan202
这种方法确实更加简洁明了,但不适用于任意维度的数组。因此,在具体情况下需要根据实际情况来选择哪种方法。 - Stephan202
这是一种有用且常用的方法,但请注意这也是未定义行为。(阅读http://groups.google.com/group/comp.lang.c/msg/327b816c5ea5d97b了解原因) - Johannes Schaub - litb
我给一个赞,因为这是一种有用的技巧,哈哈。 - Johannes Schaub - litb

3
一种不需要将尺寸作为单独参数指定的良好类型安全的方法如下所示:
#define ROWS 100
#define COLUMNS 30 

void doSomethingToAllRows(double (*row)[ROWS][COLUMNS], int col, double val)
{
    for(size_t i = 0; i < ROWS; ++i)
        (*row)[i][col] = val;
}

void doSomethingToAllColumns(double (*col)[ROWS][COLUMNS], int row, double val)
{
    for(size_t i = 0; i < COLUMNS; ++i)
        (*col)[row][i] = val;
}

int main(int argc, char **argv)
{
    double matrix[ROWS][COLUMNS];

    /* Modify each column of the 10th row with the value of 3 */
    doSomethingToAllColumns(&matrix, 10, 3); 

    /* Modify each row of the 10th column with the value of 3 */
    doSomethingToAllRows(&matrix, 10, 3);

    return 0;
}

因为以下原因,将双倍的** 传递是完全错误的:

void test()
{
  double **a;
  int i1 = sizeof(a[0]);//i1 == 4 == sizeof(double*)

  double matrix[ROWS][COLUMNS];
  int i2 = sizeof(matrix[0]);//i2 == 240 == COLUMNS * sizeof(double)
}

如果你传入一个双精度浮点数,然后像数组一样访问它,你将会导致崩溃、段错误或未定义的行为。

你的函数使用了引用,而原帖指定了 C 语言。除此之外,如果你十分迫切地想要避免使用容器,那么这是一个不错的解决方案。 - Andrew Grant
+1,如果程序中使用的数组的维度是固定的,那么这段代码比我的解决方案更好。 - Stephan202

0

由于你所说的“列”在内存中是不连续存储的,因此没有直接实现这个的方法。

但是,你可以创建一个指针数组,并在其中存储对另一个数组索引的引用。你需要循环遍历数组中的所有元素,因此它可能不比其他任何解决方案更好。但是,根据你需要按列访问数组的频率,这可能是值得的。


0

你无法这样做,因为在C语言中,数组的存储方式是将每一行的元素连续存储在一起。这意味着数组的一行是一个连续的内存块,对于C来说,它可能就像一个独立的数组一样。但是列不是这样工作的,因为一列的元素在内存中不是连续的;相反,它们以N字节间隔分布,其中每一行的长度为N字节。这意味着您可以通过使用指针算术有效地访问2D数组的各个列的元素,但除了将元素复制到新数组中外,没有方法将列实际上变成一个数组。


0

不,没有。这是不可能的,因为在C语言中,数组是内存的连续部分,而行和列同时不能连续是显然的。

话虽如此,如果你知道行的长度,从一列的单元格跳到下一个单元格还是相当容易的。看下面的例子:

void processColumn(double *array, int colIdx, int rowLen, int rowCnt) {
    for (int i = colIdx; i < rowCnt * rowLen; i += rowLen) {
       // do whatever you want
    }
}

#define N 5
#define M 10

double array[N*M];

processColumn(array, 3, N, M);   

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