left operand must be l-value

336
04 августа 2021, 02:20

Я хочу неизвестному типу указателя присвоить void *, т.е.

int i = 5;
void* iptr = &i;
int* ptr;
ptr = iptr;

Получаю ошибку невозможности привидения. Пытаюсь привести тип к void *

(void *)ptr = iptr;

Однако снова получаю ошибку, но немного другую: left operand must be l-value. Почему в таком случае левая часть становится r-value? Далее я сделал что-то такое

*(void**)&ptr = iptr

и все заработало. Однако что это значит я до конца не понимаю. Помогите, пожалуйста, разобраться.

Answer 1

Результат приведения типа всегда является rvalue (за исключением случая приведения к ссылочному типу в С++). Почему вас это удивляет - не ясно. Целевой тип приведения в общем случае не имеет ничего общего с исходным типом. Как тут может получиться lvalue? С каким объектом в памяти это lvalue будет связано?

Вариант *(void**)&ptr не является "приведением типа" вообще. Это попытка переинтерпретации памяти указателя int *ptr как объекта типа void *. Такая переинтерпретация приводит к неопределенному поведению.

Если вы хотите в указатель int *ptr занести значение указателя void *iptr (что за странная схема именования?), то приведение типа тут возможно такое

ptr = (int *) iptr;
// или
ptr = static_cast<int *>(iptr);

Однако в языке С и этого не нужно

ptr = iptr;

ибо в С преобразования указателей как в void *, так и из void * делаются неявно.

Answer 2

Задавая вопрос, вы свалили в кучу теги С++ и С, что в данном конкретном случае, неправильно - в этих языках принят разный синтаксис явного преобразования типов. C++, в виду обратной совместимости, поддерживает синтаксис С, но пользоваться им неудобно.

Вам нужно приводить тип не того, куда вы хотите что-то записать, а того, что вы хотите записать.

Таким образом, на C++:

int i = 5;
void* iptr = &i;
int* ptr;
ptr = reinterpret_cast<int*>(iptr);

Этот синтаксис вполне явно указывает, что (iptr) и к какому типу (int*) надо приводить.

Сишные скобочки из 80x же могут сыграть с вами злую шутку, однако в данном случае, нужно ими пользоваться вот так:

int i = 5;
void* iptr = &i;
int* ptr;
ptr = (int*)iptr;

То, как вы поступили тоже будет работать, однако создает трудности в понимании кода. Кроме того, вы не сможете написать более качественный код с использованием const:

int i = 5;
void* iptr = &i;
const int* ptr = reinterpret_cast<int*>(iptr);
Answer 3

То, чего вы хотите добиться, может приводить к серьёзным проблемам.

@AR Hovsepyan в комментарии уже написал Вам про это:

вот поэтому работа с void* небезопасна...

Пример, где происходит разыменование int* как double*, то есть по факту будет попытка прочитать по некоторому адресу байт больше, чем там на самом деле есть(проинициализировано)

Я не знаю точно что это - UB или нет, но это очевидно ошибка.

#include <iostream>
static int N = 2;
static int* iptr = &N;
static double D = 15.0;
static double* dptr = &D;
int* getPtr(int i){
    (void)i;
    return iptr;
}
double* getPtr(double d){
    (void)d;
    return dptr;
}
int main()
{
    int n = 5;
    void* ptr = &n;
    auto iptr = getPtr(1);
    iptr = static_cast<decltype(iptr)>(ptr);    //ok, типы свопадают
    std::cout << *iptr << std::endl;
    auto dptr = getPtr(1.0);
    dptr = static_cast<decltype(dptr)>(ptr);    //error, int* -> double*
    std::cout << *dptr << std::endl;
    return 0;
}
Answer 4

Потому что вы фактически приводите значение указателя ptr к типу void*. Получается что-то вроде

0xDEADBEEF = iptr;

Во втором случае вы берете адрес переменной ptr, приводите его (адрес!) к void**, и при разыменовании получаете переменную по этому адресу типа void*.

READ ALSO
Веб доступ к десктопному приложению

Веб доступ к десктопному приложению

Объясню суть проблемы: есть десктопное графическое приложение ,написанное на с++, появилась срочная необходимость доступа к нему через web браузер,...

155
Вывести в консоль фигуру из цифр

Вывести в консоль фигуру из цифр

Пользователь вводит размер фигуры сам и программа печатает в консоли прямоугольник следующим образом:

150
std::variant внутрянняя цикличность(JSON)

std::variant внутрянняя цикличность(JSON)

Захотелось мне парсер сделать для JSON

163
Функция не возвращает ссылку

Функция не возвращает ссылку

Есть следующая функция, она должна возвращать ссылку на значение ключа, если ключ такой существует в словаре, иначе выбрасывать исключение,...

158