【问题标题】:Reference a 2-D array column in C?引用 C 中的二维数组列?
【发布时间】:2009-03-30 19:47:58
【问题描述】:

普通的旧 C(不是 C++ 或 C#)中,是否有一种简单的方法可以将二维数组中的列作为单独的一维数组引用?连续这样做很容易。假设我有 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 中引用指针的所有复杂方法,似乎应该有一种方法可以做到这一点。

【问题讨论】:

    标签: c arrays


    【解决方案1】:

    好吧,你必须传递行的大小和行数:

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

    现在你可以利用 matrix[i][j] = matrix + i * rowSize + 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;
    }
    

    【讨论】:

    • 这不会像你想的那样。传入双**是完全错误的。这将导致应用程序中的段错误。我会在我的回答中解释更多。
    • 双**不是必须的;我在编写示例代码时注意到了这一点。然而,它与段错误无关。
    • 它确实更干净,但不适用于任意维度的数组。因此,这取决于情况,首选哪种方法。
    • 这是一种有用且经常使用的方法。但请注意,这也是未定义的行为。 (阅读groups.google.com/group/comp.lang.c/msg/327b816c5ea5d97b了解原因)
    • 我给 +1 是因为它是一种有用的技术:p
    【解决方案2】:

    在不将维度指定为单独参数的情况下执行此操作的一种不错的类型安全方法如下:

    #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。除此之外,如果您不顾一切地避免使用容器,这是一个很好的解决方案。
    • +1,因为如果程序中使用的数组的维度是固定的,则此代码优于我的解决方案。
    【解决方案3】:

    由于您所称的“列”不连续地存储在内存中,因此没有真正的方法可以直接将其关闭。

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

    【讨论】:

      【解决方案4】:

      你不能真正做到这一点,因为 C 中的数组是这样存储的,每行的元素都存储在一起。这意味着数组的一行是一个连续的内存块,就 C 而言,它也可能是一个独立的数组本身。它与列的工作方式不同,因为列的元素在内存中不连续;相反,它们以N 字节为间隔,其中每一行的长度为N 字节。这意味着您可以通过使用指针算法有效地访问 2D 数组列的各种 元素,但是除了将元素复制到一个新的数组。

      【讨论】:

        【解决方案5】:

        不,没有。不可能,因为在 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);   
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-07-30
          • 2018-09-25
          • 2020-06-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-09-19
          相关资源
          最近更新 更多