C/C++. ASI. ВОПРОС. Конструкция *(int*)

ya_noob

Участник
Автор темы
57
7
В языке C и C++ существуют указатели. Естественно понятно, что они указывают на определенную ячейку памяти.
C++:
// Я знаю что ссылки могут обьявляться следующим образом.
int x = 1;
int* ax = &x;

// Но меня смутило следующее.

int& addr = *(int*)0xBAB230; // интовая ссылка addr содержит в себе адрес памяти, который сначала обьявляется как указатель, а затем разименовывается?

// Я хочу понять, правильно ли я это понимаю. *(int*)0xBAB230 - int* 0xBAB230 - это указатель указателя, который потом разименовывается (благодаря * спереди) и получается что-то вроде int& addr = b, где b = 0xBAB230, а *b = значение этого адреса? Или нет?
// В ИТОГЕ: int& addr = *(int*)0xBAB230 == int& addr = b; addr = *b; *(int*)0xBAB230 - адрес адреса, который затем разименовывают и получается адрес в нужном формате?

// Скорее всего я все не так понимаю. Напишите пожалуйста, что это за конструкция *(int*), как ее можно по другому написать, и почему именно в таком виде "*(int*)".
 
Решение
C++:
int& addr = *(int*)0xBAB230;
(int*) - C style cast, в данном случае эквивалентный код выглядит так:

C++:
int& addr = *(reinterpret_cast<int*>(0xBAB230));
0xBAB230 - integer literal, интерпретируется как число.

reinterpret_cast<int*> - преобразование числа 0xBAB230 типа int в указатель на int. Фактически данная конструкция ничего не делает.
В данном случае она нужна для преобразования числа в указатель, на уровне системы типов языка.

В результате этого выражения получается указатель, который указывает на адрес 0xBAB230

Далее идет *(reinterpret_cast<...>(...));

Разыменование указателя дает lvalue ссылку на тип под указателем.

Фактически работает примерно так: звездочка около типа заменяется...

ARMOR

kjor32 is legend
Модератор
4,852
6,083
C++:
// Скорее всего я все не так понимаю. Напишите пожалуйста, что это за конструкция *(int*), как ее можно по другому написать, и почему именно в таком виде "*(int*)".

C++:
*reinterpret_cast<int*>(0xBAB230);

А *(int*) - это вроде как старый вариант переобразования типов
 
  • Нравится
Реакции: ya_noob

Digger Man

Любитель Linux
Модератор
1,638
1,125
Да, *(int*)0xBAB230 это разыменование указателя на int, *(int*) используется для приведения указателя на void к указателю на int
C++:
// В ИТОГЕ: int& addr = *(int*)0xBAB230 == int& addr = b; addr = *b; *(int*)0xBAB230 - адрес адреса, который затем разименовывают и получается адрес в нужном формате?
Мы разыменовываем 0xBAB230 , и присваиваем это значение, addr и получаем addr = *(int*)0xBAB230 и addr = b;
Альтернативный вид это конструкции, будет выглядеть как
C++:
*reinterpret_cast<int*>(0xBAB230)
*reinterpret_cast<int*>(0xBAB230) возвращает значение переменной, находящейся по адресу 0xBAB230
 

EclipsedFlow

Известный
Проверенный
1,040
464
C++:
*reinterpret_cast<int*>(0xBAB230);

А *(int*) - это вроде как старый вариант переобразования типов
Он не старый, а CИ-шный вариант преобразования типов.
Для языка C++ нужно/можно приводить static_cast, const_cast, dynamic_cast, reinterpret_cast. Так и СИ-шным вариантом выше.

Код:
// Как я понимаю мы копируем значение переменной из адреса предварительно приведя к типу указателя int и последующим его разыменованием.
int addr = *(int*)0xBAB230;

// А если добавить в перед нашей переменной амперсанд(ссылку)[&] то мы не копируем, а берем ссылку переменной с адреса
int& addr = *(int*)0xBAB230;

// Если что подправьте если не правильно понимаю
 

kin4stat

mq-team
Всефорумный модератор
2,730
4,711
C++:
int& addr = *(int*)0xBAB230;
(int*) - C style cast, в данном случае эквивалентный код выглядит так:

C++:
int& addr = *(reinterpret_cast<int*>(0xBAB230));
0xBAB230 - integer literal, интерпретируется как число.

reinterpret_cast<int*> - преобразование числа 0xBAB230 типа int в указатель на int. Фактически данная конструкция ничего не делает.
В данном случае она нужна для преобразования числа в указатель, на уровне системы типов языка.

В результате этого выражения получается указатель, который указывает на адрес 0xBAB230

Далее идет *(reinterpret_cast<...>(...));

Разыменование указателя дает lvalue ссылку на тип под указателем.

Фактически работает примерно так: звездочка около типа заменяется амперсандом(ссылкой)

*(T*) -> (T&), где T - произвольный тип
После этого ссылка на int присваивается переменной addr.

Если говорить очень грубо - ссылка это всегда разыменованный указатель, который не может быть НЕ ИНИЦИАЛИЗОВАН и НЕ МОЖЕТ БЫТЬ ИЗМЕНЕН(сам адрес ссылки)

В результате выражения, получается ссылка типа int на адрес 0xBAB230. Любые операции с этой переменной будут взаимодействовать с ячейкой памяти по адресу 0xBAB230 как с целым числом(int'ом)
 
Последнее редактирование:

san0

Известный
Друг
411
267
Разыменование указателя дает lvalue ссылку на тип под указателем.

Фактически работает примерно так: звездочка около типа заменяется амперсандом(ссылкой)

*(T*) -> (T&), где T - произвольный тип
Не, что-то не так. Поправьте, если неправильно понял.

Унарный оператор * над указателем на T это T (lvalue).
Ссылка на T (т.е. T&) может быть инициализирована объектом типа T (не T&).
Так как любое lvalue явно идентифицируется в памяти, обращение к ссылке под капотом будет ссылаться к адресу, в котором расположен этот lvalue

В языке C и C++ существуют указатели

Вам уже много написали всего, поэтому попробую немного вокруг описать, может это поможет

Ссылочный тип данных - это новояз именно C++. Сохраняя общую семантику указателей (в частности, техническую часть), он становится более безопасным и предпочитаемым типом нежели указатель, так как:
  • Ссылка не может быть неопределенной/нулевой/указывать на невалидный объект. Представьте, как C++ обошел все языки, в которых только недавно решили null-safety вводить;
  • Ссылка четко определена как немутабельная в отличии от указателя (нельзя поменять куда она ссылается). Кроме того, не бывает ссылки на ссылку, массив ссылок и прочих головоломок;
  • Ссылка исходя из правил и ограничений выше указанных имеет определенное поведение, из разряда, что ссылки должны быть инициализированы для членов класса, что константная ссылка продлевает срок жизни объекта и так далее.
  • Удобство использования, так как не нужно постоянно разыменовывать его и прочие мелочи.
Надеюсь не будет звучать тупо, но в целом при использовании ссылок не нужно беспокоится об адресах, ссылки - это о значениях, которые расположены по адресам, указатели - наоборот, об адресах, которые указывают на значения.
Окей, предположим, что мы хотим пользоваться этой вашей крутой ссылкой хваленой, но нужно это для адреса 0xBAB230:

C++:
// ** давайте не будет придираться к тому, что не всегда sizeof(int) == sizeof(int*)
// да и вообще адрес памяти - не совсем верный термин, так как это не всегда линейное пространство, c/c++ - это не только x86/arm
    
// сохраним желаемый адрес памяти в переменной. Уже выше говорили, что 0xBAB230 - числовой литерал, значит и тип переменной int
int address_as_int = 0xBAB230;
// так как у нас адрес, то объявить ссылку мы не можем, нужно получить значение по этому адресу, для чего будем пользоваться указателями
// чтобы представить число как адрес в памяти, его необходимо преобразовать в указатель
int* address_as_pointer = (int*)address_as_int;
// здесь есть 3 варианта
// *address_as_pointer   -> разыменование указателя. Получит то, куда он ссылается. Т.е. значение по адресу 0xBAB230
// address_as_pointer    -> адрес, куда указывает указатель, т.е. 0xBAB230
// &address_as_pointer   -> адрес самого указателя address_as_pointer, это такая же переменная как и все другие, 
//                          просто содержит в себе адрес, а значит должна где-то находиться в памяти

// как уже подчеркнули, чтобы инициализировать ссылку, нужно передать ей значение, т.е. разыменовать указатель
// если указатель неверный, то "ошибка" случится на этапе разыменовывания указателя, но не инициализации ссылки
int& reference = *address_as_pointer;

// теперь можно пользоваться, "разыменовывание" ссылки сделает сам компилятор, 
// причем с гарантией того, что он указывает на инициализированный объект
// reference = 0;

// а вот с указателем проблемы, ведь нет гарантии, что address_as_pointer указывает на валидный адрес (даже если бы сделали его константным), 
// до момента его разыменовывания это может быть бомба замедленного действия. 
// Каждое разыменовывание выполняется самостоятельно со всеми рисками
// *address_as_pointer = 0;
 
  • Нравится
Реакции: legendabrn и moreveal

SR_team

like pancake
BH Team
4,720
6,370
Ссылка это обертка над указателем. Для компилятора запись
int &x = *(int*)0x123
Означает следующее:
int *x = &(*(int*)0x122)
Тут & и * взаимоуничтожаются, и остается:
int *x = (int*)0x123

Далее, когда ты пишешь в x, компилятор подставляет * и получается, что ты пишешь в *x = 1337
 
  • Нравится
Реакции: ya_noob

kin4stat

mq-team
Всефорумный модератор
2,730
4,711
Унарный оператор * над указателем на T это T (lvalue).
Ссылка на T (т.е. T&) может быть инициализирована объектом типа T (не T&).
Так как любое lvalue явно идентифицируется в памяти, обращение к ссылке под капотом будет ссылаться к адресу, в котором расположен этот lvalue
Ты путаешь категорию значения с типом ссылки. Разыменование дает lvalue reference, lvalue reference сам по себе является lvalue
 

san0

Известный
Друг
411
267
Ты путаешь категорию значения с типом ссылки. Разыменование дает lvalue reference, lvalue reference сам по себе является lvalue
C++:
int& addr = *(int*)0xBAB230;
Ссылка - это левая часть, ее тип - lvalue reference to int.
Справа - выражение, которое после вычисления *((T*)ptr) будет lvalue выражением, но типа T, а не lvalue reference T&

Даже явный каст к T& ничего не дает, он все равно неявно преобразуется к T и инициализирует ссылку, как и сказано в стандарте
C++:
int  a = 1;
int* ptr = (int*)&a;

// даже не ссылку можно инициализировать при помощи lvalue выражения (static_cast к lvalue reference это lvalue) типа lvalue reference (int&)
int  val = static_cast<int&>(*ptr);
int& ref = static_cast<int&>(*ptr);

Где стандартом обозначено, что *(T*) == (T&) т.е. разыменование указателя равняется T& а не T?
 

kin4stat

mq-team
Всефорумный модератор
2,730
4,711
C++:
int& addr = *(int*)0xBAB230;
Ссылка - это левая часть, ее тип - lvalue reference to int.
Справа - выражение, которое после вычисления *((T*)ptr) будет lvalue выражением, но типа T, а не lvalue reference T&

Даже явный каст к T& ничего не дает, он все равно неявно преобразуется к T и инициализирует ссылку, как и сказано в стандарте
C++:
int  a = 1;
int* ptr = (int*)&a;

// даже не ссылку можно инициализировать при помощи lvalue выражения (static_cast к lvalue reference это lvalue) типа lvalue reference (int&)
int  val = static_cast<int&>(*ptr);
int& ref = static_cast<int&>(*ptr);

Где стандартом обозначено, что *(T*) == (T&) т.е. разыменование указателя равняется T& а не T?
Да, ты прав, я там ошибся.

*ptr дает lvalue на T, и уже в момент присвоения категория lvalue материализуется в тип, и там получается T&