У вас есть две основные проблемы, которые я вижу в crea_array
,
- Вы объявляете
char* r;
, но затем пытаетесь присвоить возврат sscanf (buf, "%s %d/%d/%d %d\n", ...
(например, r = sscanf (...
. Это неверно.sscanf
возвращает тип int
, представляющий количество успешных преобразований, которые имели место, как указано в строке формата (например, "%s %d/%d/%d %d\n"
вернет 5
в случае успеха, а удалит '\n'
, это вызовет проблемы.) Ваш компилятор должен кричать на вас предупреждения. Если нет, вам нужно включить предупреждения компилятора, добавив -Wall -Wextra -pedantic
в качестве параметров компилятора и не принимать код, пока он не скомпилируется безодиночное предупреждение . crea_array
должно быть объявлено как тип tipo_dati_file *
и должно содержать return array;
в конце. Вы должны назначить возврат указателю обратно в вызывающей стороне. Вы также должны *За 1025 * до return
или вы только что создали утечку памяти , поскольку невозможно free()
памяти, выделенной для buf
после возврата из функции. (Далее, если вы простовыделяя 1000-char
каждыйвремя, просто используйте фиксированный буфер, например, char buf[1000];
и избавьте от необходимости выделять buf
полностью.
В целом, вы можете сделать что-то похожее на:
#define MAXC 1000 /* if you need a constant, #define one (or more) */
tipo_dati_file *crea_array (char* Nome_f, int v)
{
int i = 0,
s,
r;
char buf[MAXC] = "";
//tipo_ritorna risultati;
FILE* file;
//n = conta_righe(file);
tipo_dati_file *array = malloc (v * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
exit (EXIT_FAILURE);
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
exit (EXIT_FAILURE);
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", array[i].regione,
&array[i].data.giorno, &array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fput ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
printf ("%s %d/%d/%d %d\n", array[i].regione, array[i].data.giorno,
array[i].data.mese, array[i].data.anno, array[i].mm_pioggia);
i++;
}
fclose (file);
return array;
}
Примечание: Я не скомпилировал приведенный выше код.
Тогда в main
вы можете сделать что-то похожее на:
tipo_dati_file *array = crea_array (name, v);
(примечание: вы также должны передать 3-й параметр типа int *numelem
, чтобы вы могли присвоить *numelem = i;
перед возвратом, сделав доступным количество заполненных элементов обратно в вызывающей стороне, если фактически прочитано меньше v
)
Если вы опубликуете Минимальный, полный и проверяемый пример (MCVE) вместе с образцом файла данных (10 строк или около того), я буду рад помочь вам в дальнейшем и могу подтвердитькод работает - поскольку у меня будет что-то, что я могу скомпилировать и запустить.
Редактировать следующие предупреждения опубликовано (в комментариях)
Два предупреждения подробно рассматриваются вкомментарии под ответом.Как только они будут решены, вы столкнетесь с ужасным SegFault
, так как не выделяете хранилище для array[i].regione
.
В вашем вложенном наборе структур:
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
regione
это неинициализированный указатель, который указывает на некоторую неопределенную область памяти (которой вы не владеете).Когда вы пытаетесь написать символы там с помощью sscanf
(BOOM - SegFault - скорее всего).
У вас есть два варианта: (1) объявлять regione
как фиксированный массив, например, char regione[CONST]
(неэффективно)или (2) прочитайте строку для regione
во временный буфер, а затем выделите память для strlen + 1
символов и скопируйте строку из временного буфера в новый блок памяти и назначьте начальный адресдля этого блока до regione
(вы можете использовать strlen/malloc/memcpy
или strdup
- если он у вас есть, он делает все три)
С этим исправлением и несколькими изменениями для вашей функции crea_array
(например, передав указатель на int
для хранения номера структуры, заполненной в array
вместо v
), вы можете сделать что-то вроде следующего:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXDATA 64
typedef enum mese_e { Genv= 1, Feb, Mar, Apr, Mag, Giu,
Lug, Ago, Set, Ott, Nov, Dic
} tipo_mese;
typedef struct data_s {
int giorno;
tipo_mese mese;
int anno;
} tipo_data;
typedef struct dati_file_s {
char* regione;
tipo_data data;
int mm_pioggia;
} tipo_dati_file;
typedef struct ritorna_s {
tipo_dati_file* array;
int count;
} tipo_ritorna;
tipo_dati_file *crea_array (char *Nome_f, int *nelem)
{
int i = 0,
r;
char region[MAXC] = ""; /* temp buffer to hold array[i].regione */
char buf[MAXC] = "";
FILE* file;
tipo_dati_file *array = malloc (MAXDATA * sizeof *array);
file = fopen (Nome_f, "r");
if (file == NULL) {
printf ("Errore in apertura del file!");
return NULL;
}
if (!array) { /* if you allocate, you must validate - every time */
perror ("malloc-array");
return NULL;
}
while (fgets (buf, MAXC, file) != NULL)
{
r = sscanf (buf, "%s %d/%d/%d %d", region,
&array[i].data.giorno, (int*)&array[i].data.mese,
&array[i].data.anno, &array[i].mm_pioggia);
if (r != 5) { /* validate return of every (s)scanf funciton */
fputs ("error: failed to parse buf.\n", stderr);
continue; /* get next line */
}
array[i].regione = strdup (region);
if (!array[i].regione) { /* strdup allocates - you must validate */
perror ("strdup-array[i].regione");
for (int j = 0; j < i; j++) /* on failure free prior mem */
free (array[j].regione); /* and return NULL */
free (array);
return NULL;
}
i++;
}
fclose (file);
*nelem = i; /* update nelem with number of struct filled */
return array;
}
int main (int argc, char **argv) {
int index = 0,
nelem = 0;
char *datafile = argc > 1 ? argv[1] : "dat/staterain.txt";
tipo_ritorna statistics[MAXDATA] = {{ .array = NULL }};
statistics[index].array = crea_array (datafile, &nelem);
if (statistics[index].array && nelem > 0) {
statistics[index].count = nelem;
for (int i = 0; i < statistics[index].count; i++) {
printf ("%-12s %02d/%02d/%4d %3d\n",
statistics[index].array[i].regione,
statistics[index].array[i].data.giorno,
statistics[index].array[i].data.mese,
statistics[index].array[i].data.anno,
statistics[index].array[i].mm_pioggia);
free (statistics[index].array[i].regione); /* free strings */
}
free (statistics[index].array); /* free array */
}
return 0;
}
Пример Использование / Вывод
$ ./bin/staterain
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
Использование памяти / проверка ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанности относительнолюбой выделенный блок памяти: (1) всегда сохраняет указатель наНачальный адрес для блока памяти, таким образом, (2) он может быть освобожден , когда он больше не нужен.
Обязательно, чтобы вы использовали программу проверки ошибок памятичтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, попытайтесь прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
является нормальным выбором.Для каждой платформы есть похожие проверки памяти.Все они просты в использовании, просто запустите вашу программу через нее.
$ valgrind ./bin/staterain
==3349== Memcheck, a memory error detector
==3349== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3349== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==3349== Command: ./bin/staterain
==3349==
Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
==3349==
==3349== HEAP SUMMARY:
==3349== in use at exit: 0 bytes in 0 blocks
==3349== total heap usage: 5 allocs, 5 frees, 2,110 bytes allocated
==3349==
==3349== All heap blocks were freed -- no leaks are possible
==3349==
==3349== For counts of detected and suppressed errors, rerun with: -v
==3349== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.