getline 中的段错误

Posted

技术标签:

【中文标题】getline 中的段错误【英文标题】:Segfault in getline 【发布时间】:2021-12-31 10:30:16 【问题描述】:

以下代码应该读取文件“rules.txt”并将其逐行写入设备。 流程应该是:

    从 rules.txt 中读取行 将其回显到设备

由于readline,以下代码总是以段错误结尾,我不知道为什么:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
  

#define BUFFER_LENGTH 256

int main()

    char *line;
    size_t len = BUFFER_LENGTH;
    
    int fd = open("./rules.txt", O_RDONLY);
    if(fd == -1)
     perror("open failed"); return 0; 

    FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");

    if(fout == NULL)
     close(fd); perror("fopen failed, log.txt is busy!"); return 0; 

    while (1) 
    
        line = (char*) malloc(len*sizeof(char));
        
        if(line==NULL)
perror("malloc failed!"); return 0; 


        int bytesRead = getline(&line, &len, fd);
        
        if (bytesRead == -1) 
        
            perror("Failed to read the message from the device.");
            return errno;
        

        sprintf(line,"%s","lala");
        printf("line = %s", line);
    

    fclose(fout);
    close(fd);
    return 0;




编辑:

我更正了代码,但仍然出现段错误。这是更正后的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
  

#define BUFFER_LENGTH 256

int main()

    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;
    
   fp = fopen("./rules.txt", "r");


    if(fp  == NULL)
     perror("open failed"); return 0; 

    FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");

    if(fout == NULL)
      perror("fopen failed!"); return 0; 

    while (1) 
    
        

        ssize_t bytesRead = getline(&line, &len, fp);
        
        if (bytesRead == -1) 
        
            return 0;
        

        printf("line = %s", line);
        fprintf(line,"%s",fout);
    

    fclose(fout);
    fclose(fp);
    return 0;


【问题讨论】:

getline 采用 FILE *,而不是 int(文件描述符)。 @Cheatah 我没有注意到我错误地写了“readline”而不是“getline”,谢谢指出,我编辑了标题。 您的fprintf 的参数顺序错误:fprintf(fout,"%s",line); 您确实应该启用编译器警告并密切注意它们。 【参考方案1】:

首先,正确的核心getline()循环是

 /* FILE     *in = fopen(..., "r"); */
    char   *line = NULL;
    size_t  size = 0;

    while (1) 
        ssize_t  len = getline(&line, &size, in);
        if (len < 0)
            break;

        /* You have 'len' chars at 'line', with line[len] == '\0'. */
    

所以,导致段错误的不是getline(),而是你的fprintf(line, "%s", fout);应该是fprintf(fout, "%s", line);或只是fputs(line, fout);,或fwrite(line, 1, bytesRead, fout);,因为该行可以包含@987654330的嵌入式NUL字节@ 和 fputs() 考虑字符串结束标记。


如果我们修改代码,将源文件名和目标文件名作为命令行参数(- 表示标准输入或标准输出),这就是我个人希望看到的:

/* SPDX-License-Identifier: CC0-1.0 */

/* This tells the GNU C library to expose POSIX features, including getline(). */
#define  _POSIX_C_SOURCE  200809L

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

int main(int argc, char *argv[])

    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) 
        const char *self = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", self);
        fprintf(stderr, "       %s SOURCE TARGET\n", self);
        fprintf(stderr, "\n");
        fprintf(stderr, "This copies all content from SOURCE to TARGET, line by line.\n");
        fprintf(stderr, "Use '-' for standard input source or standard output target.\n");
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    

    const char *srcpath = argv[1];
    const char *dstpath = argv[2];

    /* If the path is "-", set it to NULL. */
    if (srcpath && !strcmp(srcpath, "-"))
        srcpath = NULL;
    if (dstpath && !strcmp(dstpath, "-"))
        dstpath = NULL;

    FILE *src, *dst;

    /* Open source for reading. If srcpath is NULL, use stdin. */
    if (srcpath) 
        src = fopen(srcpath, "r");
        if (!src) 
            fprintf(stderr, "%s: %s.\n", srcpath, strerror(errno));
            return EXIT_FAILURE;
        
     else 
        src = stdin;
    

    /* Open target for writing. If dstpath is NULL, use stdout. */
    if (dstpath) 
        dst = fopen(dstpath, "w");
        if (!dst) 
            fprintf(stderr, "%s: %s.\n", dstpath, strerror(errno));
            fclose(src);
            return EXIT_FAILURE;
        
     else 
        dst = stdout;
    

    char          *line = NULL;
    size_t         size = 0;
    unsigned long  linenum = 0;

    while (1) 
        ssize_t  len = getline(&line, &size, src);
        if (len < 0)
            break;

        linenum++;

        if (fwrite(line, 1, len, dst) != (size_t)len) 
            fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
            fclose(dst);
            fclose(src);
            return EXIT_FAILURE;
        
    

    /* Technically, we don't need to release dynamically allocated (non-shared) memory,
       because we're just about to exit, and the OS will automagically do that for us.
       We can do this at any point we want during the loop, too. */
    free(line);
    line = NULL;
    size = 0;

    /* We do not know why getline() returned -1.  Check if an error occurred, and whether we're at end of input. */
    if (ferror(src) || !feof(src)) 
        fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
        fclose(dst);
        fclose(src);
        return EXIT_FAILURE;
    

    /* Check if any write errors have occurred. */
    if (ferror(dst)) 
        fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
        fclose(dst);
        fclose(src);
    

    /* Read errors should not occur at close time, but it costs very little for us to test anyway. */
    if (fclose(src)) 
        fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
        fclose(dst);
        return EXIT_FAILURE;
    

    /* Write errors can occur at close time, if the output has been buffered, or the target is on
       a remote filesystem.  Again, it costs us very little to check. */
    if (fclose(dst)) 
        fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
        return EXIT_FAILURE;
    

    /* No errors; target is an identical copy of the source file. */
    fprintf(stderr, "%lu lines copied successfully.\n", linenum);
    return EXIT_SUCCESS;

SPDX-License-Identifier 是表示代码许可的常用方式。我用的是CC0-1.0,基本意思是“随心所欲,只是不要因为任何问题责怪作者:不保证,不保证。”

#define _POSIX_C_SOURCE 200809L 告诉 GNU C 库我们希望它公开 POSIX.1-2008 功能,其中包括 getline()。如果您查看man 3 getline,您会在概要部分看到glibc 2.10 及更高版本要求将_POSIX_C_SOURCE 至少定义为200809L

大部分代码是打印用法,从命令行获取源文件名和目标文件名,并将-处理为特殊名称,“标准流”,这样即使在管道中也可以使用通过将- 指定为输入和/或输出文件名。

getline() 循环使用fwrite() 将行写入目标文件。这样,如果输入包含嵌入的 NUL 字节 (\0),目标仍将与源文件相同。

在循环之后,我们丢弃了行缓冲区,尽管由于程序即将退出,我们可以省略它(因为操作系统将在我们退出时释放所有动态分配的(非共享)内存)。

我喜欢使用ferror(src) || !feof(src) 检查srcsrc 中是否发生错误的代码没有到达输入结尾;并检查fclose() 的返回值,以防报告延迟(写入)错误。诚然,fclose() 对于只读文件永远不会失败,fclose() 应该只对我们在特定情况下写入的文件失败,但检查成本很低,这样运行程序的用户将被告知程序是否检测到数据丢失。

我认为忽略检查此类错误在道德上是应受谴责的(尤其是“因为它们很少发生”),因为此类测试是人类用户知道操作是否成功或是否发生了一些奇怪问题的唯一方法.调查任何问题取决于人类,并取决于我们的程序报告可检测到的问题。

【讨论】:

这个特定程序不需要 ant 命令行输入,所以这部分对我来说有点过时了,但除了你展示了一些不错的想法之外,我会给他们一个尝试!谢谢!【参考方案2】:

getline 函数将FILE * 作为参数,而不是int。替换以下行:

int fd = open("./rules.txt", O_RDONLY);

由;

FILE *fin = fopen("./rules.txt", "r");

相应地修复以下行中的错误检查,就像您对 fout 所做的那样。

然后换行:

int bytesRead = getline(&line, &len, fd);

现在应该使用fin:

ssize_t bytesRead = getline(&line, &len, fin);

注意getline 返回ssize_t,而不是int。 你也从来没有写信给fout,但我猜你还在处理这段代码。

确保启用编译器警告,因为您的编译器肯定会警告您使用 int 参数而应使用 FILE *

【讨论】:

我按照你说的改了代码,用fprintf把write加到fout里面了。。。还是segfault,怎么办?

以上是关于getline 中的段错误的主要内容,如果未能解决你的问题,请参考以下文章

Windows:处理所有线程中的段错误

函数调用中的段错误

生产者消费者程序中的段错误

C中多线程python扩展中的段错误

atoi(str) 中的段错误

c++ 类向量中的段错误