
		paulo1205
		
		(usa Ubuntu)
		
		Enviado em 26/03/2018 - 17:41h 
		hrcerq escreveu:
Primeiramente, vamos resolver a terminologia [...]
Não sei se você conhece o conceito de ponteiros, mas esse conceito é essencial para entender o problema: um ponteiro é uma variável que aponta para um endereço de memória onde estará (ou espera-se que esteja) guardado algum valor que você vai usar. 
Como o objetivo é esclarecer a terminologia, permita-me alguns pequenos apartes.
Um ponteiro é um 
valor que indica um endereço de memória (e que também, durante a compilação, pode trazer informação sobre o tipo de dado que pode estar guardado nesse endereço).  Esse valor de endereço pode existir independentemente de se existe ou não uma variável que o armazene.
Dizer que uma variável “é” um ponteiro é comum, mas é impreciso — tão impreciso, na verdade, quanto dizer que uma variável “é” um inteiro, ou um 
double ou qualquer outro tipo de dado.  Em todos esses casos, o que se quer realmente dizer é que 
os valores que tais variáveis permitem guardar são do tipo 
X ou 
Y.
Quando você digita &nome[0] está se referindo ao endereço de memória da primeira posição do vetor. Porém, ocorre a expressão "nome" significa exatamente a mesma coisa que "&nome[0]", isto é, um ponteiro para a primeira posição do vetor. 
Na verdade, depende do contexto.
O que você disse é quase sempre verdade, mas há dois casos em que as duas expressões são diferentes: quando o nome do 
array é usado como operando dos operadores 
sizeof ou 
&.  Em todas as demais expressões, o nome do 
array produz, por decaimento, um valor que funciona como ponteiro para seu primeiro elemento.
E é exatamente isso que a função scanf está esperando, um ponteiro para a primeira posição da string (já que o primeiro parâmetro é "%s" e não "%c"). 
Eu concordo com você, mas acho importante deixar claro que é do programador o ônus de garantir que o ponteiro para caráter se refere ao primeiro elemento de um 
array, em vez de a um único caráter.  A função 
scanf() — e, aliás, qualquer função em C — não tem como qualificar o dado original ao qual um ponteiro se refere, porque toda passagem de argumentos funciona com cópia de valores.  De posse apenas do valor, e não da expressão de onde ele saiu, não há meios de desfazer um possível decaimento de 
array para ponteiro.
Porém, se você coloca o segundo parâmetro como "&nome", então está informando um ponteiro para outro ponteiro. Por isso a escrita não ocorre como esperado. 
Correto!
Contudo, nem sempre esse erro aparece facilmente pois, embora os tipos de dados sejam distintos (
nome decai para “ponteiro para 
char”; 
&nome é calculado (não decai!) e é do tipo “ponteiro para 
array com 30 elementos do tipo 
char”), os valores numéricos dos dois ponteiros costuma ser o mesmo, ou seja, o endereço bruto, desconsiderando os tipos de cada um, é idêntico para ambos.  Além disso, funções com número variável de argumentos, como 
printf() e 
scanf(), têm regras que tornam o diagnóstico desse tipo de erro ainda mais complicado.
Mesmo assim, por causa da prevalência desse tipo de erro em operações comuns de entrada e saída, alguns compiladores implementam testes específicos para fazer uma validação básica entre as 
strings de formatação dessas funções e os tipos dos demais argumentos.  No caso do 
gcc, que é o compilador padrão da maioria das distribuições de Linux, é necessário aplicar opções de compilação que habilitem tal diagnóstico.  Pode-se usar, para tanto, “
-Wformat” ou “
-Wall”.  Eu recomendo sempre “
-Wall”, bem como “
-Werror”, para que o compilador, além de alertar sobre código perigoso, impeça a compilação de seguir adiante se tal código perigoso for encontrado.
Note porém que esses diagnósticos apresentam apenas garantias necessárias (quanto ao tipo de dados dos argumentos), não garantias suficientes (e.g. um ponteiro para 
char será válido tanto para conversão com 
"%s" quanto para conversão com 
"%c", seja ele gerado a partir do decaimento de um 
array, seja calculado, seja reencaminhado de alguma variável de tipo ponteiro).
Terceiramente, vamos tentar entender porque o programa funcionou como esperado (por você) quando compilado no Turbo C (pelo compilador interno dele, o qual não conheço). O fato é que da maneira como o código está, o normal seria não funcionar mesmo. Talvez o compilador do Turbo C tenha alguma regra que identifique esse erro (que é bem comum) e já aplique a correção (não tenho como confirmar se é isso mesmo que ocorre). Pessoalmente não acho que isso seja bom, porque induz o programador a continuar errando. De toda forma, o Turbo C já está descontinuado há algum tempo, assim como o DevC. 
Não é proteção do Turbo C, mas deve ter sido uma daquelas coincidências fortuitas devido à forma como as variáveis locais são dispostas na memória.
Em sua explicação sobre o porquê de usar conversão 
"%c" quando o destino é um único caráter, faltou você dizer duas coisas: (1) por que não foi necessário colocar o espaço antes do especificados de conversão 
"%s", e (2) o que acontece quando a pessoa trata (no caso, fazendo com que 
scanf() trate) um único caráter como se fosse um 
array.
A resposta de (1) é que a função 
scanf() automaticamente executa o descarte de espaços em branco que ocorram antes de determinadas conversões, incluindo 
"%s", mas não quando se usa 
"%c".
No caso de (2), o comportamento da função é tratar as posições de memória adjacentes ao único caráter como se fossem partes de um único 
array.  As consequências últimas de assim se proceder são imprevisíveis, mas o que geralmente ocorre inicialmente é que os dados provenientes da conversão sobrescrevem variáveis declaradas no entorno da variável usada de modo incorreto.  Como toda string em C tem de ter um 
byte nulo após o final dos caracteres que a compõem, mesmo que o usuário digite apenas um caráter, a função 
scanf() vai guardar dois 
bytes: o primeiro (digitado pelo usuário) no lugar esperado, e o segundo (o 
byte nulo) na posição de memória seguinte, pertença ela a quem pertencer.
O padrão do C não prescreve o modo como variáveis devem ser dispostas na memória, de modo que cada compilador pode fazer suas próprias escolhas, inclusive levando em conta situações como uso ou não de otimizações e situações especiais de alinhamento de dados.  Pelas descrições de comportamento no DevC++ e Code::Blocks, que usam o GCC como compilador, e no Turbo C, uma explicação possível seria que o GCC dispôs em memória primeiro 
sexo, e depois, bem adjacente, 
nome, de modo que o 
byte nulo gravado pela leitura de 
sexo com uma conversão de 
string acabou sobrescrevendo a primeira posição de 
nome, tornando-a, para todos os efeitos, uma 
string vazia.  O Turbo C, pelo visto, usou uma disposição de dados diferente, de modo que o 
byte nulo após 
sexo não chegou a sobrescrever o conteúdo de 
nome (mas pode eventualmente ter perturbado o valor de 
idade, cujo valor não foi impresso para comparação).