Enviado em 23/08/2021 - 05:37h 
		DcoderLA escreveu:
O problema é que você está tentando fazer uma atribuição a um valor calculado.  Mas vamos por partes, inciando com exemplos simples do problema, até chegar a casos mais complicados.
Veja este exemplo, e note como ele não faz sentido:
// Exemplo de código inválido 1: tenta atribuir um valor constante a outro valor constante. 
O primeiro exemplo foi muito óbvio, e o erro salta aos olhos.  A operação que deu erro para você é muito menos óbvia, mas ela tem praticamente o mesmo problema.
Antes, porém, de chegar a ela, vejamos outro exemplo de código invalido:
// Exemplo de código inválido 2: tenta atribuir um valor constante a um valor calculado. 
No bloco acima, a expressão “a+b” provocaria um cálculo para a obtenção de um valor que não possui um endereço próprio para armazenamento na memória.  E justamente porque não possui um endereço próprio, não faz sentido tentar usar o operador de atribuição para tentar sobrescrevê-lo.
E aí a gente chega ao seu código.
O campo 
nome  da estrutura à qual você atribuiu o apelido 
contatos  é um 
array  (também chamado de 
vetor , mas eu não vou usar essa designação para não confundir com o tipo 
std::vector  da biblioteca padrão do C++), cujos 50 elementos são do tipo 
char .
Em C, declarações de 
arrays  provocam a alocação de espaços fixos de memória, que nunca vão mudar de posição ao longo de todo o tempo de vida desses 
arrays .  Cada identificador que designa um 
array  se refere ao bloco fixo de memória para ele disponibilizado, não ao seu conteúdo
† .  E justamente porque o identificador do 
array  se refere a algo que é fixo e cujo valor (que é um endereço na memória) é conhecido pelo compilador, este último não reserva espaço extra na memória para armazenar tal endereço, mas, durante a compilação, apenas substitui diretamente no programa cada ocorrência do identificador pelo endereço correspondente
‡ , de modo análogo ao de um valor constante ou um valor calculado, como nos exemplos mostrados anteriormente.
Por designar um endereço, o valor produzido pelo compilador em substituição ao identificador do 
array  é de um tipo ponteiro para dado compatível com o tipo dos elementos do 
array .  No seu caso, como o tipo do campo 
nome  da estrutura é 
array  de elementos do tipo 
char , então o tipo do valor produzido pelo identificador do 
array  será ponteiro para 
char  (em C, “
char * ”).  E, de novo, esse valor não pode ser sobrescrito por outro porque ele é um valor calculado durante a compilação e imediatamente substituído no código compilado, não residindo em qualquer variável do programa.
E este é o sentido da mensagem de erro dizendo que o que aparece no lado esquerdo do operador de atribuição teria de ser um 
lvalue  modificável: 
lvalue  é o resultado de uma expressão que designa um objeto que possui um local de armazenamento bem definido na memória do programa; além disso, para poder ser alvo de uma operação de atribuição, não basta estar numa posição bem definida da memória, mas também tem de poder ser gravado.
int i;           // Variável do tipo inteiro.lvalue  de ‘i’.lvalue  de ‘i’.lvalue  de ‘i’..lvalue  do array inteiro designado por ‘ai’ (o operador ‘&’ é um dos casos em que o identificador do array não é substituído imediatamente pelo valor constante do endereço de seu primeiro elemento). 
Se você quiser manipular o conteúdo de 
arrays , tem de fazê-lo elemento por elemento, ou então usar uma função que faça essa manipulação por você (mas tais funções vão fazer exatamente uma manipulação individual dos elementos, apenas escondendo de você o trabalho).
No caso de 
arrays  de caracteres usados para representar 
strings , há várias funções de <string.h> que podem ser do seu interesse, tais como 
strncpy (), 
strncat (), 
memset () e outras.
Como último comentário, vi que você tentou atribuir a 
string  “NULL” a contatos ainda não preenchidos.  Não sei se é isso mesmo o que você queria fazer, se queria zerar a string ou que queria algo equivalente à sinalização de 
string  inválida, que às vezes é indicada por argumentos de função por meio de ponteiros nulos.  Se for o primeiro caso, você tem de usar uma função de cópia de 
strings , tal como 
strncpy ().  Se for o segundo, creio que o melhor seria usar 
memset (), tendo 
0  como segndo argumento.  Se for o terceiro, não faz sentido, uma vez que cada array corresponde a um bloco fixo de memória e que nunca será nulo, de modo que provavelmente você deveria usar uma das duas possibilidades anteriores.
typedef struct   †  O conteúdo só é manipulado quando você se refere aos elementos do 
array  (com algo como “
contato.nome[0] ”, que indica o primeiro caráter do campo 
nome  do objeto 
contato , ou “
vcontatos[3].nome[4] ”, que indica o quinto caráter do campo 
nome  do quarto elemento do 
array  de contatos 
vcontatos ).  Note, ainda, que ter uma região fixa de memória não implica necessariamente que o conteúdo dessa memória será fixo.  Em outras palavras, você não pode mudar um determinado 
array  de lugar mas, se os elementos não tiverem sido declarados como constantes, provavelmente pode alterar os valores de tais elementos.
‡  Existem apenas três exceções em C para a a regra de substituir o identificador do 
array  por um valor do tipo ponteiro constante para o primeiro elemento: no próprio momento da declaração (por motivos óbvios), quando o identificador é submetido como operando para o operador 
sizeof  (nesse caso, o compilador calcula o tamanho total do bloco de memória fixo associado ao 
array ), e quando o identificador é usado como operando do operador 
&  (que obtém um “ponteiro para o 
array  inteiro”, em vez de apenas um ponteiro para o primeiro elemento).  Em C++, há outros casos de exceção, tais como operando de como 
typeof  ou parâmetro de 
templates .
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)