首页 > 解决方案 > C - 未知维度的2D游戏板的动态分配

问题描述

我想编写一个基于棋盘的游戏,并希望将棋盘表示为 2Dchar数组。特别是,该板由 4 种不同类型的字符/单元格组成:

怪物朝某个方向看,上面的箭头状字符表示。

我希望能够从以下未知的文本文件中加载关卡:

一个示例级别文本文件:

    ######                                 ##########
    #  < #                                 #        #
    #    #########                         #       ##
    #    #       #                         #^      #
    #    #       #                        ##       #
  ###            #      ########        ###        #
  #     #   ######      #      ##########   #     ##
  #     #^  #           #                 #########

如您所见,前 3 行有 49 个字符,但其他行包含 48/47 个字符。差异可能要大得多,因此我需要对两个维度都使用动态分配。

我应该使用固定大小的缓冲区逐个字符还是逐行读取文件,并在必要时扩展它?

这是我尝试过的:

int needed_num_rows = 1;
int needed_bytes_col = 16;

char **board = calloc(needed_num_rows, sizeof(char*));
char *buf = calloc(needed_bytes_col, sizeof(char));

int idx = 0;
while (fgets(buf, needed_bytes_col, level_file) != NULL) {
    if (buf[needed_bytes_col - 1] != '\n' && buf[needed_bytes_col - 1] != '\0') { // not read to the end yet
        needed_bytes_col = needed_bytes_col * 2 + 1;
        buf = realloc(buf, needed_bytes_col);
        buf += needed_bytes_col;
    } else { // whole line has been read
        if (idx + 1 > needed_num_rows) {
            needed_num_rows = needed_num_rows * 2 + 1;
            board = realloc(board, needed_num_rows);
        }

        board[idx++] = buf;
        needed_bytes_col = 16;
        buf = calloc(needed_bytes_col, sizeof(char));
    }
}

标签: cmultidimensional-arraydynamic-memory-allocation

解决方案


这似乎有点复杂,但是你看它。我使用mmap()了和一个行解析功能。这个函数只做换行查找,加上跳过重复的并检查长度。这个想法是有三个返回:-1 = 完成,-2 = 跳过这一行,正 = 使用这一行(或使用第二个参数,例如结构)。

因此,即使只解析行的存在和长度,它仍然是某种解析。主要部分是:

plret = parse_lines(&pmap)

pmapmmap()映射的移动副本。parse_lines()推进它。plret告诉您是 1) 停止 2) 继续还是 3) 行动并继续。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>

/* file must have NL at end, and memory a \0 */
int parse_lines(char **pmap) {

    int const maxline = 60;
    char *map = *pmap;
    while(*map == '\n')            //skip empty lines??? 
        map++;

    if (! *map)
        return -1;                 // means EOF 

    char *nnl = strchr(map, '\n');  
    *pmap = nnl + 1;               // new position for caller: start of next line

    if (nnl - map < maxline) 
        return nnl - map;          // return length of line  
    else
        return -2;                 // too long  
}
void parse_file() {
    char const *f = "test";
    size_t const mmlen = 256*256;                     // or fstat()

    int fd = open(f, O_RDONLY);
    char *mapping = mmap(0, mmlen, PROT_READ, MAP_PRIVATE, fd, 0),
         *pmap = mapping;  
    
    int plret;
    while ((plret = parse_lines(&pmap)) != -1) 

        if (plret > 0) {

            char *s = calloc(plret+1, 1);
            memcpy(s, pmap-plret-1, plret);           // NL (+1) or not? 

            printf("%d at %p now %p:\t\"%s\"\n", plret, pmap, s, s);
            free(s);                                  // rather store it in an array
        } 
        else
            printf("line too long - skip or truncate or abort\n"); 

    /* End of File */
    munmap(mapping, mmlen);
    // + realloc array for @s-pointers, if it was allocated "big enough"
}
int main(void) {
    parse_file();
}

我遗漏了数组arr[idx++] = s(没有检查/重新分配)。但是输出很好地说明了正在发生的事情(倒数第三行超长):

53 at 0x7fdc29283036 now 0x5557230822a0:        "    ######                                 ##########"
53 at 0x7fdc2928306c now 0x5557230826f0:        "    #  < #                                 #        #"
53 at 0x7fdc292830a2 now 0x555723082730:        "    #    #########                         #       ##"
52 at 0x7fdc292830d7 now 0x555723082770:        "    #    #       #                         #^      #"
52 at 0x7fdc2928310c now 0x5557230827b0:        "    #    #       #                        ##       #"
52 at 0x7fdc29283141 now 0x5557230827f0:        "  ###            #      ########        ###        #"
52 at 0x7fdc29283176 now 0x555723082830:        "  #     #   ######      #      ##########   #     ##"
51 at 0x7fdc292831aa now 0x555723082870:        "  #     #^  #           #                 #########"
line too long - skip or truncate or abort
2 at 0x7fdc292831fd now 0x5557230828b0: "##"
1 at 0x7fdc292831ff now 0x5557230828d0: "#"

0x7fd 处的字节......在 0x555 处作为字符串找到了一个新家......

他们丢失了换行符,但现在有一个空终止符。


推荐阅读