【问题标题】:Read from file to char array, C [closed]从文件读取到字符数组,C [关闭]
【发布时间】:2016-08-23 13:29:13
【问题描述】:

我在使用此代码时遇到了一些问题,希望得到一些帮助。该函数从文件中读取动态分配的内存

感谢@JonathanLeffler 的帮助 - 函数缩进效果很好!但是又出现了一个问题:使用函数 read_file,从文件读取到字符数组,然后传递给缩进。

================================================ ============================

//--------------- read_file valgrind validations --------------------
==396== 144 bytes in 1 blocks are definitely lost in loss record 62 of 66 
==396==    at 0x4C2AD10: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==396==    by 0x401AC1: read_file (polisher.c:24) 
==396==    by 0x4025CE: test_indent (test_source.c:174) 
==396==    by 0x406BC7: srunner_run (in /tmc/test/test) 
==396==    by 0x402C67: tmc_run_tests (tmc-check.c:134) 
==396==    by 0x402902: main (test_source.c:235) 
==396== 

================================================ ======

char *read_file(const char *filename)
{
    FILE *f = fopen(filename, "r");
    if(!f)
        return NULL;
    int n = 0, c = 0;
    char *a = NULL;
    c = fgetc(f);
    while(c != EOF)
    {
        n++;
        c = fgetc(f);
    }
    freopen(filename, "r", f);
    a = calloc(n + 1, sizeof(char));
    c = fgetc(f);
    n = 0;
    while(c != EOF)
    {
        a[n] = c;
        n++;
        c = fgetc(f);
    }
    a[n] = '\0';
    fclose(f);
    return a;
}

================================================ =================

START_TEST(test_indent)
{
    char *str = read_file("testifile.c");
    if (!str) str = read_file("test/testifile.c");
    if (!str) {
        fail("[M6.01.c] read_file(\"testifile.c\") returned NULL");
    }
    char *res = indent(str, "    ");
    if (!res) {
        free(str);
        free(res);
        fail("[M6.01.c] indent(\"testifile.c\") returned NULL");
    }

    char buf[OUTPUTLEN];
    if (mycompare_new(res, ref61c, buf, OUTPUTLEN)) {
        free(res);
        free(str);
        fail("[M6.01.c] Invalid string from indent(\"testifile.c\"): %s", buf);
    }
    free(str);
    free(res);
    test_complete();
}
END_TEST

【问题讨论】:

  • 第一个问题的错误是什么?对于 valgrind 输出,你能指出哪一行出错(哪一行代码是第 116、127、...行)?
  • @Garf365 strncpy(dest + dest_offset, pad, pad_len + 1); 是 116。dest[dest_offset++] = c; 是 127。当我尝试将此函数发送到服务器时,它显示“提前退出,返回值 1”。第一个问题的错误信息是“Received signal: SIGABRT (Aborted). For main, PID 9424”
  • 请编辑您的问题以添加此信息。此外,检查每次在 valgrind 输出中提到函数 indent 并为每个提到的行添加有关行的信息
  • @Garf365 好的,完成。
  • 错误消息说“您分配了 144 个字节(例如char *space = malloc(144);),然后您尝试写入不属于分配空间的space[144]。当然,这是不允许的。

标签: c linux optimization valgrind indentation


【解决方案1】:

您的基本问题是,将单个字符添加到输出缓冲区的代码不会检查是否有多余的字符空间,并且可能没有。您可以使用更长的缩进(例如" /* Look Ma! */ ",即 16 个字符)来更快地解决错误。

您目前拥有的位置:

        continue;
    } 
    dest[dest_offset++] = c;        
    input++;
}

蛮力和粗心的解决方案补充:

        continue;
    }
    if (dest_offset >= dest_len)
    {
        printf("XX: DO = %zu, DL = %zu, PL = %zu, LV = %zu\n", dest_offset, dest_len, pad_len, pad_level);
        putchar('@');fflush(0);
        char *ptr = realloc(dest, dest_len * 2);
        if(!ptr)
        {
            free(dest);
            return NULL;
        }
        dest_len *= 2;
        dest = ptr;
    }
    putchar('.');fflush(0);
    dest[dest_offset++] = c;
    input++;
}

哦,我留下了一些我最终使用的调试代码。我添加了很多模糊相似的打印代码。循环顶部的断言也有帮助:assert(dest_offset <= dest_len);。当它触发时,事情变得更加清晰(但我花了一段时间才找出它为什么触发)。我还将换行处理代码中的测试扼杀为:

        if (dest_offset >= dest_len || (pad_len * pad_level + 1) >= (dest_len - dest_offset))
        {
            printf("YY: DO = %zu, DL = %zu, PL = %zu, LV = %zu\n", dest_offset, dest_len, pad_len, pad_level);
            putchar('@');fflush(0);
            char *ptr = realloc(dest, dest_len * 2);
            if(!ptr)
            {
                free(dest);
                return NULL;
            }
            dest_len *= 2;
            dest = ptr;
        }

realloc() 从未被解雇,这是令人惊讶的事情之一。

我认为您需要一个函数来将一个字符添加到您的输出缓冲区,并且您需要将输出缓冲区控件包装成一个结构(struct Buffer { char *buffer; size_t maxlen; size_t curlen; } 或类似的结构),并且您有一个函数处理(重新)根据需要分配空间。这将避免“蛮力和粗心”解决方案的明显重复。如果您愿意,可以将其设为static inline 函数——编译器可能会通过这种方式避免一些开销,而不会影响代码的可读性。还有一个令人讨厌的重复,两个循环将缩进的倍数添加到缓冲区。当然,这最好用另一个函数来处理——但它与“添加一个字符”不同,因为您可以明智地检查是否有足够的空间并进行一次重新分配。或者编写函数来获取长度和指向非空终止缓冲区的指针(因此单个字符的长度为 1,填充字符串的长度为 pad_len),单个函数可以完成所有工作——可能是更好的解决方案.我还是把控件打包成一个结构,让编译器优化。

测试main():

int main(void)
{
    char data[] = "#include <stdio.h>\nint main(void)\n{\nputs(\"Hello World!\\n\");\nreturn 0;\n}\n";
    printf("Before: [[%s]]\n", data);
    fflush(0);
    char *reformatted = indent(data, " /* Look Ma! */ ");
    printf("Indent: -complete-\n");
    fflush(0);
    printf("Source: [[%s]]\n", data);
    fflush(0);
    printf("Target: [[%s]]\n", reformatted);
    free(reformatted);
    return 0;
}

【讨论】:

  • 非常感谢,我已经编辑了我的代码和问题,但现在有一个小问题...... Intend 功能完美运行,服务器说我已经通过了这个练习,但现在它抱怨在 read_file 上,但一切似乎都运行良好......
猜你喜欢
  • 1970-01-01
  • 2015-01-13
  • 2017-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多