Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference a 2-D array column in C?

Tags:

arrays

c

Is there an easy way to reference a column in a 2-D array as a separate 1-D array in plain old C (not C++ or C#)? It's easy to do this for a row. Asssume I have 2 functions:

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

Then, I might use the first one like this:

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

What I want it a way to access a column easily.

for( int j=0; j < 3; j++ )
{
   result = doSomethingWithACol( ??????????? );
}

The only thing I've come up with so far is transforming the matrix to swap the rows with the columns. But this code is supposed to be as efficient as possible in terms of memory and speed. With all of the convoluted ways to reference pointers in C, it seems like there should be a way to do this.

like image 575
Keith G Avatar asked Feb 28 '23 21:02

Keith G


2 Answers

Well, you'd have to pass the size of a row, and the number of rows:

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

Now you can make use of the fact that matrix[i][j] = matrix + i * rowSize + j;

Alternatively, you can also use the following signature:

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

Here, you'll have to pass the pointer to the first element of the column that you want to process, instead of the pointer to the first row.


Example code: This code sums the elements in the second column (compile with 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;
}
like image 163
Stephan202 Avatar answered Mar 10 '23 11:03

Stephan202


A nice typesafe way to do this without specifying the dimensions as a separate parameters is as follows:

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

It is completely wrong to pass a double ** for this reason:

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

If you passed in a double ** then accessed it like an array you would cause a crash, segfault or undefined behavior.

like image 20
Brian R. Bondy Avatar answered Mar 10 '23 11:03

Brian R. Bondy