Dynamic allocation with vector
Allocating dynamically 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 doing *(pimpolho + i)
. It is simply a question of whether you use array syntax or pointer syntax, and array is simpler and should therefore be used when possible.
Note : As I said in the commentary, for the example you have 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 releasing it with free
when you no longer need it. In this case how you will use the vector until the end of the program is not worth releasing the memory because it will already be released at the end of it but in other cases it has to do it otherwise memory leaks.
So do not do this in your programs unless it has a concrete goal and benefits.
Refactoring
I do not want to let go of some important refactorings that you can do that are not complicated. Avoid maximizing the repetition of logic, as this brings much more trouble than it seems. Looking at reading 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] = '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] = '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];
}
}
';
}
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);
}
';
j=0;
fscanf(arq,"%c", &ch);
while( ch != ',')
{
pimpolho[i].nome[j] = ch;
fscanf(arq,"%c", &ch);
j++;
}
pimpolho[i].nome[j] = '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];
}
';
j=0;
fscanf(arq,"%c", &ch);
while( ch != ',')
{
pimpolho[i].email[j] = ch;
fscanf(arq,"%c", &ch);
j++;
}
pimpolho[i].email[j] = 'typedef struct Dado{
// ^----
char CPF[12];
char nome[41];
char email[31];
int idade;
struct Dado* proximo; //<---
} Dado;
';
fscanf(arq, "%d", &pimpolho[i].idade);
fscanf(arq,"%c", &ch);
}
This actually corresponds to reading the 4 fields, the CPF, name, email and age, with the first 3 being the same. But the code was repeated. Not only is it harder to read, but it gets more extensive and propitious to go wrong when you need to change it so you have to change it in all places right. Whenever this happens, abstract the equal logic for a function and call it. Now look how much better it is:
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] = '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);
';
return 1;
}
In ordering it has the same problem because it repeats two ordering logics first by sorting by age, and then sorting those that have the same age:
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
}
This is entirely unnecessary because you can do both at once:
Dado *pimpolho = malloc(sizeof(Dado) * (num+1));
I will not dwell on this part, but keep in mind that the change made in the order is by copy. This can become quite inefficient if the amount of data is too large as it requires having to copy lots of bytes from side to side to make the switch. The way to solve is to use an array of pointers instead of an array with all the objects directly, but this implies changing the code almost all, and is probably overdone for the exercise in question.
There are other things that I can improve of course, but I focused on only 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 perfect sense, because in fact when using a list you do not need to know how many elements you have previously. This means that you do not have to go through the file twice, being the first to find the number of people that exist. You can simply read, allocate and link the pointers to each other. Now the code itself is more complicated because it involves allocations, pointer changes, memory release, etc ...
First implies changing the structure so that each person can have a pointer to the next one:
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] = '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] = '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];
}
}
';
}
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);
}
';
j=0;
fscanf(arq,"%c", &ch);
while( ch != ',')
{
pimpolho[i].nome[j] = ch;
fscanf(arq,"%c", &ch);
j++;
}
pimpolho[i].nome[j] = '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];
}
';
j=0;
fscanf(arq,"%c", &ch);
while( ch != ',')
{
pimpolho[i].email[j] = ch;
fscanf(arq,"%c", &ch);
j++;
}
pimpolho[i].email[j] = 'typedef struct Dado{
// ^----
char CPF[12];
char nome[41];
char email[31];
int idade;
struct Dado* proximo; //<---
} Dado;
';
fscanf(arq, "%d", &pimpolho[i].idade);
fscanf(arq,"%c", &ch);
}
Then you have to be able to see when you have reached the end of the file. The most straightforward is to interpret this in the reading of the first field CPF
. For this, the simplest is to change the function ler_string_arq
to return 0
when it reaches the end of the file:
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] = '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);
';
return 1;
}
Then while
reading is now also quite different:
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
}
For the writing would be quite identical changing the syntax largely:
%pre%
Note that I purposely omitted the sort order of people, because now with a linked list this is much more complicated and I do not want to stretch more than the answer is already quite big. Normally these sorting in lists are done with Merge Sort and they end up handy in the scenario where I I pointed to being sort with pointers and so are quite efficient.
In addition, the complexity between the two sorting algorithms is quite different because Merge Sort runs in O(nlogn)
whereas the bubble sort that runs in O(n²)
.