Я хочу неизвестному типу указателя присвоить 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
и все заработало. Однако что это значит я до конца не понимаю. Помогите, пожалуйста, разобраться.
Результат приведения типа всегда является rvalue (за исключением случая приведения к ссылочному типу в С++). Почему вас это удивляет - не ясно. Целевой тип приведения в общем случае не имеет ничего общего с исходным типом. Как тут может получиться lvalue? С каким объектом в памяти это lvalue будет связано?
Вариант *(void**)&ptr
не является "приведением типа" вообще. Это попытка переинтерпретации памяти указателя int *ptr
как объекта типа void *
. Такая переинтерпретация приводит к неопределенному поведению.
Если вы хотите в указатель int *ptr
занести значение указателя void *iptr
(что за странная схема именования?), то приведение типа тут возможно такое
ptr = (int *) iptr;
// или
ptr = static_cast<int *>(iptr);
Однако в языке С и этого не нужно
ptr = iptr;
ибо в С преобразования указателей как в void *
, так и из void *
делаются неявно.
Задавая вопрос, вы свалили в кучу теги С++
и С
, что в данном конкретном случае, неправильно - в этих языках принят разный синтаксис явного преобразования типов. 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);
То, чего вы хотите добиться, может приводить к серьёзным проблемам.
@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;
}
Потому что вы фактически приводите значение указателя ptr
к типу void*
. Получается что-то вроде
0xDEADBEEF = iptr;
Во втором случае вы берете адрес переменной ptr
, приводите его (адрес!) к void**
, и при разыменовании получаете переменную по этому адресу типа void*
.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Объясню суть проблемы: есть десктопное графическое приложение ,написанное на с++, появилась срочная необходимость доступа к нему через web браузер,...
Пользователь вводит размер фигуры сам и программа печатает в консоли прямоугольник следующим образом:
Есть следующая функция, она должна возвращать ссылку на значение ключа, если ключ такой существует в словаре, иначе выбрасывать исключение,...