C-使用malloc作为字符的地址**

时间:2018-07-19 13:09:02

标签: c malloc

我正在尝试用C创建井字游戏,并且我具有创建游戏板的功能:

void createBoard(char*** board, int size)
{   
    int i, j;
    *(board) = malloc(size * sizeof( char* ));
    for(i = 0; i < size; i++)
    {
        *(board)[i] = malloc(size * sizeof(char));
    }

    printf("Default board:\n");

    for(i = 0; i < size; i++)
    {
        for(j = 0; j < size; j++)
        {
            *(board)[i][j] = '-';
            printf("%c", *(board)[i][j]);
            printf("  ");
            if((j + 1) % size  == 0)
                printf("\n");
        }
    }
}

在主函数中,我将一个字符的地址传递给此函数**:

int main()
{
    int size;
    char** board = NULL;
    char userInp;

    sizeOfBoard(&size);

    createBoard(&board, size);
}

,并且我使用*(board)访问指针的值(首先是NULL),然后将malloc返回的地址分配给它,但是我不知道为什么它不起作用。有人可以帮我解决此问题。

1 个答案:

答案 0 :(得分:2)

不要那样做。而是创建一个描述电路板和电路板状态的结构,然后使用该结构。在结构中,使用一维数组表示板状态:

typedef struct {
    int   rows;
    int   cols;
    char *cell;
}  board;
#define  BOARD_INIT  { 0, 0, NULL }

#define  CELL(b, r, c) ((b).cell[(c) + (r)*(b).cols])

要创建木板,您可以使用例如

int  create_board(board *b, const int rows, const int cols)
{
    const size_t  cells = (size_t)rows * (size_t)cols;
    const size_t  bytes = sizeof (board) + cells * sizeof (b->cell[0]);

    if (!b) {
        /* No board specified. */
        errno = EINVAL;
        return -1;
    }

    b->rows = 0;
    b->cols = 0;
    b->cell = NULL;

    if (rows < 1 || cols < 1) {
        /* Invalid size. */
        errno = EINVAL;
        return -1;
    }
    if ((size_t)(cells / (size_t)rows) != (size_t)cols ||
        (size_t)((bytes - sizeof (board)) / cells) != sizeof (b->cell[0])) {
        /* Board is too large. */
        errno = ENOMEM;
        return -1;
    }

    b->cell = malloc(bytes);
    if (!b->cell) {
        /* Board was too large. */
        errno = ENOMEM;
        return -1;
    }

    b->rows = rows;
    b->cols = cols;

    return 0;
}

void  free_board(board *b)
{
    if (b) {
        free(b->cell);
        b->rows = 0;
        b->cols = 0;
        b->cell = NULL;
    }
}

您可以使用CELL(board, row, column)访问宏,也可以使用以下安全的访问器功能:

char  get_cell(board *b, const int r, const int c, const char outside)
{
    if (!b || r < 0 || c < 0 || r >= b.rows || c >= b.cols)
        return outside;
    else
        return b->cell[r*b->cols + c];
}

void set_cell(board *b, const int r, const int c, const char value)
{
    if (b && r >= 0 && c >= 0 && r < b.rows && c < b.cols)
        b->cell[r*b->cols + c] = value;
}

get_cell()set_cell()都不会尝试在实际板之外进行访问。 (如果尝试访问无效的板或板外部,则outside的第四个参数get_cell()定义结果值。)


说实话,我更喜欢为单元格数组使用C99灵活数组成员。在那种情况下,结构就像

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>

typedef struct {
    int            rows;
    int            cols;
    unsigned char  cell[];
} board;

然后,board类型与FILE流句柄类似:您永远不会静态声明它们(board b;FILE f;),而要使用指向一个的指针({ {1}},board *b)。要创建新的木板,您可以使用例如

FILE *f

在C99中,我们可以将访问器函数声明为board *create_board(const int rows, const int cols) { board *b; const size_t cells = (size_t)rows * (size_t)cols; const size_t bytes = cells * sizeof (b->cell[0]); const size_t total = bytes + sizeof (board); /* Check for invalid size */ if (rows < 1 || cols < 1) { errno = EINVAL; return NULL; } /* Check for size overflow */ if ((size_t)(cells / (size_t)rows) != (size_t)cols || (size_t)(bytes / cells) != sizeof (b->cell[0]) || total <= bytes) { errno = ENOMEM; return NULL; } /* Allocate memory for the whole board */ b = malloc(total); if (!b) { /* Failed. Too large. */ errno = ENOMEM; return NULL; } /* Optional: Clear the entire structure. */ memset(b, 0, total); /* Initialize the board fields. */ b->rows = rows; b->cols = cols; return b; } void free_board(board *b) { if (b) { b->rows = 0; b->cols = 0; free(b); } } ,这意味着它们仅在相同的转换单元中可见(因此,您可以在声明static inline结构的头文件中声明这些访问器函数。也是如此),也向C编译器暗示它应该内联这些代码。基本上,board函数应与预处理器宏一样快,但也应提供类型安全性。

static inline

要打印电路板(使用static inline int get_cell(board *b, const int row, const int col, const int outside) { if (!b || row < 0 || col < 0 || row >= b->rows || col >= b->cols) return outside; else return b->cell[row * b->cols + col]; } static inline int set_cell(board *b, const int row, const int col, const int value, const int outside) { if (b && row >= 0 && col >= 0 && row < b->rows && col < b->cols) return b->cell[row * b->cols + col] = value; else return outside; } |+作为单元格之间的线条),可以使用例如

-

其中static int board_char(board *b, const int row, const int col) { if (b && row >= 0 && col >= 0 && row < b->rows && col < b->cols) { const int value = b->cell[row * b->cols + col]; if (isprint(value)) return value; else return ' '; } else return ' '; } void print_board(FILE *out, board *b) { if (out && b && b->rows > 0 && b->cols > 0) { const int lastrow = b->rows - 1; const int lastcol = b->cols - 1; int r, c; for (r = 0; r < lastrow; r++) { for (c = 0; c < lastcol; c++) { fputc(board_char(b, r, c), out); fputc('|', out); } fputc(board_char(b, r, lastcol), out); fputc('\n', out); for (c = 0; c < lastcol; c++) { fputc('-', out); fputc('+', out); } fputc('-', out); fputc('\n', out); } for (c = 0; c < lastcol; c++) { fputc(board_char(b, lastrow, c), out); fputc('|', out); } fputc(board_char(b, lastrow, lastcol), out); fputc('\n', out); } } 返回与所引用单元格相对应的描述性字符。上面的版本检查数组中的值是否是可打印的字符,如果是,则返回它;否则,返回它。否则,包括面板外的字符,它将返回一个空格。可以将board_char()if (isprint(value))语句一起使用,而不是switch (value) { ... }来声明用于每个单元格的每个值。

(例如,如果零表示未使用/空闲,1表示第一名玩家(例如case),2表示第二名玩家(例如X),则可以将3用作“已阻止”,将某些单元格标记为不可用,从而使游戏更加有趣。)

您可以使用宽泛的Curses库(例如ncursesw)在终端中使您的游戏具有交互性,并使用漂亮的箱形绘制字符绘制游戏板。例如,

O

您可以使用本地化和宽字符支持将上述类型的板打印到终端上,而无需使用Curses库,但是不幸的是Windows中会出现问题。 (截至目前,Microsoft仍在为终端/控制台应用程序提供适当的Unicode支持,而无需使用Microsoft特定的C扩展。)