Reclamação que não entendi do compilador [RESOLVIDO]

1. Reclamação que não entendi do compilador [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 03:16h

Bom dia a todos!
O código abaixo deveria funcionar, mas porque o compilador reclama de não-nulo?
Se alguém puder me explicar o que está acontecendo...
#include <stdio.h>

int main(void) {
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wb");

if(!Destiny) {
FILE *Source = fopen("/etc/rc.d/rc.inet1.conf", "rb");

for(int Char; (Char = getc(Source)) != EOF; )
fputc(Char, Destiny);

fclose(Source );
fclose(Destiny);
} else {
fclose(Destiny);
puts("O Backup já existe!");
}
}

bash-5.2$ gcc test.c -o test -O3 -Wall -pedantic -pedantic-errors -Werror
test.c: In function 'main':
test.c:11:10: error: argument 2 null where non-null expected [-Werror=nonnull]
11 | fputc(Char, Destiny);
| ^~~~~~~~~~~~~~~~~~~~
In file included from test.c:2:
/usr/include/stdio.h:549:12: note: in a call to function 'fputc' declared 'nonnull'
549 | extern int fputc (int __c, FILE *__stream);
| ^~~~~
cc1: all warnings being treated as errors



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/01/2023 - 04:19h

Relendo com calma o código, vi que você supõe que se não conseguir abrir o arquivo destino, é porque ele já existe. Essa assunção é falsa quando você usa o modo wb, que sempre cria um arquivo com tamanho zero, mesmo que já houvesse um anterior.

O Linux e alguns outros sistemas UNIX-like permitem colocar um modificador x na especificação do modo de abertura, provocando o efeito de causar um erro caso o arquivo já exista. Esse erro específico (entre outros erros possíveis) pode ser discriminado se, ao examinar a variável global errno, o valor nela contido foi igual a EEXIST.
FILE *fp=fopen("arquivo", "wbx");
if(!fp && errno==EEXIST){
/* Arquivo já existe. */
}


Se o seu sistema não suportar o flag x, o jeito de saber se o arquivo já existe é tentar abri-lo para leitura. Se funcionar, é porque existe.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)

3. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Alexa Bolada
alexabolada

(usa Debian)

Enviado em 12/01/2023 - 03:16h

Isso parece ser um erro conhecido, onde o compilador está reclamando do segundo argumento do comando fputs() em que deve ser passado um ponteiro válido.

As vezes as funções padrão do C exigem um argumento não nulo (nonnull argument), como é o caso do argumento Stream do comando fputs.

Sempre que uma função exigir um argumento não nulo, como no seu caso, é importante verificar se a variável (no seu caso, "Destiny") não é nula antes de passar para a chamada da função. Uma verificação adicional é usar assert() para garantir que seu argumento não é nulo.

Você pode resolver esse problema, antes de chamar a função fputs($Char, $Destiny), adicione uma assert(Destiny) para garantir que o argumento Stream é válido. Veja como deve ficar abaixo:

#include <stdio.h>

int main(void) {
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wb");

if(!Destiny) {
FILE *Source = fopen("/etc/rc.d/rc.inet1.conf", "rb");

assert(Destiny);
for(int Char; (Char = getc(Source)) != EOF; )
fputc(Char, Destiny);

fclose(Source );
fclose(Destiny);
} else {
fclose(Destiny);
puts("O Backup já existe!");
}
}



4. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 03:36h

alexabolada escreveu: o compilador está reclamando do segundo argumento do comando fputs() em que deve ser passado um ponteiro válido.
Uma verificação adicional é usar assert() para garantir que seu argumento não é nulo.

1) Mas meu ponteiro é válido, ele não é nulo!
2) Porque preciso fazer uma verificação adicional desnecessária? Só porque usei um if para verificar um arquivo?
// COMO assim ele não entende que isso não é nulo? apenas porque está dentro de um if?
// Porque se o comando estivesse fora do if ele entende! Então qual a diferença?
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wb");

Não entendi essa regra! É como se eu tivesse que colocar comandos repetidos para serem validados!
Imagina!
// Imprima Bom dia!
puts("Bom dia");

// É pra imprimir Bom dia, entendeu compilador?
puts("Bom dia");

// Já imprimiu Bom dia compilador???
puts("Bom dia");

Qual é a diferença se eu tiro o if que nada tem a ver com o fputc do compilador não reclamar?
Não gostei nada de ter que adicionar uma biblioteca a mais desnecessária! <assert.h>
Não gostei nada de ter que adicionar um comando inútil desnecessário! assert
Qual o porque disso? Gostaria de entender!
#include <stdio.h>

int main() {
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wb");
FILE *Source = fopen("/etc/rc.d/rc.inet1.conf" , "rb");

for(int Char; (Char = getc(Source)) != EOF; )
fputc(Char, Destiny);

fclose(Source );
fclose(Destiny);
}



5. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Alexa Bolada
alexabolada

(usa Debian)

Enviado em 12/01/2023 - 03:36h

O compilador está alertando o uso do segundo argumento do comando fputs() porque ele é esperado para ser um ponteiro válido (não nulo). Em geral, quando usamos comandos do sistema operacional para realizar algumas operações, é recomendado que antes de executá-los, verifiquemos se os dados necessários são válidos e existem os elementos necessários para a operação. A biblioteca assert.h contém o comando "assert", que verifica se o argumento passado é não nulo. Assim que confirmarmos que o argumento passado é válido, o compilador não vai apontar o erro.


6. Re: Reclamação que não entendi do compilador

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/01/2023 - 03:43h

Veja bem o if que está cercando seu laço de repetição. Ele só executa se o ponteiro Destiny for nulo.

Tire o sinal de negação (“!”) de antes do nome do ponteiro.


EM TEMPO: “Destiny” significa destino, mas com muito mais com o sentido de “sina”, aquela coisa que está fadada a acontecer (e.g. “quis o destino que ele nascesse cego”). Um sinônimo de “destiny” em inglês seria “fate”. Se quiser usar o nome por extenso em inglês, você possivelmente deveria dizer “destination”.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


7. Re: Reclamação que não entendi do compilador

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 04:30h

alexabolada escreveu: O compilador está alertando o uso do segundo argumento do comando fputs() porque ele é esperado para ser um ponteiro válido (não nulo). Em geral, quando usamos comandos do sistema operacional para realizar algumas operações, é recomendado que antes de executá-los, verifiquemos se os dados necessários são válidos e existem os elementos necessários para a operação. A biblioteca assert.h contém o comando "assert", que verifica se o argumento passado é não nulo. Assim que confirmarmos que o argumento passado é válido, o compilador não vai apontar o erro.

Considero isso um BUG do compilador, ou seja, o compilador não está verificando o que foi escrito no código! Ou pior, está esqueçendo o que já foi declarado!
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wb"); 

O ponteiro Destiny ao ser declarado, recebe o endereço de memória para o qual ele está apontando. E não é um endereço NULO.
Como assim o compilador não sabe disso? Qual foi a parte que o compilador não entendeu na declaração?

É extremamente inútil, desnecessário, precisar usar assert para fazer o que já foi feito!
Imagina? João lava o carro, mas preciso pagar para Pedro verificar se João lavou o carro, Então, preciso pagar ao Marcelo para verificar se Pedro verificou se João lavou o carro!
Quem inventou uma coisa dessa? É completamente ilógico ter que fazer isso!
Se alguém souber responder, qual a versão do gcc que inventou esse problema? Eu estou usando a v.12.2.0 e é a primeira vez que vejo algo tão inútil!


8. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Alexa Bolada
alexabolada

(usa Debian)

Enviado em 12/01/2023 - 04:30h

{"id":"cmpl-6XmL18UspxsirECD3txZMFGvZLgDb","object":"text_completion","created":1673508655,"model":"text-davinci-003","choices":[{"text":"","index":0,"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":1021,"total_tokens":1021}}



9. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 04:42h

paulo1205 escreveu:
Veja bem o if que está cercando seu laço de repetição. Ele só executa se o ponteiro Destiny for nulo.
Gostei de saber disso!

EM TEMPO: “Destiny” significa destino, mas com muito mais com o sentido de “sina”, aquela coisa que está fadada a acontecer (e.g. “quis o destino que ele nascesse cego”). Um sinônimo de “destiny” em inglês seria “fate”. Se quiser usar o nome por extenso em inglês, você possivelmente deveria dizer “destination”.
Obrigado pela explicação! Estou focando também em melhorar meu inglês e isso é importante!

Estranhamente suas respostas só apareceram para mim agora! E sua resposta foi dada antes de outras perguntas abaixo! E sempre ao escrever eu atualizo a página antes, mas não chegaram para mim!



10. Re: Reclamação que não entendi do compilador

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 04:47h

paulo1205 escreveu: Relendo com calma o código, vi que você supõe que se não conseguir abrir o arquivo destino, é porque ele já existe. Essa assunção é falsa quando você usa o modo wb, que sempre cria um arquivo com tamanho zero, mesmo que já houvesse um anterior.
Obrigado, isso meio que passou desapercebido, visto que a intenção não é danificar o arquivo existente!

O Linux e alguns outros sistemas UNIX-like permitem colocar um modificador x na especificação do modo de abertura, provocando o efeito de causar um erro caso o arquivo já exista. Esse erro específico (entre outros erros possíveis) pode ser discriminado se, ao examinar a variável global errno, o valor nela contido foi igual a EEXIST.
FILE *fp=fopen("arquivo", "wbx");
if(!fp && errno==EEXIST){
/* Arquivo já existe. */
}
Obrigado por mais essa informação também, porque anotarei como alternativas, mas a idéia não é usar uma biblioteca a mais! E esse comando funcionou também!

A resposta do Paulo acima me ajuda a conhecer mais da linguagem C.

Apenas acrescentando para que outros possam entender! Essa solução NESTA PERGUNTA, também não resolve o problema, pois a pergunta começa errada desde o início! Deixei um exemplo abaixo pra mostrar que por estar errada a pergunta, qualquer solução será problemática!

#include <stdio.h>
#include <errno.h>

int main() {
FILE *Destiny = fopen("/etc/rc.d/rc.inet1.conf_Original", "wbx");

if(!Destiny && errno == EEXIST) {
puts("O Backup já existe!");
} else {

// Segmentation fault ocorre porque Destiny no comando acima não foi aberto
// logo fputc não irá funcionar e não posso fechar um arquivo que não foi aberto!
FILE *Source = fopen("/etc/rc.d/rc.inet1.conf", "rb");

for(int Char; (Char = getc(Source)) != EOF; )
fputc(Char, Destiny); // Segmentation fault

fclose(Source );
fclose(Destiny); // Segmentation fault
}
}



11. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 05:16h

paulo1205 escreveu: você supõe que se não conseguir abrir o arquivo destino, é porque ele já existe. Essa assunção é falsa quando você usa o modo wb, que sempre cria um arquivo com tamanho zero, mesmo que já houvesse um anterior.
Essa foi a resposta mais importante! Porque preciso modificar isso!

A idéia como sempre é ter o código menor possível, menos linhas, menos tamanho, menos bibliotecas.

Agora terei que repensar como posso fazer novamente, visto que prefiriria já ao abrir o arquivo ter a opção de gravá-lo se houvesse necessidade, o que não existe nas opções de abrir um arquivo! Neste momento a única idéia que tenho é abrí-lo 2 vezes, uma pra checar se existe e outra pra gravá-lo.

Ou como vc informou usar wbx que impede fopen de abrir o arquivo, ou seja, faz tudo o que eu preciso e quero, mas que pede uma biblioteca a mais, que claro se já existir no programa não é problema. O outro ponto é se o programa rodará apenas no Linux, neste caso sim, mas em situações futuras em outros casos também tenho que refletir.

Terei que pensar que rumo decidirei tomar!


12. Re: Reclamação que não entendi do compilador [RESOLVIDO]

Apprentice X
ApprenticeX

(usa FreeBSD)

Enviado em 12/01/2023 - 10:30h

paulo1205 escreveu:Veja bem o if que está cercando seu laço de repetição. Ele só executa se o ponteiro Destiny for nulo. Tire o sinal de negação (“!”) de antes do nome do ponteiro
OBS a solução desse problema já foi descartada, visto que não posso usar o w. Apenas querendo entender a lógica do compilador!

Sim, sem o ! é executado, porém tirando o ! tenho que colocar as instruções no else do if, e nessa situação o problema persiste!
Abaixo um pequeno exemplo demonstrativo. Ou seja, não posso usar o if nem o else pra um caso como esse!
#include <stdio.h>

int main(void) {
FILE *Source = fopen("test.c", "r");
FILE *Destiny = fopen("test_11012023.c", "w");

if(Destiny)
puts("Destiny existe! Porque foi criado por w");

else {
puts("Só compilo se vc comentar o fputc abaixo!");
for(int Char; (Char = getc(Source)) != EOF; )
fputc(Char, Destiny);
}

fclose(Source );
fclose(Destiny);
}




01 02



Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts