paulo1205
(usa Ubuntu)
Enviado em 30/01/2023 - 12:04h
ApprenticeX escreveu:
Boa Noite a todos!
Porque esse comportamento estranho, apenas porque troquei os parâmetros para compilar?
Não sei se você percebeu, mas seu programa está errado. Já mostro onde, mas já adianto que, na minha máquina, com a otimização ligada, a versão sem impressão de cada registro “contou” 4 registros, mas a versão com impressão gerou lixo na tela e “contou” 7 registros. Ambas erradas, portanto, e com diferenças em relação aos comentários que você colocou no código.
Se quiser, antes de continuar lendo, use essa informação e tente achar o erro no seu programa. Depois você continua lendo e confere se sua conclusão bate com o que eu vou dizer.
Fui orientado a compilar assim: gcc test.c -o test -O3 -Wall -pedantic -pedantic-errors -Werror
Pq dessa forma eu conheceria todos os erros que eu preciso consertar e ainda estaria otimizando o programa! Resumo, seria a melhor forma e a mais correta de compilar um programa!
Possivelmente fui eu quem o orientou nesse sentido (ou parecido com isso, pois eu raramente uso
-O3 , geralmente uso apenas
-O2 ). No entanto, eu mudei minha recomendação de alguns meses para cá, porque me dei conta que
-Wall , ao contrário do que o nome sugere, não produz todos os possíveis diagnósticos. Agora eu venho usando e recomendando
-Wextra .
Infelizmente, no entanto, mesmo
-Wextra no lugar de
-Wall não muda nada com relação ao fato de o compilador deixar de detectar o erro no seu programa.
Acontece que é estranho o comportamento do código abaixo, e isso bagunça tudo!
#include <stdio.h>
int main(void) {
struct {
char Col1[5],
Col2[5],
Col3[5];
} Database1[] = {{"reg1", "a", "b"},
{"reg2", "c", "d"},
{"reg3", "e", "f"},
{"reg4", "g", "h"},
{"reg5", "i", "j"}};
int QtdReg = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Porque conta 5 registros compilando assim: gcc test.c -o test
// E 4 registros compilando assim: gcc test.c -o test -O3 -Wall -pedantic -pedantic-errors -Werror
////////////////////////////////////////////////////////////////////////////////////////////////////
for( ; Database1[QtdReg].Col1[0]; QtdReg++);
A lógica aqui está errada pois você não indica, entre os elementos do
array Database1 , nenhum elemento que contenha um
byte nulo na primeira posição do campo
Col1 , e portanto não há como garantir que a condição de parada desse suposto contador da quantidade de elementos será atingida em algum momento.
Se o programa chegou a funcionar em tempo finito e sem produzir uma falha de segmentação, foi por AZAR (não por sorte, porque eu considero que algo que deixa um
bug importante sem diagnóstico claro é algo negativo, não positivo). O azar só não foi maior porque, com a otimização ligada, você conseguiu perceber que a contagem estava diferente do que você gostaria, e porque calhou de o laço de repetição parar antes do que você achava que deveria.
O que explica essa parada antes do ponto é o fato de que, ao acionar a otimização máxima (opção
-O3 ), você habilitou a otimização agressiva de laços de repetição, que leva o compilador a expandir as iterações do laço como se não houvesse laço nenhum, mas sim uma sequência linear de instruções que supostamente produziriam o mesmo efeito final.
Entretanto, como parte dessa otimização agressiva, o compilador assume que sua condição de parada será satisfeita em algum momento, e usa suas heurísticas de otimização tomando essa assunção como verdadeira. No seu caso, ela não o é, então a heurística de otimização falha fragorosamente, “contando” menos do que você gostaria.
printf("%d registros\n", QtdReg);
QtdReg = 0;
for( ; Database1[QtdReg].Col1[0] != '\0'; QtdReg++);
printf("%d Registros\n", QtdReg);
////////////////////////////////////////////////////////////////////////////////////////////////////
QtdReg = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Pq imprimindo na tela ele conta corretamente, independente da forma como compilo?
////////////////////////////////////////////////////////////////////////////////////////////////////
for( ; Database1[QtdReg].Col1[0]; puts(Database1[QtdReg].Col1), QtdReg++);
printf("%d Registros\n", QtdReg);
Por AZAR (de novo, hehehe...)!
A lógica de detecção de condição de parada continua errada para o conjunto de dados em questão, mas o fato de haver uma chamada de função envolvendo parte dos dados usados na avaliação da condição de parada foi suficiente,
na sua máquina e com certas condições de execução do momento em que você executou , para provocar a coincidência de parar no lugar que você gostaria.
No meu micro, com a minha versão de compilador e com meu SO e na manhã de hoje, o resultado final foi de 7 registros, sendo que a tentativa de imprimir o 6º e o 7º registros produziu caracteres espúrios no meu terminal.
QtdReg = 0;
for( ; Database1[QtdReg].Col1[0] != '\0'; puts(Database1[QtdReg].Col1), QtdReg++);
printf("%d Registros\n", QtdReg);
}
Existe explicação para isso?
Como se viu acima, trata-se de
bug no seu código. Em todos os laços de repetição, a condição de parada assume a presença de
bytes nulos para os quais não há qualquer provisão explícita na declaração e definição de
Database1 .
Que tenha funcionado sem a otimização ligada é outro exemplo de puro azar. Provavelmente o que aconteceu nesse caso é que o compilador deixou um espaço de
padding após o final do
array , e esse espaço foi casualmente preenchido com zeros, o que permitiu aos laços de repetição encontrar um
byte nulo onde você gostaria que ele estivesse. Com otimização agressiva ligada, que inclui otimização de espaço ocupado, no entanto, não houve
padding , e não havia
byte nulo na posição correspondente ao sexto elemento elemento do
array .
A forma de corrigir totalmente o programa sem mudar as lógicas de parada dos laços de repetição é incluir no
array um elemento que explicitamente caracterize o “fim dos dados válidos”. Por exemplo:
struct {
char Col1[5],
Col2[5],
Col3[5];
} Database1[] = {{"reg1", "a", "b"},
{"reg2", "c", "d"},
{"reg3", "e", "f"},
{"reg4", "g", "h"},
{"reg5", "i", "j"},
{"", "", ""} };
Fazendo desse modo funciona com otimização ligada ou não, imprimindo ou sem imprimir.
Entretanto, a verdade é que agora o
array tem seis elementos, em vez de cinco, e o sexto elemento funciona somente como marcador de fim.
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)