Ссылка на столбец 2-D массива в C? - PullRequest
4 голосов
/ 30 марта 2009

Есть ли простой способ ссылаться на столбец в 2-D массиве как отдельный 1-D массив в обычном старом 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, кажется, должен быть способ сделать это.

Ответы [ 5 ]

5 голосов
/ 30 марта 2009

Ну, вам нужно передать размер строки и количество строк:

 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;
}
3 голосов
/ 30 марта 2009

Хороший типобезопасный способ сделать это без указания размеров в качестве отдельных параметров:

#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)
}

Если вы передали двойное **, а затем получили доступ к нему, как к массиву, это вызвало бы крах, сегфоут или неопределенное поведение.

0 голосов
/ 30 марта 2009

Нет, нет. Не может быть, поскольку в 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);   
0 голосов
/ 30 марта 2009

Вы не можете этого сделать, потому что массивы в C хранятся так, что элементы каждой строки хранятся вместе. Это означает, что строка массива является непрерывным блоком памяти, и, что касается C, это также может быть независимый массив. Это не работает так же со столбцами, потому что элементы столбца не являются непрерывными в памяти; скорее они расположены с интервалом N байтов, где каждая строка имеет длину N байтов. Это означает, что вы можете эффективно получить доступ к различным элементам столбца двумерного массива, используя арифметику указателей, но на самом деле нет способа фактически преобразовать столбец в сам массив, кроме как путем копирования элементов в новый массив.

0 голосов
/ 30 марта 2009

Поскольку «столбцы», как вы их называете, хранятся в памяти с перерывами, реального способа напрямую справиться с этим не существует.

Однако вы можете создать массив указателей и сохранить ссылки на индексы другого массива в этом. Вам нужно было бы перебрать все элементы в вашем массиве, так что это, вероятно, не лучшее решение, чем любое другое. Однако, в зависимости от того, как часто вам нужен доступ к массиву по столбцам, это может быть целесообразным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...