C语言新手,如何返回一个2D数组的指针?

5

我一直在尝试将一个Java程序转换为C语言,这是一个手表模拟器,我使用Ascii艺术来显示时间。我已经将所有数字(0-9)存储在二维字符数组中(例如:9):

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

我现在有一个函数,其工作是将存储在int数组中的时间转换(例如22:04:59,将存储为220459的数组)。该函数应返回与每个数字对应的Ascii艺术品,以便最终可以调用打印时间的函数(以ascii形式),该函数需要采用6个char [] []参数。
简而言之,我需要知道使用哪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])

在Java中,我的解决方案很简单,只需编写一个返回char[][]数组的函数,然后使用一个switch语句来处理10个情况(0-9),以下是前几行代码:
char timeToAsciiArt[][](int digitNumber, int small) {
switch (timeRightNow[digitNumber]) {
    case 0:
    return zero;
}

可能的解决方案: 我读到至少有两种可能的解决方案(一般情况下),1. 用数组指针替换。2. 用结构体包装。
我的想法: 1. 我真的不确定如何返回一个指向数组的指针(可以有人给出例子吗?:)。

2
char (*timeToAsciiArt(int, int))[7][5] {switch(/**/){case 0: return &zero;}}。或者:char (*timeToAsciiArt(int, int)[5] {switch(/**/){case 0: return zero;}} - EOF
非常感谢,我最终采用了你的解决方案 :)。 - Akudo
4个回答

2
我建议采用以下方法。
定义一个包含具有静态存储期的图像数组的函数。
例如:
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;
}

或许如果这个函数返回类型是 char ( * )[5] 会更好。

例如:

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;
}

然后编写一个函数,该函数将以结构体的形式返回一个由6个元素组成的数组,这些元素将对应于相应的数字。这些元素将用作调用函数get_image的参数。

就这么多。


2
我理解您希望有以下内容:
  • 一种ASCII艺术的存储方式;
  • 一个函数,接收数字并返回相应ASCII艺术存储的指针;
  • 一个函数,接收一组ASCII艺术并将其打印出来。

存储ASCII艺术

将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
}

当然,如果你绝对不打算使用固定大小以外的内容,那么数组就可以了。


找到正确的ASCII艺术

在C语言中传递数组时,通常情况下不会直接传递数组:通常会使用指向它的指针。因此,函数的正确原型不应该是:

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

相反,如果您使用结构体

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

请注意我们返回的是一个结构体指针。虽然直接传递结构体是完全可行的,但这可能不被视为良好的实践,因为它会根据您的结构体大小引入一些开销。
或者,您可以使用指向char的指针的朴素方法:
char* time_to_ascii_art(int digit_number, int small);

注意,如果你有一个指向二维数组的char*指针,在尝试访问它的内容时,你需要自己进行数学计算。例如,访问第x行的第y个元素:array[width * x + y]。为了避免这种情况,你可以使用数组指针:
char (*time_to_ascii_art)(int digit_number, int small)[ASCII_ART_WIDTH];

在这个函数中,您可以像在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;
    }
}

或者你也可以在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.
}

将ASCII艺术传递给显示函数

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

void print_screen(struct ascii_digit* hour_first_digit, …);

指向char的指针:

void print_screen(char* hour_first_digit, …);

或者指向字符数组的指针:
void print_screen(char (*hour_first_digit)[ASCII_ART_WIDTH], …);

1
在C中,数组指针。当你传递参数时,它总是按副本传递的,因此当你“发送”一个数组时,你发送的是指针的副本,而不是数组的副本。
结构体按值复制(如intfloat等),并且可以包含静态数组:
typedef struct {
  char tab[7][5];
}Digit;

所以你可以创建变量:Digit zero, one, two…并将值设置到其中:zero.tab[0][0] = ' ';。你可以将它传递给函数或返回它,它将被复制。

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

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

你的printScreen函数没有变化。 要存储此select_digit函数返回的指针,您需要像这样声明它:
char (*tmp)[5];
tmp = select_digit(5);
printScreen(tmp, …);

1
您使用数组和指针的想法可以更或多或少地转化为:
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;
    ...
 }

你可以将返回的图像传递给print_screen函数。
unsigned timeNow[] = {1, 2, 3, 4, 5, 6};
char (*first_digit)[5] = timeToAsciiArt(timeNow, 0);
printScreen(first_digit, ... , );

或者直接
printScreen( timeToAsciiArt(timeNow, 0) , ...);

最终,这表明在struct中包装将使您的代码更易读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接