1

好的,我已经阅读了关于 SO 和许多其他地方的大量答案,但我似乎无法掌握这个简单的功能。请原谅我这么简单的事情,我已经 8 年多没有做过 c/c++ 代码了,我非常想重新学习,所以请耐心等待......

我尝试了许多不同的方法来做到这一点,从通过函数参数分配字符串,将值转移到直接返回它,但在此期间似乎没有任何效果。在编译期间我也没有收到任何错误,但我在运行时确实遇到了段错误。我非常想知道为什么下面的函数不起作用......我只是不明白为什么 else 以 char *content 类型返回正常,但是 strcat(content, line); 才不是。尽管 strcat 的手册页显示 strcat 的定义应该是 (char *DEST, const char *SRC)。正如我目前所理解的那样,它试图在 while 内对 line 变量进行强制转换只会将整数返回给指针。所以我在这里被难住了,希望有时间的人能接受教育!

char * getPage(char *filename) {
    FILE *pFile;
    char *content;
    pFile = fopen(filename, "r");
    if (pFile != NULL) {
        syslog(LOG_INFO,"Reading from:%s",filename);
        char line [256];
        while (fgets(line, sizeof line, pFile) != NULL) {
            syslog(LOG_INFO,">>>>>>>Fail Here<<<<<<<");
            strcat(content, line);
        }
        fclose(pFile);
    } else {
        content = "<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title></head><body><h1>Does Work</h1></body></html>";
        syslog(LOG_INFO,"Reading from:%s failed, serving static response",filename);
    }
    return content;
}

非常感谢这篇文章中所有的好答案。我会给讨论中的每个人一个复选标记,但不幸的是我不能......

4

6 回答 6

2

您需要为content. 按照您的操作方式,它必须足够大以容纳整个文件。您可以预先分配一个巨大的缓冲区并希望获得最好的结果,或者分配一个较小的缓冲区并根据需要重新分配它。

更好的是重新排列代码以避免一次存储整个文件的需要,尽管如果您的调用者需要整个网页作为字符串,那可能很难。

另请注意,您需要从两个代码路径返回相同类型的内存。有时不能返回静态字符串,有时不能返回堆分配的字符串。这肯定会让人头疼和/或内存泄漏。因此,如果您将文件内容复制到内存块中,您还应该将静态字符串复制到相同类型的块中。

于 2012-02-09T02:32:13.383 回答
2

这很简单,但如果您习惯了高级语言,这将非常令人惊讶。 C 不会为您管理内存C 也没有真正的字符串。该content变量是指针,而不是字符串。您必须在调用之前手动分配字符串所需的空间strcat。编写此代码的正确方法是这样的:

FILE *fp = fopen(filename, "r");
if (!fp) {
    syslog(LOG_INFO, "failed to open %s: %s", filename, strerror(errno));
    return xstrdup("<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title>"
                  "</head><body><h1>Does Work</h1></body></html>");
} else {
    size_t capacity = 4096, offset = 0, n;
    char *content = xmalloc(capacity);
    size_t n;
    while ((n = fread(content + offset, 1, capacity - offset, fp)) > 0) {
        offset += n;
        if (offset == capacity) {
            capacity *= 2;
            content = xrealloc(content, capacity);
        }
    }
    if (n < 0)
        syslog(LOG_INFO, "read error from %s: %s", filename, strerror(errno));
    content[offset] = '\0';
    fclose(fp);
    return content;
}

笔记:

  1. 由 I/O 故障触发的错误消息应始终包括strerror(errno).
  2. xmalloc, xrealloc, 和xstrdup是围绕它们的对应项的包装函数,没有前导x;他们使程序崩溃而不是返回NULL。这几乎总是比在每个可能发生的地方手动从内存不足中恢复要少的悲伤。
  3. 我返回xstrdup("...")而不是"..."在打开失败的情况下,以便调用者始终可以调用free(content). 调用free字符串文字会使您的程序崩溃。
  4. 天哪,这是很多工作,不是吗?这就是为什么人们倾向于使用高级语言编写 Web 应用程序的原因。;-)
于 2012-02-09T02:39:36.823 回答
1

content 只是一个指向字符串的指针,而不是实际的字符串 - 它为您的字符串保留了 0 个字节的空间。您需要分配足够大的内存来保存小时字符串。请注意,在您必须释放它之后

char *content=malloc(256);

你的代码应该没问题 - 哦,我建议使用 strncat

对内容的第二次分配之前工作正常 - 因为您将指针设置为指向您的 const 字符串。如果您将内容更改为 malloc 的内存区域 - 那么您还希望将您的固定字符串 strncpy 到内容中。

理想情况下,如果您可以使用 C++ std::string。

于 2012-02-09T02:30:15.047 回答
1

content是一个野指针;该变量包含垃圾,因此它指向左侧字段的某个位置。当您使用 将数据复制到其中时strcat,数据会转到某个随机的、可能是错误的位置。解决这个问题的方法是在content某个好的地方指出一点。由于您希望它比函数调用更有效,因此需要在函数调用堆栈之外的某个地方分配它。您需要使用malloc()在堆上分配一些空间。然后调用者将拥有内存,并free()在不再需要时调用将其删除。

您还需要将else直接分配给 的部分更改content为使用strcpy,以便free()始终有效。你不能释放你没有分配的东西!

通过所有这些代码,请确保您记住分配了多少空间malloc(),并且写入的数据不要超过您的空间,否则您会遇到更多的崩溃。

于 2012-02-09T02:32:36.833 回答
1

char *foo只是指向某个内存块的指针,该内存块保存了构成字符串的字符。所以你不能使用strcat,因为你没有任何内存可以复制到。在语句中,您正在使用该行在if堆栈上分配本地内存,但是由于该内存是函数的本地内存,因此一旦返回,它将消失,因此您不能.char line[256]return line;

因此,您真正想要的是分配一些持久内存,例如使用strdupor malloc,以便您可以从函数中返回它。请注意,您不能混合使用常量和分配的内存(因为您的函数的用户必须free使用内存 - 只有当它不是常量时才有可能)。

所以你可以使用这样的东西:

char * getPage(const char *filename) {
    FILE *pFile;
    char *content;
    pFile = fopen(filename, "r");
    if (pFile != NULL) {
        syslog(LOG_INFO,"Reading from:%s",filename);
        /* check the size and allocate memory */
        fseek(pFile, 0, SEEK_END);
        if (!(content = malloc(ftell(pfile) + 1))) { /* out of memory ... */ }
        rewind(pFile);
        /* set the content to be empty */
        *content = 0;
        char line [256];
        while (fgets(line, sizeof line, pFile) != NULL) {
            syslog(LOG_INFO,">>>>>>>Fail Here<<<<<<<");
            strcat(content, line);
        }
        fclose(pFile);
    } else {
        content = strdup("<!DOCTYPE html><html lang=\"en-US\"><head><title>Test</title></head><body><h1>Does Work</h1></body></html>");
        syslog(LOG_INFO,"Reading from:%s failed, serving static response",filename);
    }
    return content;
}

这不是最有效的方法(因为strcat每次都必须找到结尾),但对代码的修改最少。

于 2012-02-09T02:34:19.177 回答
1

较早的答案提出了解决方案:

char content[256];

这个缓冲区将不足以容纳除最小文件之外的任何内容,并且在执行时指针content超出范围return content;。(您前面的行content = "static..";很好,因为字符串被放置在.rodata 数据段中,并且在程序的整个生命周期中,它的指针将始终指向相同的数据。)

如果您为contentwith分配内存malloc(3),您可以“增加”所需的空间 with realloc(3),但这会引入一个可怕的错误的可能性——无论您将指针交给什么,都必须在内存分配完成后清理数据(否则你会泄漏内存),它不能简单地调用free(3),因为content指针可能指向静态分配的内存。

因此,您有两个简单的选择:

  • 用于每次需要时复制静态字符串,并strdup(3)用于content = malloc(size);静态路径
  • 让您的呼叫者负责提供内存;每个调用都需要提供足够的内存来处理文件的内容静态字符串。

我可能更喜欢第一种方法,如果只是因为在调用之前无法知道第二种方法所需的大小。

于 2012-02-09T02:39:17.657 回答