分段错误而不是显示消息 - 使用 c 中的指针从文件中读取

Posted

技术标签:

【中文标题】分段错误而不是显示消息 - 使用 c 中的指针从文件中读取【英文标题】:Segmentation fault instead of showing message - reading from a file by using pointers in c 【发布时间】:2020-04-19 19:32:14 【问题描述】:

我写了一个程序,它从一个文件中读取。我使用打印数组太大的条件,但是当我使用太大的数组而不是显示此消息时,我遇到了分段错误。

这是我的程序

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

#define N 10000 // Maximum array size

int _strlen(char *array) 
    int i;
    for (i = 0; array[i] != '\0'; ++i);    
    return i;


int readText(FILE *wp, char *s, int max) 
    int sum = 0;
    if (_strlen(s) > max) 
        printf("This array is too big. Maximum size is %d", max);
     else 
        while ((*s++ = fgetc(wp)) != EOF) 
            sum++;
        
        *(s-1) = '\0';
    
    return sum;


int main(int argc, char *argv[]) 
    FILE *wz, *wc;                       
    char *s;
    char array[N];
    s = array;
    if (argc != 3)                               
        printf("Wrong arguments number\n");
        printf("I should run this way:\n");
        printf("%s source result\n",argv[0]);
        exit(1);
    

    if ((wz = fopen(argv[1], "r")) == NULL) 
        printf("Open error %s\n", argv[1]);
        exit(1);
    
    if ((wc = fopen(argv[2], "w")) == NULL) 
        printf("Open error %s\n", argv[2]);
        exit(2);
    

    fprintf(wc, "Read text from file source.txt");

    readText(wz, s, 10000);   

    return 0;

在我想要的输出中:This array is too big. Maximum size is %d 而不是Segmentation fault core dumped

另外,我想说程序是当我使用较小的数组时,但我想在用户使用太大的数组而不是分段错误时向用户显示正确的消息。

谢谢,我以这种方式更改了我的程序。唯一的问题是这个程序在每个 while 循环中检查 if 条件,所以这个程序可能很慢。

int readText(FILE *wp, char *s, int max) 
    int sum = 0;
    if (_strlen(s) > max) 
        printf("This array is too big. Maximum size is %d", max);
     else 
        while ((*s++ = fgetc(wp)) != EOF) 
            sum++;
            if (sum > max) 
                printf("This array is too big. Maximum size is %d", max);
                break;
            
        
        *(s-1) = '\0';
    
    return sum;

【问题讨论】:

考虑将数组放在堆上(而不是栈上)。堆栈空间有限,这可能会导致您的问题。你可以这样做char *array = calloc(sizeof(char), N); 您有一些未定义的行为。数组永远不会被赋值,所以 strlen(s) 的值是未定义的。 您将指向未初始化数据 (s) 的指针传递给 readText,那么您希望该函数中对 _strlen(s) 的调用如何工作? 谢谢,我编辑了我的帖子并编写了它工作的新功能。唯一的问题是这个程序在每个 while 循环中检查 if 条件,所以这个程序可能很慢。 摆脱readText中对_strlen的调用。 _strlen 函数告诉您已经拥有的字符串有多长,而不是可以存储字符串的缓冲区有多大。您需要一个字符串来调用_strlen,而readText 的开头没有字符串,因为您还没有阅读它。 【参考方案1】:

评论/其他答案解决了您未定义的行为(在您的情况下是分段错误)。

唯一的问题是这个程序在每个 while 循环中检查 if 条件,所以这个程序可能很慢。

您的程序之所以慢不是因为“如果”,而是因为您读取文件 char per char。

使用 stat 或等效函数,您可以获得文件的大小以读取它只抛出一个 fread

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

#define N 10000 // Maximum array size

int main(int argc, char *argv[]) 
  char array[N];
  FILE *wz, *wc;                       
  struct stat st;
  off_t sz;

  if (argc != 3)                               
    printf("Wrong arguments number\n"
           "I should run this way:\n"
           "%s source result\n", argv[0]);
    exit(1);
  

  if ((wz = fopen(argv[1], "r")) == NULL) 
    printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
    exit(1);
  

  if (stat(argv[1], &st) == -1) 
    printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
    exit(1);
  

  if (st.st_size > N-1) 
    printf("This array is too big. Maximum size is %d", N-1);
    sz = N-1;
  
  else
    sz = st.st_size;

  if (fread(array, 1, sz, wz) != sz)  
    printf("cannot read %s : %s", argv[1], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    exit(1);
  
  array[sz] = 0;
  fclose(wz);

  if ((wc = fopen(argv[2], "w")) == NULL) 
    printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    exit(2);
  

  /* ... */

  fclose(wc);

  return 0;

知道文件的大小可以消除对恒定大小的限制并尝试读取文件,同时您可以为以下内容分配足够的内存:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int main(int argc, char *argv[]) 
  char * array;
  FILE *wz, *wc;                       
  struct stat st;

  if (argc != 3)                               
    printf("Wrong arguments number\n"
           "I should run this way:\n"
           "%s source result\n", argv[0]);
    exit(1);
  

  if ((wz = fopen(argv[1], "r")) == NULL) 
    printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
    exit(1);
  

  if (stat(argv[1], &st) == -1) 
    printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
    exit(2);
  

  if ((array = malloc(st.st_size + 1)) == NULL) 
    printf("Not enough memory to memorize the file %s\n", argv[1]);
    exit(3);
  

  if (fread(array, 1, st.st_size, wz) != st.st_size)  
    printf("cannot read %s : %s", argv[1], strerror(errno));
    fclose(wz); /* for valgrind end test etc */
    free(array); /* for valgrind etc */
    exit(4);
  
  array[st.st_size] = 0;
  fclose(wz);

  if ((wc = fopen(argv[2], "w")) == NULL) 
    printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
    free(array); /* for valgrind etc */
    exit(5);
  

  /* ... */

  fclose(wc);
  free(array); /* for valgrind etc */

  return 0;

反正由于程序“源结果”的使用可能是你想把argv[1]指定的文件复制到argv[2]指定的文件中 em>,在这种情况下,最好逐块读取和写入,而不是全部读取,以免白白使用大量内存并管理输入文件大小大于内存大小的情况。

【讨论】:

【参考方案2】:

您无法测量使用 _strlen(s) 的目标数组的长度,大小作为参数给出,使用 _strlen() 读取未初始化的数组具有未定义的行为。

此外,您在测试EOF 之前将fgetc(fp) 存储到*s++。这在所有情况下都是不正确的:

如果char 类型是有符号的,则EOF 无法与\377 的有效字节值区分开来。 如果char 是无符号的,则无法测试EOF,因为它已被转换为0xffchar 值,因此循环将永远运行,写入超出目标数组的末尾,直到这导致崩溃。

您只需在读取循环中添加一个测试,以在缓冲区已满时停止从文件中读取字节,并将字节读取到int 变量中,这样您就可以可靠地测试文件结尾。

这是修改后的版本:

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

#define N 10000 // Maximum array size

int readText(FILE *wp, char *s, int max) 
    int i = 0, c;
    while (i < max - 1 && (c = fgetc(wp)) != EOF) 
        s[i++] = c;
    
    s[i] = '\0';
    return i;


int main(int argc, char *argv[]) 
    FILE *wz, *wc;                       
    char array[N];
    int nread;

    if (argc != 3)                               
        printf("Wrong arguments number\n");
        printf("I should run this way:\n");
        printf("%s source result\n", argv[0]);
        exit(1);
    

    if ((wz = fopen(argv[1], "r")) == NULL) 
        printf("Open error %s\n", argv[1]);
        exit(1);
    
    if ((wc = fopen(argv[2], "w")) == NULL) 
        printf("Open error %s\n", argv[2]);
        exit(2);
    

    fprintf(wc, "Read text from file source.txt\n");

    nread = readText(wz, array, N);   

    printf("Read %d bytes\n", nread);

    return 0;

【讨论】:

以上是关于分段错误而不是显示消息 - 使用 c 中的指针从文件中读取的主要内容,如果未能解决你的问题,请参考以下文章

c ++分段错误将指针传递给函数

使用C中的结构进行分段错误

为啥在访问二级指针时出现分段错误错误? C语言

[使用结构指针的c ++分段错误

C - 指向结构指针的指针 - 分段错误

exe C时出现分段错误错误