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²)
.
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.
– Isac
I think so, but the teacher is asking to use dynamic memory and I’m having difficulty
– heigon77
But it has to be with vector or it has to be with list ?
– Isac
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
– heigon77
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
– heigon77
But if you can see a form using list I think q tbm is valid
– heigon77