Reputation: 165
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
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
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
Reputation: 323
As I understand, you wish to have :
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.
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 y
th member of the x
th 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 n
th 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.
}
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
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