我是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); }

最佳答案

我在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;。您必须将返回值分配给调用者中的指针。您还必须在free (buf);之前使用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);


(注意:您还应该传递类型为int *numelem的第三个参数,以便可以在返回之前分配*numelem = i;,如果实际读取的元素少于v,则可以在调用者中返回填充的元素数)

如果您将发布A Minimal, Complete, and Verifiable Example (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/memcpystrdup -如果有的话,它会全部执行三个操作)

有了该修复程序并对您的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)


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

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

09-09 20:55