C 程序可以在 gdb 中找到(通过 fopen)但不能释放(a.out)

C Program can find (via fopen) in gdb but not release (a.out)

这是一个简单的 C 程序,它读取文件位置的用户输入并打印第一行(如果存在):

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

int main() {
    // init vars for user input, error checking
    char file[PATH_MAX];
    scanf("%s", file);
    errno = 0;
    FILE *contents = fopen(file, "r");

    // debug, check if file exists and raises error if not
    if (contents == NULL || errno != 0) {
        printf("Error raised: %s", strerror(errno));
        exit(1);
    }

    char example[1024] = "";
    fgets(example, 1024, contents);
    printf("%s", example);
    
    // necessary closing of the stream
    fclose(contents);

    return 0;
}

运行 调试 (gdb) 上的代码工作正常,我只是输入同一目录中的文件位置,它工作正常。但是 运行 像 cat file.txt | ./a.out 这样的东西是行不通的;显然,strerror() returns No such file or directory。我对 C 的了解还不够多,不知道未定义的行为是如何起作用的,但我所知道的是它一定与 fopen.

有关

无论哪种方式,我只是希望有一个替代的工作解决方案,或者知道为什么调试器可以做一些可执行文件不能做的事情。

我怀疑您真正需要做的只是 echo file.txt | ./a.out,但您的代码还有其他问题。从参数而不是标准输入中读取文件名要清晰得多。如果您确实使用 scanf 从 stdin 读取,您应该限制读取的数据量以防止缓冲区溢出,并且您必须始终检查 scanf 返回的值以了解它是否实际读取了任何数据。您不能假定 errno 的非零值表示错误。成功完成的函数没有义务避免更改 errno 并且可以将其保留为非零值。也许尝试这样的事情:

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

int
main(void)
{
        char file[PATH_MAX];
        char fmt[64];
        snprintf(fmt, sizeof fmt, "%%%ds", PATH_MAX - 1);
        if( scanf(fmt, file) == 1 ){
                FILE *contents = fopen(file, "r");
                if( contents == NULL ) {
                        perror(file);
                        return 1;
                }

                char example[1024];
                fgets(example, 1024, contents);
                printf("%s", example);
                fclose(contents);
                return 0;
        }
        return 1;
}

但这确实是一个 很多 清洁器,可以避免 scanf 并只执行 const char *path = argv[1] 或类似操作。

请注意 scanf%s 不适合此任务,因为无法读取包含空格的文件名。 fgets 也好不到哪里去,因为名称也可以包含换行符。

我不能准确地说出这个词,但是 cat 直接将 txt 的内容输入到程序中……显然它开始尝试查找一个不存在的文件。 运行 可执行文件工作,因为程序通过获取文件目录工作,然后搜索它,剩下的就是历史。

示例解决方案:运行 a scanf/fgets(无论偏好如何)直到 EOF/NULL 进入某个变量。