#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;
}