Home > Software design >  How to populate every row of two dimensional array C
How to populate every row of two dimensional array C

Time:10-10

I'm starting to learn C and C in context of embedded devices, and recently I started to make my own libraries, to clean up my code a little bit. I have a problem with passing two dimensional array to my function. The problem is as follows:

uint8_t cardSectors[8][16];
cardsClient.readDataBlocks(uid, uidLength, *cardSectors, 8, 16);

As you can see I'm passing cardSectors as pointer to the array. Later in the function reading the card, I'm writing the card data onto the pointer by using:

mifareclassic_ReadDataBlock(currentBlock, &cardSectors[currentBlock]);

And it works! I can read the data by using: &cardSectors[currentBlock].

But here is the catch. What about reading the data from original variable uint8_t cardSectors[8][16] ? I cannot get into data that I want no matter what I did. I don't know if I'm not saving this data really or something else is wrong. Basically this is a call stack:

uint8_t cardSectors[8][16];
cardsClient.readDataBlocks(uid, uidLength, *cardSectors, blocks, blockSize);
Serial.println("Do something with this data!");

How one goes about accessing this data after readDataBlocks was executed?

CodePudding user response:

In C , it is probably easiest to

#include <vector>
template <class T>
using Arr2D = std::vector<std::vector<T>>;

and to pass this type around.

Since C is just barely above the level of an assembly language, lacking templates and other convenient concepts, one way to approach 2D arrays is to have a linear chunk of memory and to calculate the index into the memory with some formula like ìndex = row * ncols col. This saves you some work constructing and cleaning up of some arrays of pointers to something structure and it also gives better cache locality compared to allocating each row individually on the heap.

typedef struct Arr2D_tag {
  void* data;
  size_t element_size;
  size_t nrows;
  size_t ncols;
} Arr2D_t;
void* arr2d_at(Arr2D_t *array, size_t row, size_t col) {
  if (NULL == array) return NULL;
  if (NULL == array->data) return NULL;
  if (row >= array->nrows) return NULL;
  if (col >= array->ncols) return NULL;
  size_t index = row * array->ncols   col;
  return (char*)array->data   array->element_size * index;
}

Along with the usual C style wild casting to the type you expect. You can pretty this up further, by having a per-type facade to the thing (a struct of function pointers for the operations on an array of a specific type, constructed with some preprocessor macros or something). It will still look ugly but may be a tad more convenient to use.

If you only have fixed size matrices, like 3x3 or 4x4 as is so often in the context of computer graphics, you could alternatively to the first given C version use std::array instead, giving you the extra benefits of not having to use the heap and having the same cache locality as the C version:

#include <array>
template <size_t NROW, size_t NCOL>
using Arr2D_f32 = std::array<std::array<float,NCOL>, NROW>;
// ad lib for other types, e.g.
template <size_t NROW, size_t NCOL>
using Arr2D_f64 = std::array<std::array<double,NCOL>, NROW>;

Once you have those types, populating them is easy:

void populate_my_array2d(Arr2D_size_t<7,13>& a) {
  size_t value = 1;
  for (auto& row : a) {
    for (auto& col : row) {
      col = value  ;
    }
  }
}

In the C-case, since the underlaying storage is just a 1d array, to just fill the matrix with a single value, you only need 1 loop, instead of 2 nested loops. Here the general case:

#include <stdbool.h>
bool populate_my_array( Arr2D_t * array, const void* value) {
  if (NULL == array) return false;
  for (size_t r = 0; r < array->nrows; r  ) {
    for (size_t c = 0; c < array->ncols; c  ) {
       void* cell = arr2d_at(array,r,c);
       if (NULL != cell) {
         memcpy(cell,value,array->element_size);
       } else {
         return false;
       }
    }
  }
  return true;
} 
  • Related