How to transform my code with static memory struct to C dynamics?

Asked

Viewed 221 times

4

The exercise asks me to read information in a file, being them, Cpf, name, email and age of several people store in a struct, order in ascending order by age, if equal ages by Cpf, and print in another file with the same format that received,all information of a person is separated by comma and from one person to another by a different line. And I already know the maximum size of the information, but I don’t know the number of lines. I did using static memory but must create a vector of structs using memory and dynamical and I’m not able to do it.

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

typedef struct{
 char CPF[12];
 char nome[41];
 char email[31];
 int idade;
}Dado;

int main()
{
FILE *arq, *arqout;
char ch;
int num=0,i,j,aux;

arq = fopen("read.txt","r");
while( fscanf(arq,"%c", &ch)!= EOF )
    if(ch == '\n')
    num++;
rewind(arq);
Dado pimpolho[num+1];

for (i=0;i<num;i++)
{
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].CPF[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].CPF[j] = '\0';
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].nome[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].nome[j] = '\0';
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].email[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].email[j] = '\0';
    fscanf(arq, "%d", &pimpolho[i].idade);
    fscanf(arq,"%c", &ch);
}

for (i = num - 1; i > 0; i--)
    for (j = 0; j < i; j++)
        if (pimpolho[j].idade > pimpolho[j+1].idade)
        {
            pimpolho[num] = pimpolho[j];
            pimpolho[j] = pimpolho[j+1];
            pimpolho[j+1] = pimpolho[num];
        }
for (i = num - 1; i > 0; i--)
    for (j = 0; j < i; j++)
        if (pimpolho[j].idade == pimpolho[j+1].idade)
        {
            aux = strncmp(pimpolho[j].CPF,pimpolho[j+1].CPF,11);
            if (aux>0)
            {
                pimpolho[num] = pimpolho[j];
                pimpolho[j] = pimpolho[j+1];
                pimpolho[j+1] = pimpolho[num];
            }
        }
arqout = fopen("write.txt","w");
for (i=0;i<num;i++)
{
     fprintf(arqout,"%s,%s,%s,%d\n",pimpolho[i].CPF,pimpolho[i].nome,pimpolho[i].email,pimpolho[i].idade);
}
fclose(arqout);
fclose(arq);
return 0;
}

It is possible to change this with some changes?

  • 1

    But the idea is to use a list instead of a vector with dynamic memory ? Otherwise it makes no sense. There is no advantage in creating a dynamical vector for the example you have, and complicates the code.

  • I think so, but the teacher is asking to use dynamic memory and I’m having difficulty

  • But it has to be with vector or it has to be with list ?

  • It is to be cm vector I think, by which I understood I need to declare the struct q I did as a pointer variable and then allocate the memory space dynamically to the amount of struct q the program will use, pq is one for each line of the file and the lines of the files vary. Linked list he said he wouldn’t use yet

  • It should be like in the case of my program Dado *pimpolho; and then work with it like this, but I’m not getting it is giving segmantion fault and other mistakes

  • But if you can see a form using list I think q tbm is valid

Show 1 more comment

2 answers

3


Dynamic allocation with vector

Dynamic allocation is as simple as calling malloc directly:

Dado *pimpolho = malloc(sizeof(Dado) * (num+1));

The rest you have works because when you do pimpolho[i] is equivalent to making *(pimpolho + i). It is simply a matter of whether you use array syntax or pointer syntax, and the array syntax is simpler, so you should use it when possible.

Note: Just as I said in comment, for the example that has this does not bring any advantage, quite the contrary, it makes the allocation more complicated, just as it can make some parts of the code more complicated (in this case not) and forces you to worry about the release of memory with free when you no longer need it. In this case how will use the vector until the end of the program is not worth to free the memory because it will already be released at the end of the same but in other cases has to do under penalty of getting memory leaks.

So don’t do this in your programs unless it has a purpose and concrete advantages.

Refactoring

I don’t want to pass up some important refactoring you can do that’s not complicated. Avoid repetition of logic as much as possible, as this brings much more problems than it seems. Looking at the reading of the fields you have:

for (i=0;i<num;i++)
{
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].CPF[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].CPF[j] = '\0';
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].nome[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].nome[j] = '\0';
    j=0;
    fscanf(arq,"%c", &ch);
    while( ch != ',')
    {
        pimpolho[i].email[j] = ch;
        fscanf(arq,"%c", &ch);
        j++;
    }
    pimpolho[i].email[j] = '\0';
    fscanf(arq, "%d", &pimpolho[i].idade);
    fscanf(arq,"%c", &ch);
}

This actually corresponds to the reading of the 4 fields, the CPF, name, email and age, and the first 3 are the same. But the code was repeated. Not only is it more difficult to read, but it is more extensive and makes it difficult to get wrong when you need to change because you have to change in all places in the right way. Whenever this happens it abstracts the equal logic for a function and calls it. Now look how much better:

void ler_string_arq(FILE* arq, char *campo_destino){
    int letra = 0;
    char ch;
    fscanf(arq,"%c", &ch);
    while( ch != ',') {
        campo_destino[letra] = ch;
        fscanf(arq,"%c", &ch);
        letra++;
    }
    campo_destino[letra] = '\0';
}

int main() {
   //...
   for (i=0; i<num; i++) {
       ler_string_arq(arq, pimpolho[i].CPF);
       ler_string_arq(arq, pimpolho[i].nome);
       ler_string_arq(arq, pimpolho[i].email);    
       fscanf(arq, "%d", &pimpolho[i].idade);
       fscanf(arq,"%c", &ch);
   }

In the ordination it has the same problem because it repeats two ordination logics first by ordering by age, and then ordering those who were the same age:

for (i = num - 1; i > 0; i--)
    for (j = 0; j < i; j++)
        if (pimpolho[j].idade > pimpolho[j+1].idade)
        {
            pimpolho[num] = pimpolho[j];
            pimpolho[j] = pimpolho[j+1];
            pimpolho[j+1] = pimpolho[num];
        }

for (i = num - 1; i > 0; i--)
    for (j = 0; j < i; j++)
        if (pimpolho[j].idade == pimpolho[j+1].idade)
        {
            aux = strncmp(pimpolho[j].CPF,pimpolho[j+1].CPF,11);
            if (aux>0)
            {
                pimpolho[num] = pimpolho[j];
                pimpolho[j] = pimpolho[j+1];
                pimpolho[j+1] = pimpolho[num];
            }
        }

This is entirely unnecessary because you can do both at once:

for (i = num - 1; i > 0; i--)
    for (j = 0; j < i; j++)
        if (pimpolho[j].idade > pimpolho[j+1].idade ||
            (pimpolho[j].idade == pimpolho[j+1].idade && strncmp(pimpolho[j].CPF,pimpolho[j+1].CPF,11) > 0)){
            pimpolho[num] = pimpolho[j];
            pimpolho[j] = pimpolho[j+1];
            pimpolho[j+1] = pimpolho[num];
        }

I won’t dwell on this part any longer, but bear in mind that the change made in ordination is by copying. This can become quite inefficient if the amount of data is too large as it requires you to copy tons of bytes back and forth to make the exchange. The way to solve is to use an array of pointers instead of an array with all objects directly, but this implies changing almost the entire code, and is probably exaggerated for the exercise in question.

There are other details that can be improved, of course, but I have focused only on those that are stronger and have more impact on the code in general.

Dynamic allocation with list

The small difference of using a list instead of a vector already makes sense, because actually using a list does not need to know how many elements have previously. This causes you not to have to go through the file twice being the first to find the number of people that exist. You can simply read, allocate and connect the pointers to each other. Now the code itself is more complicated because it involves allocations, pointer exchanges, memory release, etc...

First implies changing the structure so that each person can have a pointer to the next:

typedef struct Dado{
//               ^----
    char CPF[12];
    char nome[41];
    char email[31];
    int idade;
    struct Dado* proximo; //<---
} Dado;

Then you have to have a way to notice when you have reached the end of the file. The most direct is to interpret this in the reading of the first field the CPF. For this the simplest is to change the function ler_string_arq to return 0 when you reached the end of the archive:

int ler_string_arq(FILE* arq, char *campo_destino){
//^----tipo int agora
    int letra = 0;
    char ch;
    if (fscanf(arq,"%c", &ch) != 1){ //se não leu um char então chegou ao fim
        return 0;
    }

    while( ch != ',') {
        campo_destino[letra] = ch;
        fscanf(arq,"%c", &ch);
        letra++;
    }
    campo_destino[letra] = '\0';
    return 1;
}

Then the while reading is now also quite different:

Dado *inicio_lista = NULL, *ultima = NULL; //ponteiros para lista e ultima pessoa

while(1) {
    Dado *pessoa = malloc(sizeof(Dado)); //cria nova pessoa com alocação dinamica
    pessoa->proximo = NULL; //proximo da pessoa criada é nulo
    if (inicio_lista  == NULL){ //se ainda nao tem nenhuma esta é a primeira 
        inicio_lista = pessoa;
    }
    if (ultima != NULL) { //se já tem pessoas liga a anterior a esta
        ultima->proximo = pessoa;
    }

    if (!ler_string_arq(arq, pessoa->CPF)){ //se apanhou EOF
        free(pessoa);
        ultima->proximo = NULL;
        break; //sai
    }
    ultima = pessoa;

    ler_string_arq(arq, pessoa->nome);
    ler_string_arq(arq, pessoa->email);
    fscanf(arq, "%d", &pessoa->idade);
    fscanf(arq,"%c", &ch);
}
fclose(arq);

For writing it would be quite identical changing the syntax:

arqout = fopen("write.txt","w");
Dado* pessoa = inicio_lista;
while (pessoa!= NULL){ //enquanto nao chega ao fim da lista
    fprintf(arqout,"%s,%s,%s,%d\n", pessoa->CPF, pessoa->nome, pessoa->email, pessoa->idade);
    pessoa = pessoa->proximo; //avança para a proxima pessoa
}

Note that I purposely omitted the ordering part of the people, because now with a linked list it is much more complicated and I do not want to go any further than the answer is already quite large. Usually these list sorts are done with Merge Sort and end up perhaps in the scenario where I indicated to be ordering with pointers and so are quite efficient.

Moreover the complexity between the two sorting algorithms is quite different because the Merge Sort performs in O(nlogn) whereas the Bubble Sort you have runs on O(n²).

  • Thank you very much even helped me especially the part with list linked because I started studying her this week, I will try to make the code using list linked now, if you can give a help in the ordering part of her I would appreciate, but if it is too much work all right, I will search more on how to sort lists

  • @Heigonsoldera You’re welcome, I’m glad I helped :). If I then ask another question about that, I can certainly help, but this one does deviate somewhat from what has been asked. Now before doing so I advise you to explore the code well and make sure you understand everything that has been done, that jumping from vector to list still adds a good deal to the complexity due to pointers and memory management among other things.

  • OK mt thanks, I’m still exploring the code and trying to understand everything, however I’m still having difficulty sorting, I’ll open another question tab so, Thank you so much for everything

1

I hope it helps you:

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

typedef struct
{
   char CPF[12];
   char nome[41];
   char email[31];
   int idade;
}Dado;

int main()
{
   FILE *arq, *arqout;
   char ch;
   int num = 0, i, j, aux;
   //melhorando o código
   //arq = fopen("read.txt", "r");
   //verifica se ocorreu algum erro
   if ((arq = fopen("read.txt", "r")) == NULL)
   {
      printf("ocorreu um erro ao abrir o arquivo read.txt!\n");
      return 0;
   }
   //sempre coloque chaves em seu código para que não fique confuso
   while (fscanf(arq, "%c", &ch) != EOF)
   {
      if (ch == '\n')
      {
         num++;
      }
   }
   rewind(arq);
   //primeira modificação transformar a estrutura de Dado estatico para dinamico
   //Dado pimpolho[num + 1];
   Dado * pimpolho = new Dado[num + 1];
   //agora ele e dinamico e sempre verifica se a alocação ocorreu com sucesso
   if (pimpolho == NULL)
   {
      return 0;
   }

   for (i = 0; i < num; i++)
   {
      j = 0;
      fscanf(arq, "%c", &ch);
      while (ch != ',')
      {
         pimpolho[i].CPF[j] = ch;
         fscanf(arq, "%c", &ch);
         j++;
      }
      pimpolho[i].CPF[j] = '\0';
      j = 0;
      fscanf(arq, "%c", &ch);
      while (ch != ',')
      {
         pimpolho[i].nome[j] = ch;
         fscanf(arq, "%c", &ch);
         j++;
      }
      pimpolho[i].nome[j] = '\0';
      j = 0;
      fscanf(arq, "%c", &ch);
      while (ch != ',')
      {
         pimpolho[i].email[j] = ch;
         fscanf(arq, "%c", &ch);
         j++;
      }
      pimpolho[i].email[j] = '\0';
      fscanf(arq, "%d", &pimpolho[i].idade);
      fscanf(arq, "%c", &ch);
   }
   //sempre coloque chaves em seu código para que não fique confuso
   for (i = num - 1; i > 0; i--)
   {
      for (j = 0; j < i; j++)
      {
         if (pimpolho[j].idade > pimpolho[j + 1].idade)
         {
            pimpolho[num] = pimpolho[j];
            pimpolho[j] = pimpolho[j + 1];
            pimpolho[j + 1] = pimpolho[num];
         }
      }
   }
   //sempre coloque chaves em seu código para que não fique confuso
   for (i = num - 1; i > 0; i--)
   {
      for (j = 0; j < i; j++)
      {
         if (pimpolho[j].idade == pimpolho[j + 1].idade)
         {
            aux = strncmp(pimpolho[j].CPF, pimpolho[j + 1].CPF, 11);
            if (aux > 0)
            {
               pimpolho[num] = pimpolho[j];
               pimpolho[j] = pimpolho[j + 1];
               pimpolho[j + 1] = pimpolho[num];
            }
         }
      }
   }
   //verifica se ocorreu algum erro
   //arqout = fopen("write.txt", "w");
   if ((arqout = fopen("write.txt", "w")) == NULL)
   {
      //libera a memoria utilizada
      delete[]pimpolho;
      printf("ocorreu um erro ao criar o arquivo write.txt!\n");
      return 0;
   }
   for (i = 0; i < num; i++)
   {
      fprintf(arqout, "%s,%s,%s,%d\n", pimpolho[i].CPF, pimpolho[i].nome, pimpolho[i].email, pimpolho[i].idade);
   }
   fclose(arqout);
   fclose(arq);
   //terminou libere a memoria utilizada
   delete[] pimpolho;
   return 0;
}
  • 1

    new and delete are reserved words of C++!

  • I’m really sorry it was my code lapse and in c and not c++;

  • the right e: Data * <pointer_variable> = (Data *)malloc(sizeof(Data) * num + 1); and releases free(<pointer_variable>);

Browser other questions tagged

You are not signed in. Login or sign up in order to post.