Akudo
Akudo

Reputation: 165

New to C, Return pointer to 2D array

I've been trying to convert a program from Java to C, it's a emulator of a watch and to display the time I'm using Ascii art. I have stored all the numbers (0-9) in 2D char arrays (fx. 9):

char nine[7][5] = {
    { '0', '0', '0', '0' },
    { '0', ' ', ' ', '0' },
    { '0', ' ', ' ', '0' },
    { '0', '0', '0', '0' },
    { ' ', ' ', ' ', '0' },
    { ' ', ' ', ' ', '0' },
    { ' ', ' ', ' ', '0' }
};

I now have a function which job is to convert the time stored in a int array (fx. 22:04:59, would be stored as 220459 in the array). The function should return the corresponding Ascii art to each digit, so that I finally can call the function that prints the time (in ascii form) that takes 6 char[][] parameters.

So in short, I need to know which 6 parameters to call this function with:

void printScreen(char hourFirstDigit[7][5], char hourSecondDigit[7][5], char minuteFirstDigit[7][5], char minuteSecondDigit[7][5], char secFirstDigit[7][5], char secSecondDigit[7][5])

In java my solution was simply to make a function that returned a char[][] array, and then a switch statement for the 10 cases (0-9), (here's the first few lines):

char timeToAsciiArt[][](int digitNumber, int small) {
switch (timeRightNow[digitNumber]) {
    case 0:
    return zero;
}

Possible solution: I've read that there where at least two possible solutions to the problem (in general), 1. Replace by a pointer to an array. 2. Wrap with a struct.

My thoughts on: 1. I'm really not sure how I would return to a pointer to an array (could someone explain how to do this with case 0: as an example? :)

Upvotes: 5

Views: 619

Answers (4)

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

I am suggesting the following.approach.

Define a function that will contain an array of the images with static storage duration.

For example

char ( * )[7][5] get_image( size_t i )
{
    static char images[10][7][5] = 
    {
        //...
        {
            { '0', '0', '0', '0' },
            { '0', ' ', ' ', '0' },
            { '0', ' ', ' ', '0' },
            { '0', '0', '0', '0' },
            { ' ', ' ', ' ', '0' },
            { ' ', ' ', ' ', '0' },
            { ' ', ' ', ' ', '0' }
        }
    };

    const size_t N = sizeof( images ) / sizeof( *images );

    return i < N ? images[i] : NULL;
}

Or maybe it will be better if the function has return type char ( * )[5]

For example

char ( * )[5] get_image( size_t i )
{
    static char images[10][7][5] = 
    {
        //...
        {
            { '0', '0', '0', '0' },
            { '0', ' ', ' ', '0' },
            { '0', ' ', ' ', '0' },
            { '0', '0', '0', '0' },
            { ' ', ' ', ' ', '0' },
            { ' ', ' ', ' ', '0' },
            { ' ', ' ', ' ', '0' }
        }
    };

    const size_t N = sizeof( images ) / sizeof( *images );

    return i < N ? images[i][0] : NULL;
}

Then write a function that will return in a structure an array of 6 elements with corresponding digits. These elements will serve as arguments to call function get_image.

That is enough.

Upvotes: 2

downhillFromHere
downhillFromHere

Reputation: 1977

Your idea of using arrays and pointers would more or less translate to :

char (* timeToAsciiArt(unsigned timeNow[6], unsigned digitNumber) )[5] {
    static char asc0[7][5] = { //fill in };
    static char asc1[7][5] = { //fill in };
    switch (timeNow[digitNumber]) {
    case 0:
        return asc0;
    case 1:
        return asc1;
    ...
 }

You can then pass the returned art to print_screen by

unsigned timeNow[] = {1, 2, 3, 4, 5, 6};
char (*first_digit)[5] = timeToAsciiArt(timeNow, 0);
printScreen(first_digit, ... , );

or directly

printScreen( timeToAsciiArt(timeNow, 0) , ...);

In the end, this serves to say that wrapping in a struct will gain you a much more readable code.

Upvotes: 1

Victor Schubert
Victor Schubert

Reputation: 323

As I understand, you wish to have :

  • Some kind of storage for your ASCII art ;
  • A function which receives a digit and return some pointer to the corresponding ASCII art storage ;
  • A function which receives a set of ASCII arts and prints them.

Storing the ASCII art

It could be wise to wrap your ASCII art into a structure, since you could then store more data about it. Perhaps in the future you will want to have a thinner 1 digit ; you would need to store data about the size of each ASCII art :

struct ascii_digit {
    unsigned int width;
    unsigned int height;
    char[MAX_HEIGHT][MAX_WIDTH] art; //Here you could instead use a pointer
                                     //instead of storing directly
}

Of course if you absolutely don't plan on having something other than a fixed size, arrays are fine.


Finding the correct ASCII art

When passing arrays around in C, you usually do not pass the array directly : you usually use a pointer to it. So a correct prototype for your function would not be

char time_to_ascii_art[][](int digit_number, int small);

but rather, if you use structures

struct ascii_digit* time_to_ascii_art(int digit_number, int small);

Note that we return a pointer to a structure. While it is absolutely possible to directly pass a structure, it may not be considered good practice as it induces some overhead depending on the size of your structure.

or you can use a naïve approach using pointers to char :

char* time_to_ascii_art(int digit_number, int small);

Note that if you have a char* pointer to a bidimensional array, you will have to do the math by yourself when trying to access its contents. For example, accessing the yth member of the xth row : array[width * x + y]. To spare yourself from doing this, you can use pointers to arrays :

char (*time_to_ascii_art)(int digit_number, int small)[ASCII_ART_WIDTH];

In this function you could either use a switch … case statement like you did in Java ( example using structures ) :

// Let's say that your structures are declared in the global scope as ascii_zero, ascii_one…
struct ascii_digit* time_to_ascii_art(int digit_number, int small)
{
    switch(digit_number) {
        case 0:
          return ascii_zero;
        case 1:
          return ascii_one;
        default:
          return NULL;
    }
}

or you could — which would also probably be a good idea in Java — have an array containing your ASCII arts, or pointer to them, indexed so that accessing the nth member will give you the ASCII art for the digit n :

// Let's now say you have an ascii_digits array of structs containing your digits
struct ascii_digit* time_to_ascii_art(int digit_number, int small)
{
    return ascii_digits + digit_number; // You should handle bad values.
    // ascii_digits being an array, it is implicitly cast to a pointer here.
}

Passing the ASCII arts to the display function

Now to pass your ASCII arts to your display function you can — depending on the datatype you chose — use either pointers to structures :

void print_screen(struct ascii_digit* hour_first_digit, …);

pointers to char :

void print_screen(char* hour_first_digit, …);

or pointers to array of chars :

void print_screen(char (*hour_first_digit)[ASCII_ART_WIDTH], …);

Upvotes: 2

hexasoft
hexasoft

Reputation: 677

In C an array is a pointer. When you pass a parameter it is always by copy, so when you "send" an array you send a copy of a pointer, not a copy of the array.

Structs are copied by value (as int, float…), and can have inside a static array:

typedef struct {
  char tab[7][5];
}Digit;

So you can create variables: Digit zero, one, two… and set values into it: zero.tab[0][0] = ' ';. And you can pass it to functions or return it, it will be copied.

Now for dealing with pointers (better in my opinion as you don't need to duplicate these arrays): an array is still a pointer. But "static" 2D arrays are a bit strange (it is not an array of arrays). As mentionned in comment by EOF your arrays are in fact (*)[5]. So it lead to something a little bit complicated to match all the types:

char (*select_digit(int val))[5] {
  if (value == 0) return zero;
  if (value == 1) return one;
  …
}

Your printScreen function is the same. To store a pointer returned by this select_digit function you have to declare it like this:

char (*tmp)[5];
tmp = select_digit(5);
printScreen(tmp, …);

Upvotes: 1

Related Questions