为什么函数中的 Printf 可以防止结构数组中的变量在另一个函数中损坏?

Why is a Printf in a Function Preventing a Variable in a Struct Array from going Corrupt in Another Function?

我有一个函数可以读取一个文件并将属性赋给一个结构数组多个整数和字符串,我的 fscanf 格式字符串如下所示:"%d.%d.%d.%d %d %60c%60c%d %8c"

如果我扫描它并使用 printf 时一切正常,但如果我不这样做,那么最后的 2 个字符串最后会损坏。

由于文件很大,这不是函数的目的,我想把那个 printf 拿出来。

 typedef struct
 {
    int nacional;
    int regional;
    int distrital;
    int municipal;
  }id_geo;

  typedef struct
  {
    id_geo id_geo;
    long int cartao_cid;
    char nome_dono[61];
    char morada[61];
    int num_porta;
    char codigo_postal[9];
   }prop_id_dono;
prop_id_dono *ler_ficheiro(char *file_name, int num_linha) {
    prop_id_dono *info_geral;
    long int cartao_cid;
    char nome_ficheiro;
    char nome_dono[60];
    char morada[60];
    int num_porta;
    char codigo_postal[8]; 
    int nacional;
    int regional;
    int distrital;
    int municipal; 
    int i = 0;
    FILE *fp; 

    fp = fopen(file_name, "r");
    if (fp == NULL)
        printf("Peço desculpa, mas não foi possível abrir o ficheiro.");

    info_geral = (prop_id_dono *)malloc(sizeof(prop_id_dono) * num_linhas); 

    while (fscanf(fp, "%d.%d.%d.%d %ld %60c%60c%d %8c\n",
           &nacional, &regional, &distrital, &municipal,
           &cartao_cid, nome_dono, morada, &num_porta, codigo_postal) != EOF) {

        // Faz o scan ao ficheiro através de variáveis temporais, sendo que só 
        // depois é que atribui valores ao vetor que contém a informação geral
        info_geral[i].id_geo.nacional = nacional;
        info_geral[i].id_geo.regional = regional;
        info_geral[i].id_geo.distrital = distrital;
        info_geral[i].id_geo.municipal = municipal;
        info_geral[i].cartao_cid = cartao_cid;
        strcpy(info_geral[i].nome_dono, nome_dono + '[=10=]');
        strcpy(info_geral[i].morada, morada + '[=10=]');
        info_geral[i].num_porta = num_porta;
        strcpy(info_geral[i].codigo_postal, codigo_postal + '[=10=]');

        //This is the magical printf
        printf("%d.%d.%d.%d %ld %s%s%d %s \n",
               info_geral[i].id_geo.nacional, info_geral[i].id_geo.regional,
               info_geral[i].id_geo.distrital, info_geral[i].id_geo.municipal,
               info_geral[i].cartao_cid, info_geral[i].nome_dono,
               info_geral[i].morada, info_geral[i].num_porta, 
               info_geral[i].codigo_postal);
        i++;    
    }
    fclose(fp);
    return info_geral;
}

输入是这样的,但它在文件中重复了 20,000 次 1.1.1.1 1234568 姓名,共60个字符(包括空格)地址(此处空格相同)门牌号码邮编

您将 %60c 的输入片段解析为大小为 60 的字符数组。

假设转换成功,这些数组将不包含正确的 C 字符串,并且您尝试连接 '[=13=]' 在 C 中不起作用。

以下是改进程序的方法:

  • 您应该将行读入本地数组以便在转换失败时更容易报告错误,并使用 sscanf() 进行解析。
  • 您应该使目标数组长一个字节并在末尾设置一个空终止符。 prop_id_dono 结构中的数组也应该足够大。
  • 您应该检查 sscanf 的 return 值并报告转换失败
  • 您应该在 num_linhas 行后停止扫描。
  • 你应该检查内存分配失败。

这是更正后的版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int nacional;
    int regional;
    int distrital;
    int municipal;
} id_geo;

typedef struct {
    id_geo id_geo;
    long int cartao_cid;
    char nome_dono[61];
    char morada[61];
    int num_porta;
    char codigo_postal[9];
} prop_id_dono;

prop_id_dono *ler_ficheiro(const char *file_name, int num_linha) {
    char buf[256];
    prop_id_dono *info_geral;
    long int cartao_cid;
    char nome_ficheiro;
    char nome_dono[61];
    char morada[61];
    int num_porta;
    char codigo_postal[9]; 
    int nacional;
    int regional;
    int distrital;
    int municipal; 
    int i = 0;
    FILE *fp; 

    fp = fopen(file_name, "r");
    if (fp == NULL) {
        printf("Peço desculpa, mas não foi possível abrir o ficheiro.");
        return NULL;
    }
    // use calloc to allocate an array initialized to all bits zero
    info_geral = (prop_id_dono *)calloc(num_linhas, sizeof(prop_id_dono));
    if (info_geral == NULL) {
        fclose(fp);
        return NULL;
    }

    while (i < num_linhas && fgets(buf, sizeof buf, fp) != NULL) {
        // Faz o scan ao ficheiro através de variáveis temporais, sendo que só 
        // depois é que atribui valores ao vetor que contém a informação geral
        if (sscanf(buf, "%d.%d.%d.%d %ld %60c%60c%d %8c",
                   &nacional, &regional, &distrital, &municipal,
                   &cartao_cid, nome_dono, morada, &num_porta, codigo_postal) != 9) {
            printf("parsing error: %s", buf);
            continue;
        }
        info_geral[i].id_geo.nacional = nacional;
        info_geral[i].id_geo.regional = regional;
        info_geral[i].id_geo.distrital = distrital;
        info_geral[i].id_geo.municipal = municipal;
        info_geral[i].cartao_cid = cartao_cid;
        nome_dono[60] = '[=10=]';
        strcpy(info_geral[i].nome_dono, nome_dono);
        morada[60] = '[=10=]';
        strcpy(info_geral[i].morada, morada);
        info_geral[i].num_porta = num_porta;
        codigo_postal[8] = '[=10=]';
        strcpy(info_geral[i].codigo_postal, codigo_postal);
        i++;    
    }
    fclose(fp);
    // note that the caller does not receive the number of lines parsed.
    // passing the address of a int for this purpose if a good solution.
    return info_geral;
}