Erro: a expressão deve ser um lvalue modificável C/C++ [RESOLVIDO]

1. Erro: a expressão deve ser um lvalue modificável C/C++ [RESOLVIDO]

Lucas
DcoderLA

(usa Debian)

Enviado em 20/08/2021 - 00:41h

Boa noite pessoal, estou exercitando vetor de structs e me deparo com esse erro sempre que tento passar o vetor por parametro para uma função. Estou fazendo isso da forma certa ? Desde Já agradeço as dicas !

Erro: a expressão deve ser um lvalue modificável C/C++

typedef struct 
{
char nome[50];
char endereco[100];
char telefone;
data aniversaio;
} contatos;

void criar(contatos vcontatos[MAX])
{
for (int i = 0; i < MAX; i++)
{
vcontatos[i].nome = "NULL";

}

}

int main()
{

contatos vcontatos[MAX];

return 0;
}



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 23/08/2021 - 05:37h

DcoderLA escreveu:

Boa noite pessoal, estou exercitando vetor de structs e me deparo com esse erro sempre que tento passar o vetor por parametro para uma função. Estou fazendo isso da forma certa ? Desde Já agradeço as dicas !

Erro: a expressão deve ser um lvalue modificável C/C++


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.
4=5; // Lembre que o operador “=” é para atribuição de valor, não de comparação entre valores.

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.
int a=0, b=1;
a+b=50;

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.
const int ci=5; // Variável do tipo inteiro constante.
i=5; // OK: a expressão “i” produz um lvalue modificável capaz de armazenar um valor inteiro.
ci=10; // ERRO: a expressão “ci” produz um lvalue não-modificável de um valor inteiro.
i+1=15; // ERRO: a expressão “i+1” não produz um lvalue.
i+i=20; // ERRO: a expressão “i+i” não produz um lvalue.

int ai[5]={1, 2, 3, 4, 5}; // Array de valores inteiros.
const int aci[5]={2, 3, 5, 7, 11}; // Array de valores inteiros constantes.
ai[0]=1; // OK: a expressão “ai[0]” produz um lvalue modificável capaz de armazenar um valor inteiro (correspondente ao primeiro elemento de ‘ai’).
aci[1]=3; // ERRO: a expressão “aci[1]” produz um lvalue não-modificável de um valor inteiro (correspondente ao segundo elemento de ‘aci’).
ai={1, 3, 5, 7, 9}; // ERRO: a expressão “ai” não produz um lvalue neste contexto (além disso, não é possível usar valores entre chaves neste contexto, que não é de declaração).

int *pi; // Ponteiro para inteiro.
const int *pci; // Ponteiro para inteiro constante.
pi=&i; // OK: a expressão “pi” produz um lvalue modificável capaz de armazenar um ponteiro para inteiro; a expressão “&i” obtém o endereço a partir do lvalue de ‘i’.
++*pi; // OK: a expressão “*pi” produz um lvalue modificável capaz de armazenar um valor inteiro (como ‘pi’ aponta para ‘i’, o efeito final neste caso é incrementar o valor de ‘i’).
pi[1]=1; // PROBLEMA: sintaticamente OK pois “pi[1]” produz um lvalue modificável supostamente capaz de armazenar um valor inteiro, que seria o segundo elemento de um array, PORÉM este ponteiro não se refere a um array, mas sim a um único valor.

pi=ai; // OK: a expressão “pi” produz um lvalue modificável capaz de armazenar um ponteiro para inteiro; a expressão “ai” não é um lvalue, mas é substituída durante a compilação pelo endereço do primeiro elemento do array.
pi[2]+=10; // OK: a expressão “pi[2]” produz um lvalue modificável capaz de armazenar um valor inteiro (como ‘pi’ aponta para os elementos de ‘ai’, o efeito final neste caso é incrementar o valor do terceiro elemento).

pi=&ci; // ERRO: a expressão “pi” produz um lvalue modificável capaz de armazenar um ponteiro para inteiro, porém um ponteiro não qualificado como sendo para dados constantes não pode receber um endereço de dados qualificados como constantes.
pi=aci; // ERRO: idem.

pci=ai; // OK: a expressão “pci” produz um lvalue modificável capaz de armazenar um ponteiro para inteiro; a expressão “ai” não é um lvalue, mas é substituída durante a compilação pelo endereço do primeiro elemento do array (o ponteiro para constante pode ser usado para apontar para objetos não-constantes).
++*pci; // ERRO: a expressão “*pci” produz um lvalue não-modificável de um valor inteiro, pois o tipo ponteiro diz que o dado apontado não é modificável (ainda que a ele tenha sido atribuído o endereço de um dado que é modificável).

int *const cpi=&i; // Ponteiro constante para inteiro (não-constante); a expressão “&i” obtém o endereço a partir do lvalue de ‘i’.
const int *const cpci=&i; // Ponteiro constante para inteiro constante (o ponteiro para dado constante pode ser usado para apontar para dado originalmente não-constante); a expressão “&i” obtém o endereço a partir do lvalue de ‘i’..
*cpi+=5; // OK: a expressão “*cpi” produz um lvalue modificável capaz de armazenar um valor inteiro (o ponteiro é constante, o dado não).
cpi=ai; // ERRO: a expressão “cpi” produz um lvalue não-modificável de um ponteiro para inteiro.

int (*pa5i)[5]; // Ponteiro para array com 5 elementos inteiros.
pa5i=&ai; // OK: a expressão “pa5i” produz um lvalue modificável capaz de armazenar um ponteiro para um array com 5 elementos inteiros; a expressão “&ai” obtém o endereço a partir do 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).
++*pa5i; // ERRO: a expressão “*pa5i” produz uma designação de array, que é transformada imediatamente no valor constante do endereço de seu primeiro elemento, e portanto não é um lvalue.
(*pa5i)[4]=100; // OK: a expressão “(*pa5i)[0]” produz um lvalue modificável capaz de armazenar um valor inteiro, correspondente ao quinto elemento do array designado por “*pa5i”.


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 
{
char nome[50];
char endereco[100];
char telefone;
data aniversaio;
} contatos;

void criar(contatos vcontatos[MAX])
{
for (int i = 0; i < MAX; i++)
{
vcontatos[i].nome = "NULL";

}

}

int main()
{

contatos vcontatos[MAX];

return 0;
}


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)





Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts