【问题标题】:RPG inventory in C programmingC 编程中的 RPG 库存
【发布时间】:2013-08-04 09:24:18
【问题描述】:

我是 C 的新手,对于我的班级,我们有一个项目,我们必须在其中编写一个函数,该函数读取文件中的所有项目并以文件顺序在动态创建的列表中返回它们。 文件的每一行都是一项,格式如下:

'<description>' <damage> <cost> <weight>

它应该按文件顺序返回指向文件中项目的基指针,如果文件不存在,则返回 NULL。

到目前为止,这是我的代码:

item_t *ReadItemsFromFile(char *file)
{
typedef struct item item_t;
struct item
{
   char name[32];
   float cost, weight;
   int dam;
};FILE *fpin = fopen(file, "r");

if(fpin != NULL)
{
  item_t i[20];
  int n = 0;
  char line[sizeof(file)];
  while(fgets(line, sizeof(line), fpin) != NULL){
      (fscanf(fpin, " '%[^']' %d %f %f", i[n].name, &i[n].dam, &i[n].cost, &i[n].weight));
  fputs(i[n].name, stdout);
  n++;
}
} else {
  return NULL;
} 
  return(0);
}

我一直在使用 fputs 来尝试测试代码,但它总是让我在第一个和最后一个项目上出现乱码。 另外,当我尝试放

fputs(&i[n].dam, stdout);

所以我可以测试结构的其他变量,我不断收到错误消息“从不兼容的指针类型传递 'fputs' 的参数 1。

我不确定我是否使用 fscanf 准确地将结构变量传递给了结构变量,或者它是否是其他东西。

【问题讨论】:

  • 请在使用前阅读本网站上的标签信息。 RPG是一种计算机语言。 (咆哮警报:RPG 也不是很能描述电脑游戏。你能说“我正在扮演……的角色”的游戏比例有多大?即使回到旧的电子游戏:我正在玩青蛙过马路的角色。我扮演宇宙飞船飞行员的角色,向小行星射击。等等)
  • 别忘了关闭你成功打开的文件。您的“列表”是否应该包含指向列表中下一项的指针,还是可以接受数组?

标签: c struct inventory


【解决方案1】:

您需要将struct 定义移到您的函数之外。

您对线应该多长的规范是错误的。您需要指定足够长的字节数以容纳一整行。

当您使用fgets() 获取一行时,您不应该使用fscanf() 从文件中读取。您已经在使用 fgets() 从文件中读取。您使用sscanf() 从包含在line 中的字符串中读取数据。

以上内容足以让你的程序做一些名义上有用的事情。

要返回项目列表,您需要实际调用malloc()(或其他类似函数)来为您指定为要求的“动态创建的列表”创建内存。由于您不知道自己有多少项目,您要么需要一种方法来发现有多少项目,要么使用一种机制来让您的列表动态增长。

【讨论】:

  • 成功了!我最终只使用 fscanf() 而不是使用 fgets()。我将如何 malloc 结构?最好是在 while/if 循环中还是在 if 循环之外进行 malloc?
  • 您可以malloc() 一次或一次使用一个结构。如果您一次做一个,您需要一种方法将它们收集在一起,以便您可以将集合返回给调用者。如果您一次完成所有操作,您只需将指针返回到单个大分配的开头(也就是说,您将使用malloc() 动态分配一个数组)。
  • 我试图一次在 while 循环中使用 malloc(),但是当我试图在最后返回指针时,它说它没有被声明。
  • 当你声明一个变量时,它的作用域是最近的大括号。如果您希望能够使用变量的值,则该变量必须在您尝试使用它的范围内。
【解决方案2】:

一个问题是您使用fgets 将文件中的一行读入line,但随后您忽略该行并使用fscanf 从下一行读取。您应该使用sscanf 来解析您刚刚阅读的行。或者,根本不要使用fgets,而直接使用fscanf作为while条件:

while(4 == fscanf(fpin, " '%[^']'%d%f%f", i[n].name, &i[n].dam, &i[n].cost, &i[n].weight)) {
    fputs(i[n].name, stdout);
    n++; }

另一个问题是您声明 line 刚好足以容纳 sizeof(char *) 字符 - 可能只有 4 或 8 个,对于整行来说不够大,所以您只能阅读线的一部分。您需要声明它足够大以容纳文件中最长的行。

第三个问题是您将 struct itemitem_t 声明为函数的本地函数,这意味着当您尝试在函数外部使用 item_t 作为其返回类型的一部分时,您的代码甚至无法编译.您需要在函数声明之前将声明移动到全局范围。

第四个问题是您将正在读取的项目数组 (i) 声明为局部变量,因此您将无法从函数中返回它——如果这样做,指针你返回会指向垃圾。但由于你总是返回 NULL,你不会看到这个问题。

【讨论】:

  • 就我个人而言,如果代码使用fgets()sscanf() 而不是fscanf(),我认为在任何扫描失败后有意义地报告错误并重新同步会容易得多。但是,这是一个小细节,其余的建议很有价值。
【解决方案3】:

fputs() 的问题是该函数专门用于打印 char * 字符串。要打印出其他内容,您可能需要使用fprintf(),或者如果您要打印到标准输出,则最好使用printf()

要消除乱码,请确保您的文件以正确的编码保存。当我将我的测试库存文件保存为没有 BOM 的 ANSI 或 UTF-8(ANSI 为 UTF-8)时,一切正常,但每当我保存为 UTF-8 时,它会在我的文件前面插入 3 个字节,程序不能处理,并为我的第一个项目吐出胡言乱语。

我还修改了ReadItemsFromFile(),如您所见,接受指向int 的指针,这将是int,读取的元素数量将存储在其中,因此我可以测试我的代码,您可以如果它不符合您班级的规范,请将其删除。此外,它非常粗糙,但我在代码中添加了使用malloc()memcpy() 重新调整存储项目数组的内存块的大小(分别需要stdlib.hstring.h ) 因为realloc() 不能在我运行可执行文件的环境中工作,但如果realloc() 适合你,我建议改用它。

还请注意,我放置了 128 个字符的任意缓冲区长度,包括每行的 \0 空终止符。这可以调整为适合您的任何内容。

这是我一直在谈论的代码:

struct item
{
    char name[32];
    float cost, weight;
    int dam;
};
typedef struct item item_t;

item_t *ReadItemsFromFile(char *file, int *count)
{
    FILE *fpin = fopen(file, "r");
    int n = 0;
    item_t *items = NULL;

    if(fpin != NULL)
    {
        char line[128];
        while(fgets(line, 128, fpin) != NULL){
            item_t *newptr = (item_t *)malloc((n + 1) * sizeof(item_t));
            memcpy(newptr, items, n * sizeof(item_t));
            free(items);
            items = newptr;

            if (4 == sscanf(line, "'%[^']' %d %f %f\n", items[n].name, &items[n].dam, &items[n].cost, &items[n].weight)) {
                n++;
            }
        }

        fclose(fpin)
    } else {
        *count = 0;
        return NULL;
    }

    *count = n;

    return items;
}

【讨论】:

  • 欢迎来到 Stack Overflow。请尽快阅读About 页面。您应该使用 realloc() 而不是 malloc() + memcpy() + free() 来增加数组的大小。然后,我们可以讨论一次增加数组大小的成本,但它确实有效。您还应该测试sscanf() 是否成功转换了 4 个数据项。
  • @JonathanLeffler 我特别提到realloc() 没有在我的应用程序上工作,并且由于它所处的环境导致应用程序挂起。此外,我试图改进 OP 的原始代码自己完成任务。
  • 好的 - 我错过了。你在什么环境下工作,realloc() 不起作用?
  • 我运行代码时非常“受打击”,可以说是 Cygwin 的副本,并且正在使用符合 C99 标准的 GCC 进行编译。通常我会在 Linux 发行版下使用 MinGW 或 GCC,但我也不认为我会遇到我遇到的问题。
【解决方案4】:

好的,我停止使用 fgets() 并将结构移到函数之外,但我仍然遇到返回指针的问题。我在 malloc 后一直试图返回指针,但它告诉我“从不兼容的指针类型返回”。到目前为止,这是我的代码:

struct items_t
{
   char name[32];
   float cost, weight;
   int dam;
};
typedef struct items_t items_t;

item_t *ReadItemsFromFile(char *file)
{

  FILE *fpin = fopen(file, "r");

if(fpin)
{   
  items_t i[50];
  int n = 0;

   while(4 == (fscanf(fpin, " '%[^']' %d %f %f", i[n].name, &i[n].dam, &i[n].cost, &i[n].weight)))
{
  n++;
  items_t *a = (items_t*)malloc(sizeof(items_t));
  return(&a);

}
} else {
  return NULL; 
}
return(0);

}

【讨论】:

  • 你的函数的返回类型是item_t *,你试图返回一个指向item_t *的指针,也写成item_t **。将return(&amp;a); 更改为return(a);。此外,你总是malloc()ing 相同的大小。我建议看看我上面的答案(但是用简单的realloc() 替换我的malloc()memcpy()free())。现在您的代码正在尝试分配一个内存块并返回指向它的指针,但是没有实际数据存储在 a.您需要返回 i。
猜你喜欢
  • 1970-01-01
  • 2015-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-14
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
相关资源
最近更新 更多