结构和动态数组

时间:2018-11-23 20:16:49

标签: c arrays struct

我是c语言的新人,对我英语不好的人感到抱歉。

我正在尝试编写一个程序,询问用户是否要使用键盘输入数据(区域,检测日期,下雨毫米)并将其保存在文件中,或者是否要给它提供文件名。 目前没有问题,文件已正确写入或读取。 文件具有以下结构:

Texas 03/03/2015 1
California 06/02/2013 5
Utah 03/01/2014 10
....

尝试使用scanf()(不要报告main,因为其中没有问题。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum mese_e {Gen=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;

int conta_righe(char* Nome_f)
{
    int i=0;
    char c;
    FILE* file;
    file=fopen(Nome_f,"r");
    while ((c=fgetc(file))!=EOF)
    {if(c=='\n')
    i++;}
    fclose(file);
    return i;

}
void crea_array (char* Nome_f)
{
    int i,n;
    char* regione= (char*)malloc(sizeof(char));
    tipo_data data;
    int mm_pioggia;
    tipo_ritorna risultati;
    FILE* file;
    n = conta_righe(Nome_f);
    printf("%d\n",n);
    tipo_dati_file* array = (tipo_dati_file*) malloc (n*sizeof (tipo_dati_file));
    file = fopen(Nome_f,"r");
    if( file==NULL )
        {
            printf("Errore in apertura del file!");
            exit(1);
        }
    for(i=0; i<=n; i++)
    {
        fscanf(file,"%s %d/%d/%d %d\n",regione, &data.giorno, &data.mese, &data.anno, &mm_pioggia);
        strcpy(array[i].regione, regione);
        array[i].data.giorno=data.giorno;
        array[i].data.mese= data.mese;
        array[i].data.anno= data.anno;
        array[i].mm_pioggia= mm_pioggia;
        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);
        }

    fclose(file);
}

尝试使用fgets()

#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum mese_e {Gen=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;

int conta_righe(char* Nome_f) {
    int i=0;
    char c;
    FILE* file;
    file=fopen(Nome_f,"r");
    while ((c=fgetc(file))!=EOF)
    {if(c=='\n')
    i++;}
    fclose(file);
    return i;

} void crea_array (char* Nome_f, int v) {
    int i=0,s;
    char* r;
    //tipo_ritorna risultati;
    FILE* file;
    //n = conta_righe(file);
    tipo_dati_file* array = (tipo_dati_file*) malloc (v*sizeof (tipo_dati_file));
    file = fopen(Nome_f,"r");

    if( file==NULL )
        {
            printf("Errore in apertura del file!");
            exit(1);
        }
    if (feof(file)==0)
    {
        char* buf= (char*) malloc(v*sizeof(char));
        /*while ( fgets( buf,10000, file) != NULL )
        {
            r = sscanf( buf, "%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);
            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++;
        }*/
        while(1)
        {
            r=fgets( buf,1000, file);
            if (r!=NULL)
            {
                printf("%s",buf);
                sscanf( buf, "%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);
                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++;
            }
               else exit(1);
        }
    }
    else exit(1);

    fclose(file); }

1 个答案:

答案 0 :(得分:1)

您在crea_array中遇到了两个主要问题,

  1. 您声明了char* r;,但随后尝试分配sscanf (buf, "%s %d/%d/%d %d\n", ...的返回值(例如r = sscanf (...。这是不正确的。sscanf返回表示数字的int类型您在格式字符串中指定的成功转化次数(例如"%s %d/%d/%d %d\n"成功返回5,并删除 {{1} },这会引起问题。)您的编译器应该向您发出警告。如果没有,您需要通过添加'\n'作为编译器选项来启用编译器警告,并且在不进行任何编译的情况下,不接受代码警告
  2. -Wall -Wextra -pedantic必须声明为类型crea_array,并且结尾必须为tipo_dati_file *。您必须将返回值分配给调用者中的指针。您还必须在return array;之前free (buf);,否则您刚刚创建了内存泄漏,因为无法return为{{1}分配的内存函数返回之后。 (此外,如果您每次只是分配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的第3个参数,以便您可以在返回之前分配tipo_dati_file *array = crea_array (name, v); ,以使填充的元素数在调用方中返回实际读取的数据少于int *numelem

如果您将发布A Minimal, Complete, and Verifiable Example (MCVE)以及示例数据文件(大约10行),我很乐意为您提供进一步的帮助,并且我可以验证代码的工作方式,因为我可以编译并运行。

编辑已发布的警告(注释中)

这两个警告在答案下方的注释中得到了详细说明。解决这些问题后,您将陷入*numelem = i;的可怕境地,因为您没有为v分配存储空间。

在您嵌套的一组结构中:

SegFault

array[i].regione是一个未初始化的指针,指向一些不确定的内存位置(您不拥有该内存位置)。当您尝试使用typedef struct dati_file_s { char* regione; tipo_data data; int mm_pioggia; } tipo_dati_file; 在此处写字符时(BOOM-SegFault-很有可能)。

您有两个选择(1)将regione声明为固定数组,例如sscanf(效率低下),或(2)将regione的字符串读入临时缓冲区,然后为char regione[CONST]个char分配存储空间,并从临时缓冲区到新的内存块,并将该块的起始地址分配给regione(您可以使用strlen + 1regione-如果有的话,它会全部执行三个操作)< / p>

通过此修复程序和对strlen/malloc/memcpy函数的一些调整(例如,传递指向strdup的指针以保留填充在crea_array中而不是int的结构数) ,您可以执行以下操作:

array

使用/输出示例

v

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,#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

始终确认已释放已分配的所有内存,并且没有内存错误。

仔细检查一下,如果还有其他问题,请告诉我。