Почему я получаю ошибку seg каждый раз, когда я пытаюсь сохранить элементы в массив на C?

#c #pointers #io #segmentation-fault #malloc

#c #указатели #io #ошибка сегментации #malloc

Вопрос:

Итак, я пытаюсь прочитать матрицу из файла и сохранить ее в массив.

Файлы матрицы выглядят следующим образом:

 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," ",amp;buff);
         int i=0;
         while(curr!=NULL){
             curr=strtok_r(NULL," ",amp;buff);
             data[i]=strtod(curr,NULL); //this
             i  ;
         }
     }
     return data;
}
  

Всякий раз, когда я выполняю отладку с помощью GDB и использую команду backtrace, это приводит меня к строке с комментарием //this .

У меня проблемы с пониманием того, что я сделал не так, и я начинаю сходить с ума!

Комментарии:

1. используйте perror для печати сообщения об ошибке, понятного для пользователя

2. Это неправильно: char *buff=malloc(sizeof(char *)*256);

3. Похоже, что эта строка curr=strtok_r(NULL," ",amp;buff); должна быть после strtod строки, если вы не хотите отбросить первое значение.

4. @RetiredNinja Действительно. Новички, я прав? Кажется, у меня также есть какая-то другая ошибка, мои данные неправильно хранятся в массиве данных после strtod. Спасибо!

5. Избегайте неправильного распределения. char *buff=malloc(sizeof(char *)*256); —> char *buff=malloc(sizeof *buf * 256);

Ответ №1:

Это

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

должно быть

      while(curr!=NULL){
         data[i]=strtod(curr,NULL); //this
         i  ;
         curr=strtok_r(NULL," ",amp;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 , вы избегаете простых несоответствий типов, как у вас было.

Комментарии:

1. Что на самом деле делает sizeof * buff, если я вызываю malloc, я выделяю память для buff? Это размер указателя char, умноженный на константу 256?

2. Поскольку buff это указатель на символ, *buff это символ, поэтому sizeof *buff это то же самое, что sizeof(char) . Это просто более безопасный способ написания вашего malloc

Ответ №2:

Вы не проверяете значение curr перед вызовом strtod() .

Это:

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

Должно быть:

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

Комментарии:

1. Я считаю, что это решение неверно, поскольку оно все равно отменит результат первого вызова strtok_r (который происходит вне цикла). Следовательно, вызов strtod должен появляться перед вызовом strtok_r в бесконечном цикле.

Ответ №3:

Вам нужно проверить возвращаемое значение из strtok_r непосредственно перед рассматриваемой строкой.

Ответ №4:

В вашем случае вам даже не нужно использовать 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 = '';
      }

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

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

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

Ответ №5:

В вашем коде есть несколько проблем, которые не были рассмотрены в других ответах:

 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 вычисляется не с учетом размера вашего буфера, а только с учетом размера a char* , который в большинстве систем равен 4 или 8. Этого может быть недостаточно для полной строки.

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

Вы изменяете buff внутри цикла. Когда вы используете его для fgets на следующей итерации цикла, вы не будете использовать адрес, который вы получили от malloc .

          int i=0;
  

Вы устанавливаете индекс для своего массива равным 0 для каждой прочитанной строки. В конце концов, это сохранит только значения из последней строки.

 
         while(curr!=NULL){
             curr=strtok_r(NULL," ",amp;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, " ", amp;saveptr);
        while(curr != NULL){
            data[i] = strtod(curr, NULL);
            i  ;
            curr=strtok_r(NULL, " ", amp;saveptr);
        }
    }
    if (numread != NULL)
        *numread=i;
    return data;
}