0

Why does this code only writes the first and the last occurrences of nome, cognome and cf?

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

typedef struct data{
    char nome[20];
    char cognome[20];
    char cf[20];
    }data;

void leggi_file(FILE *input, data **array, int *dim);
void aggiorna(data *vect, char *nome, char *cognome, char *cf, int dim);
void scrivi_file(FILE *output, data *vect, int dim);


int main(void){
    FILE *input, *output; 
    input = fopen("prog1file.txt", "r");
    output = fopen("prog1file2.txt", "w");
    data *vect1;
    int dim_vect1 = 0;

    leggi_file(input,&vect1,&dim_vect1);

    char *nome="Michele", *cognome="Nappi", *cf="nppmhl69p22b119f";
    aggiorna(vect1, nome, cognome, cf, dim_vect1);

    scrivi_file(output, vect1, dim_vect1);

    return 0;
}   

void leggi_file(FILE *input, data **array, int *dim){
    char nome[20], cognome[20], cf[17];
    int i;
    while(fscanf(input, "%s %s %s", nome, cognome, cf) == 3)
        (*dim)++;
    *array = malloc((*dim + 1) * sizeof(data));
    rewind(input);
    for(i=0; i<(*dim); i++)
        fscanf(input, "%s %s %s", array[i]->nome, array[i]->cognome, array[i]->cf);
}

void aggiorna(data *vect, char *nome, char *cognome, char *cf, int dim){
    strcpy(vect[dim].nome, nome);
    strcpy(vect[dim].cognome, cognome);
    strcpy(vect[dim].cf, cf);
}

void scrivi_file(FILE *output, data *vect, int dim){
    int i;
    for(i=0; i<(dim+1); i++)
        fprintf(output, "%s %s %s\n", vect[i].nome, vect[i].cognome, vect[i].cf);
}

It should write the occurrences of nome, cognome and cf that are stored in the first file in the second, and then add another one which was passed from the main. Why does it only write the first and the last?

In the output file I get something like this:

Giuseppe Nappo nppgspmhl65t23c126a


Michele Nappi nppmhl69p22b119f
  • 1
    This might be a good time to [learn how to debug your programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – Some programmer dude Jan 31 '19 at 08:43
  • 1
    In function `leggi_file()` you have allocated an array of pointers with `*array = malloc((*dim + 1) * sizeof(data));` but not initialised the pointers themselves. – Weather Vane Jan 31 '19 at 08:46
  • @WeatherVane I think I initialized it in the main – user10994677 Jan 31 '19 at 08:48
  • 1
    @WeatherVane This is an array of structs not pointers. I think the malloc is OK. (My C foo is a little out of condition these days so I could easily have missed something) – Euan Smith Jan 31 '19 at 08:54
  • I was reading it as though `sizeof(data)` is the size of the pointer, but it isn't. – Weather Vane Jan 31 '19 at 08:56
  • So, by first and last you mean the first entry in the file and the "Michele Nappi" entry you provide yourself? If so then I thing the problem is that "leggi_file" function is only reading the first line. You need to put some debug in tere. It may be the fscanf is not dealy with newline characters (for example). – Euan Smith Jan 31 '19 at 08:57
  • @EuanSmith even if the strings are on the same line it still writes only the first and the last occurrences – user10994677 Jan 31 '19 at 09:01
  • @user10994677 have you added some debug to find out if the problem is in the reading or writing? You need to narrow down where the problem lies. – Euan Smith Jan 31 '19 at 09:03
  • OT: regarding: `input = fopen("prog1file.txt", "r"); output = fopen("prog1file2.txt", "w");` Always check (!=NULL) the returned value to assure the operation was successful. If not successful, call `perror( "your error message" ); then `exit( EXIT_FAILURE );` The `perror()` will output both your error message and the text reason the system thinks the error occurred to `stderr` – user3629249 Jan 31 '19 at 09:05
  • OT: always enable the warnings when compiling, then fix those warnings. The compiler will output: "untitled2.c:38:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]" Such problems should be corrected – user3629249 Jan 31 '19 at 09:08
  • OT: why is the struct field `cy` defined as 20 bytes but the local variable `cy` in function: `leggi_file()` defining the field as only 17 bytes? – user3629249 Jan 31 '19 at 09:12
  • @user3629249 I fixed it. I still doesn't work. – user10994677 Jan 31 '19 at 09:16
  • OT: regarding: `while(fscanf(input, "%s %s %s", nome, cognome, cf) == 3)` when using the input format specifiers '%s' and/or '%[..]' always specify a MAX CHARACTERS modifier that is 1 less than the length of the input field because those specifiers always append a NUL byte to the input. This also avoids any possibility of a buffer overflow and the resulting undefined behavior – user3629249 Jan 31 '19 at 09:16
  • OT: regarding: `fscanf(input, "%s %s %s", array[i]->nome, array[i]->cognome, array[i]->cf);` this is missing the check (==3) that assures the operation was successful – user3629249 Jan 31 '19 at 09:21

1 Answers1

3

In the leggi_file function, array is a pointer to a pointer of structures. Think of it as a pointer to an array of structures. It is most definitely not an array of pointers to structures (as the use array[i]->nome would indicate).

The correct way is to dereference the pointer to get the array (of structures), as in (*array)[i].nome.

With array[i]->nome you treat array as an array of pointers, which it is not. It's (effectively, but not semantically) a pointer to an array.

By using array[i]->nome you will go out of bounds of the memory for array, leading to undefined behavior. You're lucky (or unlucky depending on point of view) that the code don't crash.

The only line you need to change is this one:

fscanf(input, "%s %s %s", array[i]->nome, array[i]->cognome, array[i]->cf);

Which you should change into

fscanf(input, "%s %s %s", (*array)[i].nome, (*array)[i].cognome, (*array)[i].cf);
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621