尝试执行 stem 文件时出现分段错误(核心已转储)

Segmentation fault (core dumped) while trying to execute the stem file

我有一个名为 myfile 的主干文件,它有 2 个子文件:myfile.datamyfile.names.

现在我想写一个代码,它首先检查 .names 文件的最后一行,看看是否有零,如果有任何其他数字不是零然后它会说 noisy data。如果 .names 文件的最后一行有一个零,那么我需要读取 .data 文件并对那个 .data 文件进行一些操作以产生有效的输出。

下面我给出了我尝试做的事情:

int main(int argc, char **argv)
{
                char* path;
                sprintf(path, "%s.names", argv[1]);
                printf("...%s...\n", path);
                //FILE *fp =stdin;
                fp = fopen(path, "r");
                
                char buff[1024];
                int check = 0;

                fseek(fp, 0, SEEK_SET);

                while(!feof(fp))
                {
                    memset(buff, 0x00, 1024);
                    fscanf(fp, "%[^\n]\n", buff);
                }
                
                if(strchr(buff, '0') != NULL){
                    check = 1;
                }
                
                if(check == 0){
                    printf("Noisy data...\n");
                }
                
                else if(check == 1){
                    
                    char* path1;
                    sprintf(path1, "%s.data", argv[1]);
                    printf("...%s...", path1);
                    //FILE *fp1 = stdin;
                    fp1 = fopen(path1, "r");
                    
                    char buf[1024];
                
                    if( fgets(buf, sizeof buf, fp1) == NULL ){
                        fputs("Input error\n", stderr);
                        return 1;
                    }
                    ...
                    ...
                    Produces the perfect output
}

编译成功

要执行此操作:<./executable_filename> <stemFileName> > <outputFileName>

所以每当我做的时候:./myFileName myfile output。它在说 Segmentation fault (core dumped)。为什么会这样?

还有一点,我的 .names 文件将包含:| Final Pseudo Deletion Count is 0.| Final Pseudo Deletion Count is 1.| Final Pseudo Deletion Count is 2.,所以我想 strchr 会在这种情况下完成我的工作?

我最初尝试修改下面的代码,但在尝试了几种不同的方法后,我无法做到。

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

FILE * xfopen(const char *path, const char *mode);
void * xmalloc(size_t s);

void parse_line(const char *buf, int *max, int column_count)
{
    for(int i = 0; i < column_count; i++ ){
        char *end;
        int t = strtol(buf, &end, 10);
        
        if( t > max[i] ){
            max[i] = t;
        }
        
        if( !((i < column_count - 1 && *end == ',') || (i == column_count - 1 && *end == '\n')) ){
            fprintf(stderr, "invalid input '%c' in %s", *end, buf);
            exit(1);
        }
        buf = end + 1;
    }
}

int main(int argc, char **argv)
{

    char *path1;
    char *path = argc > 1 ? argv[1] : "stdin";
    
    sprintf(path, "%s.data", argv[1]);
    
    FILE *in = argc > 1 ? xfopen(path, "r") : stdin;
    
    char buf[1024];
    int column_count = 1;
    int row_count = 1;
    int *max;

    /* Read first line to determine number of columns */
    if( fgets(buf, sizeof buf, in) == NULL ){
        fputs("Input error\n", stderr);
        return 1;
    }

    for( const char *p = buf; *p; p++ ){
        if( *p == ',' ){
            column_count += 1;
        }
    }
    
    max = xmalloc(column_count * sizeof *max);
    
    for( int i = 0; i < column_count; i++ ){
        max[i] = INT_MIN;
    }
    
    parse_line(buf, max, column_count);
    while( fgets(buf, sizeof buf, in) != NULL ){
        row_count += 1;
        parse_line(buf, max, column_count);
    }
    
    if( fseek(in, 0L, SEEK_SET) ){
        perror(path);
        return 1;
    }
    
    printf("%d %d ", row_count, column_count - 1);
    
    for( int i = 0; i < column_count - 1; i += 1 ){
        printf("%d ", max[i]);
    }
    
    printf("%d\n", max[column_count - 1] + 1);

    while( fgets(buf, sizeof buf, in) != NULL ){
        char *comma = strrchr(buf, ',');
        if( comma == NULL ){
            fprintf(stderr, "Invalid input\n");
            return 1;
        }
        
        *comma = '[=11=]';
        int k = strtol(comma + 1, NULL, 10);
        for(char *p = buf; *p;  p++){
            if( *p == ',' ) *p = ' '; 
        }
        printf("%s %d\n", buf, k + 1);
    }
}

FILE *
xfopen(const char *path, const char *mode)
{
    FILE *fp = path[0] != '-' || path[1] != '[=11=]' ? fopen(path, mode) :
        *mode == 'r' ? stdin : stdout;
    if( fp == NULL ){
        perror(path);
        exit(EXIT_FAILURE);
    }
    
    return fp;
}

void *
xmalloc(size_t s)
{
    void *rv = malloc(s);
    if( rv == NULL ){
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    return rv;
}

几个错误立即显现出来:

                char* path;
                sprintf(path, "%s.names", argv[1]);

传递给 sprintf 的缓冲区必须指向可写内存,但是 path 未初始化并且没有特别指向任何地方。参见 。您可能希望 path 成为数组而不是指针。

如果您启用警告,例如gcc -Wall -O。那会节省你问这个问题的时间(和我回答它的时间)。

此外,由于您无法控制字符串 argv[1] 的长度,因此缓冲区的固定长度永远都不够长,因此您可能想使用 snprintf 来防止缓冲区溢出。请参阅 understanding the dangers of sprintf(...)(适用于 C++,但解决了相同的问题)。

你在 path1 再往下有同样的问题。

fp = fopen(path, "r");
如果失败,

fopen 将 return NULL,并且您永远不会检查它。在这种情况下,您的程序会出现段错误。 fp1 再往下也是同样的问题。

while(!feof(fp))

这是always wrong

fscanf(fp, "%[^\n]\n", buff);

sprintf 类似的溢出风险。此外,您必须检查 fscanf 是否成功;这是检查文件结尾的正确方法,代替错误的 while (!feof(fp)).

if( fgets(buf, sizeof buf, fp1) == NULL ){
                        fputs("Input error\n", stderr);
                        return 1;

fgets returning NULL 不一定表示“输入错误”;可能只是已到达文件末尾。


有关分段错误的原因以及如何调试它们的一般信息,另请参阅

  • What is a segmentation fault?

  • What is a debugger and how can it help me diagnose problems?