EOF dúvida relacionada ao Post respondido pelo Paulo [RESOLVIDO]

1. EOF dúvida relacionada ao Post respondido pelo Paulo [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 27/06/2020 - 17:28h

Post Referência: https://www.vivaolinux.com.br/topico/C-C++/leitura-em-c

Paulo, sempre que encontro posts que vc responde, dou uma atenção especial. Porque mesmo o post não estando relacionado ao que estou procurando ou mesmo a alguma dúvida minha, sempre possue muita informação importante! Existem muitos posts que estão salvos em meu Navegador que aos poucos estou estudando e analisando suas respostas!

Como sou aprendiz, vou deixando para o momento certo os posts ou respostas que ainda não posso compreender, para no momento certo absorver as respostas!

O Caso de sua resposta no Post acima mencionado me gerou uma dúvida! Não sei se é falta de atenção minha, ou algo que não percebi, porém é uma dúvida que me deixou preocupado, pq considero o problema meio que perigoso!

Você dá uma aula ao responder a pergunta, sobre EOF
Eu sei um pouco sobre EOF, uso ele em algumas situações. Eu uma vez li, em algum lugar, que sempre é melhor usar int para comparar com EOF para evitar problemas.

Entendi em sua explicação que o EOF pode ser perigoso e não confiável. Acredito que vc esteja falando do uso do EOF com variável char o que não seria o meu caso, pois entendi que deve-se usar int para este caso!

Entendi que sua explicação sobre o EOF e seus problemas, é toda baseada em usar char para fazer essa comparação!
MAS fiquei na dúvida se ainda existe problema com EOF usando int.

Se eu posso ou não confiar no EOF usando int e se não posso, existe alguma outra forma de eu saber que o arquivo chegou ao fim?
Não costumo ler arquivos linha por linha, sempre preferi ler caracter por caracter devido a forma como são formatados.

OBS Não relacionada a pergunta: Não estou gostando do C++ ele tem me chateado muito com uma burocracia enorme de regras que ainda considero desnecessárias, mas que estou me esforçando para compreender!
Visto que ainda considero horrível a burocracia do alfabeto! Para mim são apenas 10 números que poderiam ser considerado letras e tudo poderia ser somado e etc... baseado apenas nessa regra: É tudo letra. Se usar matemática reconhece automáticamente apenas 10 números e se não for matemática A+A = AA não entra na minha cabeça tanta burocracia para lidar com LETRAS e NÚMEROS.

E C++ levou essa burocracia para estratosfera! char, const char, string, assigned, unsigned e milhões de outras nomenclaturas... deve existir umas 300 mensagens de erro só pra dizer que é proibido converter const char em char! E para cada uma dessas nomenclaturas de tipos! É tudo letra. Transformaram a Letra A em 1 milhão de objetos diferentes que não se entendem e não são compatíveis entre si. Brincaram de matemática ao pegar a Tabela ASCII e multiplicar o que tem nela por 1 milhão só pra complicar!

Eu não sei criar uma Linguagem de programação, mas o dia que eu souber, TUDO será igual ao que é no papel. Letras, Números e TINTA para aparecer na Tela!
Eu teria o tamanho pela qtd de letra apenas isso. Sem burocracia. Qual o problema do usuário somar A+1= A1? Se ele quer uma soma use números! Errou na digitação? Problema do programador! Presta mais atenção! É assim no PAPEL. Se escreveu errado vai ter que consertar!


  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 30/06/2020 - 04:31h

Nick-us escreveu:

Post Referência: https://www.vivaolinux.com.br/topico/C-C++/leitura-em-c

Paulo, sempre que encontro posts que vc responde, dou uma atenção especial. Porque mesmo o post não estando relacionado ao que estou procurando ou mesmo a alguma dúvida minha, sempre possue muita informação importante! Existem muitos posts que estão salvos em meu Navegador que aos poucos estou estudando e analisando suas respostas!

Como sou aprendiz, vou deixando para o momento certo os posts ou respostas que ainda não posso compreender, para no momento certo absorver as respostas!

O Caso de sua resposta no Post acima mencionado me gerou uma dúvida! Não sei se é falta de atenção minha, ou algo que não percebi, porém é uma dúvida que me deixou preocupado, pq considero o problema meio que perigoso!

Você dá uma aula ao responder a pergunta, sobre EOF
Eu sei um pouco sobre EOF, uso ele em algumas situações. Eu uma vez li, em algum lugar, que sempre é melhor usar int para comparar com EOF para evitar problemas.


Não é apenas melhor. É necessário.

Entendi em sua explicação que o EOF pode ser perigoso e não confiável. Acredito que vc esteja falando do uso do EOF com variável char o que não seria o meu caso, pois entendi que deve-se usar int para este caso!


EOF não é perigoso, e é, sim, confiável. Perigoso e não-confiável é comparar um valor do tipo int, como EOF, com um valor do tipo char. Também é perigoso converter para char um valor do tipo int que pode estar fora da faixa representável por um char ou que possa ser representado de modo ambíguo ou dependente da implementação.

Entendi que sua explicação sobre o EOF e seus problemas, é toda baseada em usar char para fazer essa comparação!
MAS fiquei na dúvida se ainda existe problema com EOF usando int.


Você quer dizer uma variável do tipo int para guardar o caráter lido? Se sim, isso é perfeitamente seguro e sempre foi o jeito certo de trabalhar com funções de leitura caráter-a-caráter, desde os primórdios do C.

Se eu posso ou não confiar no EOF usando int e se não posso, existe alguma outra forma de eu saber que o arquivo chegou ao fim?


Você pode confiar em EOF, até porque seu valor é constante.

Existem outras formas saber se você atingiu o fim de arquivo, sim. Mas se você vai ler todo o arquivo, até o fim, a indicação de EOF retornado por uma função de leitura é a primeira coisa a ser testada.

Só que você tem de ter em mente que o valor EOF é devolvido também em casos de erro. Tais erros são raros, mas se você quiser ter certeza absoluta que a leitura foi interrompida por fim de arquivo, e não por erro, convém testar o arquivo para saber a causa. Para isso, existem em C as funções feof() e ferror(), e eventualmente clearerr(), especialmente em caso de erros transitórios, ou de chegar ao fim de um arquivo que pode crescer logo em seguida. Em C++, os equivalentes são std::ios::eof(), std::ios::fail() (para erros potencialmente recuperáveis) e std::ios::bad() (para erros não-recuperáveis), além de std::ios::clear() para reajustar as sinalizações de erro.

Não costumo ler arquivos linha por linha, sempre preferi ler caracter por caracter devido a forma como são formatados.


A menos que você tenha uma boa razão para ler um caráter de cada vez, se o arquivo for de texto, geralmente é mais fácil ler linha a linha.

Se o arquivo for binário, pode ser interessante ler em blocos de bytes (std::istream::read()).

OBS Não relacionada a pergunta: Não estou gostando do C++ ele tem me chateado muito com uma burocracia enorme de regras que ainda considero desnecessárias, mas que estou me esforçando para compreender!
Visto que ainda considero horrível a burocracia do alfabeto! Para mim são apenas 10 números que poderiam ser considerado letras e tudo poderia ser somado e etc... baseado apenas nessa regra: É tudo letra. Se usar matemática reconhece automáticamente apenas 10 números e se não for matemática A+A = AA não entra na minha cabeça tanta burocracia para lidar com LETRAS e NÚMEROS.


Não sei do que você está falando. Pode mostrar o trecho de código que exemplifica o problema?

Pelo que vi em outra mensagem sua, eu receio que você possa confundir, em algumas situações, char (tipo usado para guardar apenas um caráter) com char * ou const char *, que são tipos de ponteiros usados para indicar em que posição de memória começa uma string. Cuidado, portanto, para não misturar alhos com bugalhos.

E C++ levou essa burocracia para estratosfera! char, const char, string, assigned, unsigned e milhões de outras nomenclaturas...


Não conheço “assigned”, mas, pelo contexto, acho que você quis dizer simplesmente signed.

Nada disso é invenção do C++. Se você acaha burocrático, é justamente para manter compatibilidade com C.

deve existir umas 300 mensagens de erro só pra dizer que é proibido converter const char em char!


Não é proibido converter const char em char.

Você está se referindo a ponteiros? Esses, sim, não são permitidos diretamente, porque isso pode produzir uma violação de proteção de acesso do objeto apontado. O ponteiro foi declarado como constante por um motivo; se você quer violar essa restrição, é bom saber muito bem o que está fazendo, e tem de indicar isso claramente no código através da palavra-chave const_cast.

E note que isso não se aplica apenas a ponteiros para caracteres constantes, mas a ponteiros para qualquer tipo de objeto constante (const int *, const double *const (ponteiro constante para valor double constante), char *const * (ponteiro (não-constante) para ponteiro constante para caráter (não-constante)) etc.).

E para cada uma dessas nomenclaturas de tipos! É tudo letra. Transformaram a Letra A em 1 milhão de objetos diferentes que não se entendem e não são compatíveis entre si. Brincaram de matemática ao pegar a Tabela ASCII e multiplicar o que tem nela por 1 milhão só pra complicar!


Não é tudo letra. Muito pelo contrário: tudo é número.

Lembre-se do sentido original de “computador”. É mero acidente histórico nos referirmos a máquinas que fazem uma quantidade relativamente grande de contas com pouca interação humana como “computadores” e a máquinas que fazem uma conta de cada vez, sob estrito controle humano, como “calculadoras” — até porque existem híbridos como calculadoras programáveis e computadores mecânicos.

Acho um tanto engraçadas suas reclamações porque, além dos exageros (“300 mensagens de erro”, “1 milhão de objetos”), elas acabam revelando um tanto de desconhecimento a respeito da máquina que você está usando, da ferramenta que está empregando e da história de ambas.

O tipo char, apesar do nome que tem, não se destina a guardar caracteres. Ele é um tipo numérico: o menor tipo numérico inteiro que a máquina for capaz de endereçar (desde que esse tipo numérico tenha pelo menos oito bits). O nome “char” foi aplicado a esse tipo de dados porque, na época em que a linguagem foi criada (na verdade, adaptada a partir da linguagem B), esse tipo “inteiro de oito bits de largura” era suficiente para representar qualquer um dos 128 caracteres do ASCII (7 bits), que a máquina que o hospedava usava, e ainda podia guardar também os códigos numéricos usados nos cartões perfurados de mainframes da IBM (EBCDIC, cujos caracteres são distribuídos de um jeito tal em o 8º bit tem de ser usado).

Aliás, você sabia que não existe, em C, um jeito direto (i.e. que não envolva conversão explícita de tipo) de produzir uma constante literal cujo tipo seja char? Quando você escreve algo como 'a' ou '\n' (usando apóstrofos), os valores correspondentes são do tipo int, não do tipo char.

Historicamente falando, quando o C foi criado, no UNIX do PDP-11, char era com sinal (ou seja: seus valores iam de -128 até 127), porque essa era a forma mais natural de trabalhar nesse equipamento, especialmente por causa de um tipo de operação, que é a operação de converter de byte (ou do menor inteiro representável) para o tipo inteiro natural da máquina (com 16 bits ou mais). No caso do PDP-11, essa conversão se dava pela extensão do bit mais significativo (que é o bit de sinal) do byte original para todos os outros bits do valor inteiro. Isso fazia pleno sentido no PDP-11 por dois motivos: porque era a forma mais eficiente de fazer naquela arquitetura (o processador fazia isso naturalmente), e porque isso é consistente com a representação de números negativos usando notação de complemento a dois (por exemplo: o valor -15 pode ser representado em um byte binário como 11110001; ao ser convertido para inteiros de 16 bits, o mesmo valor -15 é representado como 11111111 11110001; ou seja, o bit mais à esquerda na representação como char era 1, e foi copiado todos os bits a mais que um inteiro de 16 bits possui).

Quando o UNIX e o C foram portados para outras máquinas, os implementadores notaram que algumas delas tinham instruções de conversão de byte para tipos inteiros maiores (de 16 ou 32 bits) que preenchiam os bytes mais significativos com zeros, em vez de estender o bit mais significativo do byte para a esquerda. Em máquinas assim, se o byte fosse com sinal, você tinha duas opções: usar uma operação custosa a cada conversão de byte para inteiro nativo (e vice-versa), ou usar uma conversão mais rápida, mas que sempre produziria inteiros positivos, mesmo que o byte original fosse negativo. O que o pessoal do C decidiu foi que, em máquinas assim, o melhor seria usar a operação mais eficiente, e considerar todos os bytes como sempre positivos.

Obviamente, esse tipo de coisa acabou produzindo alguma confusão, com programas que de comportavam de modos diferentes quando compilados em máquinas diferentes. Para acabar com a confusão o padrão da linguagem decidiu criar três tipos de dados distintos: signed char, unsigned char e char. Os dois primeiros têm comportamentos muito bem definidos em qualquer máquina, a despeito do custo computacional que possam causar. O terceiro tem normalmente o comportamento que for mais eficiente para cada implementação.

Veja o seguinte trecho de programa.
// Arquivo “x.c“.

char fc(char c){ return ~c; }
unsigned char fuc(unsigned char c){ return ~c; }
signed char fsc(signed char c){ return ~c; }

int g(int i){
char c=i;
unsigned char uc=i;
signed char sc=i;
return fc(c)+fuc(uc)+fsc(sc)+fuc(c)+fsc(c);
}

int h(void){ return g('\300'); } // -64 ou 192. Note que isso é um int!

// As funções abaixo são declaradas mas não definidas, porque estou mais
// interessado no que acontece dentro de j().
// A função fr() é só para impedir o compilador de otimizar demais o código
// envolvendo a variável r, dentro de j().
char fpc(const char *pc);
void fr(const int *);

int j(void){
char c='c';
unsigned char uc='u';
signed char sc='s';
int r=0;
fr(&r);
r+=c;
fr(&r);
r+=uc;
fr(&r);
r+=sc;
r+=fpc(&c); // OK: tipo do ponteiro perfeitamente compatível.
r+=fpc((char *)&uc); // Se tirar os casts dos ponteiros, dá erro, mesmo que uma
r+=fpc((char *)&sc); // das variáveis tenha a mesma representação interna que c.
return r;
}


Se você o compilar de uma forma a gerar o código em Assembly correspondente (por exemplo, usando o comando “gcc -Wall -Werror -pedantic-errors -S -fverbose-asm -O1 -fno-inline x.c”, que vai produzir o arquivo de saída x.s) e examinar tal código, vai ver algumas coisas interessantes:

  • Em todas as operações aritméticas, que misturam signed char com unsigned char, todos os operandos são convertidos para int, mesmo que o destino do resultado só tenha um byte de comprimento (isso é uma regra do C, e é um bom motivo para preferir tipos de dados que produzam conversões eficientes, sempre que isso for possível).

  • Ao converter de byte para int, nossos PCs com Intel ou AMD empregam a instrução do Assembly movsbl (na notação da AT&T, que normalmente o Linux utiliza; se for com a notação da Intel, o nome da instrução é MOVSX), que significa “movimentar (copiar) o valor de um byte (“b”), estendendo para 32 bits (“l” ou “X”) por meio da cópia para a esquerda do bit de sinal (“s”). Tal instrução é usada tanto com char não qualificado quanto com signed char, o que significa que eles têm a mesma representação interna e, portanto, produzem resultados semelhantes, mas não os torna equivalentes, pois esse comportamento pode mudar se o programa for levado para outra máquina, ou mesmo se, na mesma máquina, o compilador for invocado com uma configuração diferente.

  • Ao converter de unsigned char para int, a instrução do Assembly empregada é movzbl (ou MOVZX), que indica que os bits à esquerda devem ser preenchidos com zeros. Até algumas gerações de processadores atrás, essa instrução era ligeiramente mais lenta do que a outra (fonte: https://gmplib.org/~tege/x86-timing.pdf).

  • Converter de inteiro para qualquer tipo de byte, nos nossos PCs, é feito simplesmente através do truncamento dos bits à esquerda. Contudo existem máquinas em que isso pode não ser verdade (por exemplo, máquinas que usem representações inteiras do tipo sinal+magnitude ou deslocamento binário)


Você se queixou do C++, mas na verdade o C++ não é responsável por nenhuma das esquisitices acima. Elas são parte da linguagem C, e o C++ herdou algumas delas tão-somente para manter compatibilidade com C.

Por outro lado, o C++ quebrou algumas das esquisitices do C. Por exemplo, em C++, uma constante literal de caráter, tal como 'P', '\n' ou '\123' tem tipo char, em vez de int. Isso é necessário para que funções sobrecarregadas com argumentos de tipos diferentes possam funcionar adequadamente.

Outra melhoria do C++ foi implementar a classe std::string, que ajuda a diminuir as dificuldades de manipular strings que o C tem.

Eu não sei criar uma Linguagem de programação, mas o dia que eu souber, TUDO será igual ao que é no papel. Letras, Números e TINTA para aparecer na Tela!
Eu teria o tamanho pela qtd de letra apenas isso. Sem burocracia. Qual o problema do usuário somar A+1= A1? Se ele quer uma soma use números! Errou na digitação? Problema do programador! Presta mais atenção! É assim no PAPEL. Se escreveu errado vai ter que consertar!


Ué, e não é assim no C? Se ele errar na sintaxe, o programa nem compila. Se ele errar na semântica, o programa faz o melhor que pode, seguindo literalmente o que tiver sido dito.



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

3. Re: EOF dúvida relacionada ao Post respondido pelo Paulo [RESOLVIDO]

Nick Us
Nick-us

(usa Slackware)

Enviado em 30/06/2020 - 11:51h

paulo1205 escreveu: Você pode confiar em EOF, até porque seu valor é constante.

Obrigado!

existem em C as funções feof() e ferror(), e eventualmente clearerr(), especialmente em caso de erros transitórios, ou de chegar ao fim de um arquivo que pode crescer logo em seguida. Em C++, os equivalentes são std::ios::eof(), std::ios::fail() (para erros potencialmente recuperáveis) e std::ios::bad() (para erros não-recuperáveis), além de std::ios::clear() para reajustar as sinalizações de erro.

Eu já tinha me deparado com essas funções, apenas não dei a devida atenção a elas, mas no momento estou mais preparado, e vou estudá-las agora!

Se o arquivo for binário, pode ser interessante ler em blocos de bytes (std::istream::read()).

Essa parte eu ainda não estudei direito, estou deixando para um momento mais propício, qdo pensei em usar, com o comando fwrite, fiquei preocupado com uma resposta que vc deu, então achei melhor continuar usando texto, até eu ter uma forma melhor de arquivar meu aprendizado, ou o momento certo para me dedicar a isso!

Não é proibido converter const char em char.

Você está se referindo a ponteiros? Esses, sim, não são permitidos diretamente, porque isso pode produzir uma violação de proteção de acesso do objeto apontado. O ponteiro foi declarado como constante por um motivo; se você quer violar essa restrição, é bom saber muito bem o que está fazendo, e tem de indicar isso claramente no código através da palavra-chave const_cast.

E note que isso não se aplica apenas a ponteiros para caracteres constantes, mas a ponteiros para qualquer tipo de objeto constante (const int *, const double *const (ponteiro constante para valor double constante), char *const * (ponteiro (não-constante) para ponteiro constante para caráter (não-constante)) etc.).

Vejo que ainda tem um longo caminho pela frente para aprender!


O tipo char, apesar do nome que tem, não se destina a guardar caracteres. Ele é um tipo numérico: o menor tipo numérico inteiro que a máquina for capaz de endereçar (desde que esse tipo numérico tenha pelo menos oito bits). O nome “char” foi aplicado a esse tipo de dados porque, na época em que a linguagem foi criada (na verdade, adaptada a partir da linguagem B), esse tipo “inteiro de oito bits de largura” era suficiente para representar qualquer um dos 128 caracteres do ASCII (7 bits), que a máquina que o hospedava usava, e ainda podia guardar também os códigos numéricos usados nos cartões perfurados de mainframes da IBM (EBCDIC, cujos caracteres são distribuídos de um jeito tal em o 8º bit tem de ser usado).

Aliás, você sabia que não existe, em C, um jeito direto (i.e. que não envolva conversão explícita de tipo) de produzir uma constante literal cujo tipo seja char? Quando você escreve algo como 'a' ou '\n' (usando apóstrofos), os valores correspondentes são do tipo int, não do tipo char.

Historicamente falando, quando o C foi criado, no UNIX do PDP-11, char era com sinal (ou seja: seus valores iam de -128 até 127), porque essa era a forma mais natural de trabalhar nesse equipamento, especialmente por causa de um tipo de operação, que é a operação de converter de byte (ou do menor inteiro representável) para o tipo inteiro natural da máquina (com 16 bits ou mais). No caso do PDP-11, essa conversão se dava pela extensão do bit mais significativo (que é o bit de sinal) do byte original para todos os outros bits do valor inteiro. Isso fazia pleno sentido no PDP-11 por dois motivos: porque era a forma mais eficiente de fazer naquela arquitetura (o processador fazia isso naturalmente), e porque isso é consistente com a representação de números negativos usando notação de complemento a dois (por exemplo: o valor -15 pode ser representado em um byte binário como 11110001; ao ser convertido para inteiros de 16 bits, o mesmo valor -15 é representado como 11111111 11110001; ou seja, o bit mais à esquerda na representação como char era 1, e foi copiado todos os bits a mais que um inteiro de 16 bits possui).

Quando o UNIX e o C foram portados para outras máquinas, os implementadores notaram que algumas delas tinham instruções de conversão de byte para tipos inteiros maiores (de 16 ou 32 bits) que preenchiam os bytes mais significativos com zeros, em vez de estender o bit mais significativo do byte para a esquerda. Em máquinas assim, se o byte fosse com sinal, você tinha duas opções: usar uma operação custosa a cada conversão de byte para inteiro nativo (e vice-versa), ou usar uma conversão mais rápida, mas que sempre produziria inteiros positivos, mesmo que o byte original fosse negativo. O que o pessoal do C decidiu foi que, em máquinas assim, o melhor seria usar a operação mais eficiente, e considerar todos os bytes como sempre positivos.

Obviamente, esse tipo de coisa acabou produzindo alguma confusão, com programas que de comportavam de modos diferentes quando compilados em máquinas diferentes. Para acabar com a confusão o padrão da linguagem decidiu criar três tipos de dados distintos: signed char, unsigned char e char. Os dois primeiros têm comportamentos muito bem definidos em qualquer máquina, a despeito do custo computacional que possam causar. O terceiro tem normalmente o comportamento que for mais eficiente para cada implementação.

Adorei saber disso! Isso desperta ainda mais a minha curiosidade em conhecer bem sobre isso tudo!


Veja o seguinte trecho de programa.
// Arquivo “x.c“. 

Se você o compilar de uma forma a gerar o código em Assembly correspondente (por exemplo, usando o comando “gcc -Wall -Werror -pedantic-errors -S -fverbose-asm -O1 -fno-inline x.c”, que vai produzir o arquivo de saída x.s) e examinar tal código, vai ver algumas coisas interessantes:

Mas é claro que eu não perderia por nada em tentar compilar o código acima! E eu nem sabia da existência dessa opção! E achei o resultado fantástico porque ele parece explicar o funcionamento de cada linha em assembly pelo que entendi, eu ainda não sei assembly, mas parece que é possível converter um simples Hello World para assembly dessa forma!

Uma dúvida: Tem como eu tranformar um código escrito em C em código assembly?
Pq foi o que esse seu comando pareceu fazer, e o que achei ainda melhor, pareceu fazer explicando! Olha que trecho fantástico de um puts dentro do main. Não parece completamente Assembly mas sei que Assembly tem MOV e coisas assim!
	.text
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "ok"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
subq $8, %rsp #,
.cfi_def_cfa_offset 16
# Test.c:4: puts("ok");
movl $.LC0, %edi #,
call puts #
# Test.c:5: }
movl $0, %eax #,
addq $8, %rsp #,
.cfi_def_cfa_offset 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 9.2.0"
.section .note.GNU-stack,"",@progbits


  • Em todas as operações aritméticas, que misturam signed char com unsigned char, todos os operandos são convertidos para int, mesmo que o destino do resultado só tenha um byte de comprimento (isso é uma regra do C, e é um bom motivo para preferir tipos de dados que produzam conversões eficientes, sempre que isso for possível).

  • Ao converter de byte para int, nossos PCs com Intel ou AMD empregam a instrução do Assembly movsbl (na notação da AT&T, que normalmente o Linux utiliza; se for com a notação da Intel, o nome da instrução é MOVSX), que significa “movimentar (copiar) o valor de um byte (“b”), estendendo para 32 bits (“l” ou “X”) por meio da cópia para a esquerda do bit de sinal (“s”). Tal instrução é usada tanto com char não qualificado quanto com signed char, o que significa que eles têm a mesma representação interna e, portanto, produzem resultados semelhantes, mas não os torna equivalentes, pois esse comportamento pode mudar se o programa for levado para outra máquina, ou mesmo se, na mesma máquina, o compilador for invocado com uma configuração diferente.

  • Ao converter de unsigned char para int, a instrução do Assembly empregada é movzbl (ou MOVZX), que indica que os bits à esquerda devem ser preenchidos com zeros. Até algumas gerações de processadores atrás, essa instrução era ligeiramente mais lenta do que a outra (fonte: https://gmplib.org/~tege/x86-timing.pdf).

  • Converter de inteiro para qualquer tipo de byte, nos nossos PCs, é feito simplesmente através do truncamento dos bits à esquerda. Contudo existem máquinas em que isso pode não ser verdade (por exemplo, máquinas que usem representações inteiras do tipo sinal+magnitude ou deslocamento binário)

Você se queixou do C++, mas na verdade o C++ não é responsável por nenhuma das esquisitices acima. Elas são parte da linguagem C, e o C++ herdou algumas delas tão-somente para manter compatibilidade com C.

Por outro lado, o C++ quebrou algumas das esquisitices do C. Por exemplo, em C++, uma constante literal de caráter, tal como 'P', '\n' ou '\123' tem tipo char, em vez de int. Isso é necessário para que funções sobrecarregadas com argumentos de tipos diferentes possam funcionar adequadamente.

Outra melhoria do C++ foi implementar a classe std::string, que ajuda a diminuir as dificuldades de manipular strings que o C tem.

Bom saber disso, me deixou mais calmo com C++ e parece que tem ainda um longo caminho pela frente de aprendizado!







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts