为什么每次尝试将元素存储到 C 中的数组时都会出现段错误?

Why am I getting a seg fault every time I try to store elements into an array in C?

所以,我正在尝试从文件中读取矩阵并将其存储到数组中。

矩阵文件如下:

2 3
1 5 8
6 3 9

第一行的前两位数字代表行 x 列。

每当我尝试将某些内容存储到数组中时,我似乎都会遇到问题,因为我能够简单地将所有内容重新打印到屏幕上,但每当我尝试添加到数组中时,我都会遇到段错误。

我的代码如下:

double *read_matrix(char *filename){
     FILE *file=fopen(filename, "r");
 
     if(!file){
         fprintf(stderr, "An error has occurred. Wrong file name?\n");
         exit(errno);
     }

     double *data=(double *)malloc(sizeof(double)*4096);
     char *buff=malloc(sizeof(char *)*256);
     char *curr;

     while(fgets(buff,sizeof(buff),file)){
         curr=strtok_r(buff," ",&buff);
         int i=0;
         while(curr!=NULL){
             curr=strtok_r(NULL," ",&buff);
             data[i]=strtod(curr,NULL); //this
             i++;
         }
     }
     return data;
}

每当我使用 GDB 进行调试并使用 backtrace 命令时,它都会将我带到注释行 //this

我无法理解我做错了什么,我开始发疯了!

您需要在相关行之前检查 strtok_r 中的 return 值。

您在调用 strtod() 之前没有检查 curr 的值。

这个:

(...)
         while(curr!=NULL){
             curr=strtok_r(NULL," ",&buff);
             data[i]=strtod(curr,NULL); //this
             i++;
         }

应该是:

(...)
         while(1){
             curr=strtok_r(NULL," ",&buff);
             if (curr == NULL)
                 break; 
             data[i]=strtod(curr,NULL); //this
             i++;
         }

这个

     while(curr!=NULL){
         curr=strtok_r(NULL," ",&buff);
         data[i]=strtod(curr,NULL); //this
         i++;
     }

应该是

     while(curr!=NULL){
         data[i]=strtod(curr,NULL); //this
         i++;
         curr=strtok_r(NULL," ",&buff);  // Looking for next token shall 
                                         // be the last statement
     }

进一步这条线

char *buff=malloc(sizeof(char *)*256);

错了。应该是

char *buff=malloc(sizeof(char)*256);
                         ^^^^
                         No *

但一般来说 malloc 最好使用以下形式:

 double *data = malloc(4096 * sizeof *data);
                              ^^^^^^^^^^^^

 char *buff=malloc(256 * sizeof *buff);
                         ^^^^^^^^^^^^

通过使用 sizeof *varname,您可以避免简单的类型不匹配。

在你的情况下,你甚至不需要使用 strtok。只需使用管理空白的 strtod :

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

double *read_matrix(char *filename){
   FILE *file=fopen(filename, "r");

   if(!file){
      fprintf(stderr, "An error has occurred. Wrong file name?\n");
      exit(1);
   }

   double *data = malloc(10 * sizeof(double));
   char *buff = malloc(10 * sizeof(char));

   char *end_pointer;
   char *newline_pos;

   int i = 0;

   while(fgets(buff,sizeof(buff),file)){
      //removing newline character read by fgets
      if ((newline_pos = strchr(buff, '\n')) != NULL) {
         *newline_pos = '[=10=]';
      }

      while (strlen(buff) != 0) {
         data[i] = strtod(buff,&end_pointer);
         buff = end_pointer;
         i++;
      }
   }
   //convenient for printf output
   data[i] = '[=10=]';
   return data;
}

int main(void) {
   char file[] = "matrix.txt";
   double *array = read_matrix(file);

   int i = 0;
   while (array[i] != '[=10=]') {
      printf("%f ",array[i]);
      i++;
   }
   printf("\n");
}

您的代码中有多个问题未在其他答案中解决:

double *read_matrix(char *filename){
     FILE *file=fopen(filename, "r");
 
     if(!file){
         fprintf(stderr, "An error has occurred. Wrong file name?\n");
         exit(errno);
     }

     double *data=(double *)malloc(sizeof(double)*4096);
     char *buff=malloc(sizeof(char *)*256);

您需要一个字符缓冲区,而不是指针缓冲区。

     char *curr;

     while(fgets(buff,sizeof(buff),file)){

sizeof 不会评估缓冲区的大小,而只会评估 char* 的大小,在大多数系统上为 4 或 8。这可能不足以构成完整的一行。


         curr=strtok_r(buff," ",&buff);

您正在循环内修改 buff。当您在循环的下一次迭代中将它用于 fgets 时,您将不会使用从 malloc.

获得的地址
         int i=0;

您将读取的每一行的数组索引设置为 0。最后这只会保留最后一行的值。


         while(curr!=NULL){
             curr=strtok_r(NULL," ",&buff);
             data[i]=strtod(curr,NULL); //this
             i++;
         }
     }
     return data;

这只包含最后一行的数字,甚至只包含最后一行前 8 个字节中的数字。 来电者怎么知道你读了多少号码?

}

固定的解决方案可能如下所示:

#define BUFFSIZE 256
double *read_matrix(char *filename, int *numread) {
    FILE *file=fopen(filename, "r");
 
    if(!file){
        fprintf(stderr, "An error has occurred. Wrong file name?\n");
        exit(errno);
    }

    double *data = malloc(sizeof(double)*4096);
    char *buff = malloc(BUFFSIZE);
    char *curr = NULL;
    char *savetr = NULL;
    int i = 0;

    while (fgets(buff, BUFFSIZE, file)){
        curr = strtok_r(buff, " ", &saveptr);
        while(curr != NULL){
            data[i] = strtod(curr, NULL);
            i++;
            curr=strtok_r(NULL, " ", &saveptr);
        }
    }
    if (numread != NULL)
        *numread=i;
    return data;
}