Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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? :)

like image 702
Akudo Avatar asked Nov 05 '15 11:11

Akudo


2 Answers

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], …);
like image 138
Victor Schubert Avatar answered Sep 28 '22 01:09

Victor Schubert


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.

like image 41
Vlad from Moscow Avatar answered Sep 27 '22 23:09

Vlad from Moscow