gpt4 book ai didi

c - 结构和动态数组

转载 作者:行者123 更新时间:2023-11-30 19:03:46 25 4
gpt4 key购买 nike

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


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

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

关于c - 结构和动态数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53452504/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com