【问题标题】:Rewind File, Create Dynamic Struct倒带文件,创建动态结构
【发布时间】:2015-05-03 14:20:20
【问题描述】:

此函数将倒带文件,创建动态数组(大小),并读入数据,填充 _data 结构动态数组。请注意,流 这次是按值传递。然后该函数返回填充的 结构数组

struct _data
{
    char* name;
    long number;
};

struct _data *load(FILE *stream, int size)
{
    struct _data BlackBox = calloc(size, sizeof(_data));

    char tempName[3];

    stream = fopen("names.txt", "r");
    for (int i=0; i<size; i++)
    {
        fscanf(stream, "%s %ld", tempName, &data.number);
        BlackBox[i].name = calloc(strlen(tempName), sizeof(char));
        strcpy(BlackBox[i].name, tempName);
    }
    fclose(stream);

    return &BlackBox;
}

File Content
ron 7774013
jon 7774014

我是初学者,在设计代码时遇到了困难。有人可以解释一下。谢谢

【问题讨论】:

  • 你有什么问题?
  • @QmickZh 代码无法编译。我想将文件的内容加载到结构中。函数 (*load) 将被 main 调用。
  • 这段代码的所有错误都可以在编译器输出中轻松找到。你应该先阅读它。

标签: c arrays dynamic stream pass-by-value


【解决方案1】:

我想你有一些来自 gcc 的警告可以帮助你。

使用你的 calloc 修复内存管理,并且不返回堆栈指针

typedef struct _data                                                           
{                                                                              
    char* name;                                                                
    long number;                                                               
} _data;                                                                       

_data *load(FILE *stream, int size)                                            
{                                                                              
    _data *BlackBox = calloc(size, sizeof(_data));                             

    char tempName[3];                                                          

    for (int i=0; i<size; i++)                                                 
    {                                                                          
        fscanf(stream, "%s %ld", tempName, &BlackBox[i].number);               
        BlackBox[i].name = strdup(tempName);                                   
    }                                                                          
    fclose(stream);                                                            

    return BlackBox;                                                           
}                                                                              

int main (void)                                                                
{                                                                              
    FILE *f = fopen("test.data", "r");                                         
    _data *data = load(f, 2);                                                  
    printf("%s %ld\n", data[0].name, data[0].number);                          
    printf("%s %ld\n", data[1].name, data[1].number);                          
    return 0;                                                                  
}   

输出

 aurel@vm-pontarlier:~$ ./a.out 
ron 7774013
jon 7774014

考虑改变_data

typedef struct _data{
    char name[256];
    long number;
} _data;

扫描将是:

for (int i=0; i<size; i++)                                                 
{                                                                          
    fscanf(stream, "%s %ld", BlackBox[i].name, &BlackBox[i].number);       
}   

【讨论】:

  • 我在使用你的程序后遇到了多个错误。我如何在此处发布它们?
  • main.c:70:26: error: array type 'char [256]' is not assignable BlackBox[i].name = strdup(tempName); ~~~~~~~~~~~~~~~~ ^
  • 只使用第一部分,你把我的第一个和第二个命题混在一起了
  • 第二个命题是pastebin.com/m3R9Kh28你可以使用pastebin来分享错误
  • 您无需倒带扫描文件两次。在调用加载之前添加倒带
【解决方案2】:

您犯的错误与您之前的帖子相同。您也不会为_data 中的name 成员分配内存。至于编译错误:

  • 使用malloccalloc 动态分配的T 类型的数组由句柄控制,该句柄是指向T 类型的T* 的指针。
  • 您定义的结构是struct _data 类型,该类型包括关键字struct。如果您想为您的类型使用一个单词标识符,请使用 Ôrel 向您展示的“typedef”。
  • 您没有名为 ´data. The handle to the newly allocated memory is ´BlackBox 的变量。

如果你修复了这些错误,你会遇到编译器无法知道的逻辑错误:

  • 您的BlackBox[i].name 通过calloc 初始化为NULL,因此它不指向有效内存。你不能strcpy 任何东西。您可以做的是使用非标准但广泛可用的strdup,它首先根据需要分配内存,然后进行复制。
  • 您的临时字符串长度为 3;它最多可以包含两个字符加上空终止符。您的文件中的名称很短,但您的程序必须为任何输入做好准备,即使是非法输入。
  • FILE * 不在您的函数之外使用;它应该是 load 的本地地址。
  • 用户必须指定要读取的项目数。这违背了动态分配的目的。用户也不知道阅读了多少项目;根据要求,文件中的项目可能较少。您的功能设计应该反映这一点。 (好吧,这不是真的:你对内存进行零初始化,这意味着NULL 的字符指针表示数组的结束,但是:)
  • 您没有测试fscanf 的输出,您应该这样做。如果您的文件少于 size 项,则最后一项将包含垃圾,因为您将垃圾值复制到零初始化内存中。
  • 本身不是错误,但如果您的文件是按行组织的,请考虑先用fgets 读取一行,然后用sscanf 解析它。

下面的示例代码尝试结合这些准则。注意事项:

  • 内存不是在一个大块中分配的,而是随着对realloc 的后续调用而根据需要增长。调用realloc(NULL, s) 相当于malloc(s)
  • 函数通过指向整数的指针“填充”读取的元素数。
  • 字符串被复制到新分配的内存中。这意味着从理论上讲,字符串可以任意长。 (实际上,最大缓冲区长度 20 会将此类字符串缩短。)函数 duplicate 模拟函数 strdup
  • 清理时这些字符串应该是freed。
  • load 函数采用文件名而不是文件句柄。
  • 文件读取分两个阶段进行:首先读取一行,然后扫描该行。如果格式不是“不带空格的字符串”+“数字”,则会写入错误消息。

不管怎样,这里是:

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

struct _data
{
    char *name;
    long number;
};

/*
 *      Duplicate a string on the heap (aka strdup)
 */
char *duplicate(const char *str)
{
    char *p = malloc(strlen(str) + 1);

    strcpy(p, str);
    return p;
}

/*
 *      Read a list of names from file fn
 */
struct _data *load(const char *fn, int *psize)
{
    struct _data *data = NULL;
    FILE *stream;
    char line[80];      // buffer for line
    int lnr = 0;        // line number for error message
    int n = 0;          // number of read items

    stream = fopen("names.txt", "r");
    if (stream == NULL) return NULL;    // Can't open file

    while (fgets(line, sizeof(line), stream)) {
        long int number;
        char buf[20];

        lnr++;
        if (sscanf(line, "%19s %ld", buf, &number) == 2) {
            data = realloc(data, (n + 1) * sizeof(*data));
            data[n].number = number;
            data[n].name = duplicate(buf);
            n++;
        } else {
            fprintf(stderr, "[%s, line %d] Illegal format\n", fn, lnr);
        }
    }
    fclose(stream);
    *psize = n;             // Assign number of read items

    return data;
}

/*
 *      Free memory allocated by load
 */
void cleanup(struct _data *data, int n)
{
    while (n--) free(data[n].name);
    free(data);
}

int main()
{
    struct _data *data;
    int i, n;

    data = load("names.txt", &n);
    if (data == NULL) return -1;

    for (i = 0; i < n; i++) {
        printf("%-20s%12ld\n", data[i].name, data[i].number);
    }

    cleanup(data, n);

    return 0;
}

【讨论】:

  • 我会记得的。我擅长 Ruby 和 Java,但 C 对我来说只是新事物。很抱歉,非常感谢您的解释。
  • Ruby 和 Java 以及大多数现代语言会为您处理内存分配和垃圾收集。在 C 中,您必须自己处理所有这些。这可以带来优化的设计,但这也意味着你经常会在脚上开枪,尤其是在你还在学习的时候。对上面的建议持保留态度。 cmets 和示例代码旨在提供帮助,而不是令人反感。
  • 我接受每一个建议,我不觉得它们有冒犯性。对我来说,这永远是学习的时刻,我会从中学习。我假设,你能给我一本好书来开始理解 C。我知道 Ruby 和 Java 中的垃圾收集。 C真的让我走了。 :(
【解决方案3】:

...最后 tempName[3] 太小了:考虑到您输入的 3 个字母,它必须至少为 4。此外,您忘记为 malloc 调用中的终止空字符分配空间:

char tempName[4];
...
BlackBox[i].name = malloc(strlen(tempName)+1);

(那个 tempName[3] 没有导致错误是因为编译器可能将它四舍五入到偶数字节 - 但这是典型的初学者错误。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多