C的新手,返回指向2D数组的指针

时间:2022-05-31 21:37:26

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

我一直在尝试将程序从Java转换为C,它是一个手表的模拟器,并显示我使用Ascii艺术的时间。我已将所有数字(0-9)存储在2D字符数组(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.

我现在有一个函数,它的作用是转换存储在int数组中的时间(fx.22:04:59,将在数组中存储为220459)。该函数应该将相应的Ascii art返回给每个数字,这样我最终可以调用打印时间(以ascii形式)的函数,该函数需要6个char [] []参数。

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

所以简而言之,我需要知道调用此函数的6个参数:

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

在java中,我的解决方案只是创建一个返回char [] []数组的函数,然后是10个案例(0-9)的switch语句,(这里是前几行):

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.

可能的解决方案:我已经读过那里至少有两个可能解决问题的方法(一般来说),1。用指向数组的指针替换。 2.用结构包装。

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

我的想法:1。我真的不确定如何返回指向数组的指针(有人可以解释如何使用案例0:作为一个例子?:)

4 个解决方案

#1


2  

As I understand, you wish to have :

据我了解,您希望:

  • Some kind of storage for your ASCII art ;
  • 您的ASCII艺术的某种存储;

  • A function which receives a digit and return some pointer to the corresponding ASCII art storage ;
  • 一个接收数字并返回指向相应ASCII艺术存储的指针的函数;

  • A function which receives a set of ASCII arts and prints them.
  • 一个接收一组ASCII艺术并打印它们的函数。


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 :

将ASCII艺术包装到结构中可能是明智之举,因为您可以存储更多关于它的数据。也许在将来你会希望有一个更薄的1位数;您需要存储有关每个ASCII艺术品大小的数据:

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

在C中传递数组时,通常不直接传递数组:通常使用指向它的指针。所以你的功能的正确原型不会

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的指针:

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 *指针,则在尝试访问其内容时,您必须自己进行数学运算。例如,访问第x行的第y个成员:array [width * x + y]。为了避免这样做,您可以使用指向数组的指针:

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

在这个函数中,您可以像在Java中一样使用switch ... case语句(使用结构的示例):

// 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 :

或者你可以 - 在Java中也可能是一个好主意 - 有一个包含你的ASCII艺术的数组,或指向它们的指针,索引,以便访问第n个成员将为你提供数字n的ASCII艺术:

// 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 :

现在将您的ASCII艺术传递给您的显示功能,您可以 - 根据您选择的数据类型 - 使用指向结构的指针:

void print_screen(struct ascii_digit* hour_first_digit, …);

pointers to char :

指向char的指针:

void print_screen(char* hour_first_digit, …);

or pointers to array of chars :

或指向字符数组的指针:

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

#2


2  

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]

或者如果函数有返回类型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.

然后编写一个函数,它将在结构中返回一个包含相应数字的6个元素的数组。这些元素将作为调用函数get_image的参数。

That is enough.

足够了。

#3


1  

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.

在C中,数组是指针。当您传递参数时,它总是通过复制,因此当您“发送”数组时,您会发送指针的副本,而不是数组的副本。

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

结构按值复制(如int,float ...),并且可以包含在静态数组中:

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.

所以你可以创建变量:数字零,一,二......并设置值:zero.tab [0] [0] ='';.你可以将它传递给函数或返回它,它将被复制。

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:

现在处理指针(在我看来更好,因为你不需要复制这些数组):数组仍然是一个指针。但“静态”2D数组有点奇怪(它不是数组数组)。正如EOF在评论中提到的那样,你的数组实际上是(*)[5]。所以它导致一些有点复杂的东西匹配所有类型:

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:

您的printScreen功能是相同的。要存储此select_digit函数返回的指针,您必须将其声明为:

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

#4


1  

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

然后,您可以将返回的作品传递给print_screen

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.

最后,这可以说在结构中包装将获得更易读的代码。

#1


2  

As I understand, you wish to have :

据我了解,您希望:

  • Some kind of storage for your ASCII art ;
  • 您的ASCII艺术的某种存储;

  • A function which receives a digit and return some pointer to the corresponding ASCII art storage ;
  • 一个接收数字并返回指向相应ASCII艺术存储的指针的函数;

  • A function which receives a set of ASCII arts and prints them.
  • 一个接收一组ASCII艺术并打印它们的函数。


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 :

将ASCII艺术包装到结构中可能是明智之举,因为您可以存储更多关于它的数据。也许在将来你会希望有一个更薄的1位数;您需要存储有关每个ASCII艺术品大小的数据:

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

在C中传递数组时,通常不直接传递数组:通常使用指向它的指针。所以你的功能的正确原型不会

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的指针:

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 *指针,则在尝试访问其内容时,您必须自己进行数学运算。例如,访问第x行的第y个成员:array [width * x + y]。为了避免这样做,您可以使用指向数组的指针:

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

在这个函数中,您可以像在Java中一样使用switch ... case语句(使用结构的示例):

// 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 :

或者你可以 - 在Java中也可能是一个好主意 - 有一个包含你的ASCII艺术的数组,或指向它们的指针,索引,以便访问第n个成员将为你提供数字n的ASCII艺术:

// 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 :

现在将您的ASCII艺术传递给您的显示功能,您可以 - 根据您选择的数据类型 - 使用指向结构的指针:

void print_screen(struct ascii_digit* hour_first_digit, …);

pointers to char :

指向char的指针:

void print_screen(char* hour_first_digit, …);

or pointers to array of chars :

或指向字符数组的指针:

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

#2


2  

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]

或者如果函数有返回类型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.

然后编写一个函数,它将在结构中返回一个包含相应数字的6个元素的数组。这些元素将作为调用函数get_image的参数。

That is enough.

足够了。

#3


1  

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.

在C中,数组是指针。当您传递参数时,它总是通过复制,因此当您“发送”数组时,您会发送指针的副本,而不是数组的副本。

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

结构按值复制(如int,float ...),并且可以包含在静态数组中:

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.

所以你可以创建变量:数字零,一,二......并设置值:zero.tab [0] [0] ='';.你可以将它传递给函数或返回它,它将被复制。

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:

现在处理指针(在我看来更好,因为你不需要复制这些数组):数组仍然是一个指针。但“静态”2D数组有点奇怪(它不是数组数组)。正如EOF在评论中提到的那样,你的数组实际上是(*)[5]。所以它导致一些有点复杂的东西匹配所有类型:

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:

您的printScreen功能是相同的。要存储此select_digit函数返回的指针,您必须将其声明为:

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

#4


1  

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

然后,您可以将返回的作品传递给print_screen

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.

最后,这可以说在结构中包装将获得更易读的代码。