PDA

Просмотр полной версии : C/C++. ASI. ВОПРОС. Конструкция *(int*)


ya_noob
30.03.2023, 20:14
В языке 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*)".

ARMOR
30.03.2023, 20:43
C++:






// Скорее всего я все не так понимаю. Напишите пожалуйста, что это за конструкция *(int*), как ее можно по другому написать, и почему именно в таком виде "*(int*)".






C++:






*
reinterpret_cast

(
0xBAB230
)
;




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

Digger Man52
30.03.2023, 20:47
Да, *(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

(
0xBAB230
)




*reinterpret_cast(0xBAB230) возвращает значение переменной, находящейся по адресу 0xBAB230

EclipsedFlow
31.03.2023, 01:54
C++:






*
reinterpret_cast

(
0xBAB230
)
;




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


Он не старый, а CИ-шный вариант преобразования типов.

Для языка C++ нужно/можно приводить static_cast, const_cast, dynamic_cast, reinterpret_cast. Так и СИ-шным вариантом выше.

Код:






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

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

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

kin4stat
31.03.2023, 06:50
C++:






int
&
addr
=
*
(
int
*
)
0xBAB230
;





(int*)

- C style cast, в данном случае эквивалентный код выглядит так:

C++:






int
&
addr
=
*
(
reinterpret_cast

(
0xBAB230
)
)
;





0xBAB230

- integer literal, интерпретируется как число.


reinterpret_cast

- преобразование числа 0xBAB230 типа int в указатель на int. Фактически данная конструкция ничего не делает.

В данном случае она нужна для преобразования числа в указатель, на уровне системы типов языка.

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

0xBAB230


Далее идет

*(reinterpret_cast(...));


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

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


*(T*) -> (T&)

, где T - произвольный тип

После этого ссылка на

int

присваивается переменной addr.

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

В результате выражения, получается ссылка типа

int

на адрес

0xBAB230

. Любые операции с этой переменной будут взаимодействовать с ячейкой памяти по адресу

0xBAB230

как с целым числом(

int'ом

)

san0
31.03.2023, 16:10
Разыменование указателя дает 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;

SR_team
31.03.2023, 17:33
Ссылка это обертка над указателем. Для компилятора запись


int &x = *(int*)0x123


Означает следующее:


int *x = &(*(int*)0x122)


Тут & и * взаимоуничтожаются, и остается:


int *x = (int*)0x123


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

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


Ты путаешь категорию значения с типом ссылки. Разыменование дает lvalue reference, lvalue reference сам по себе является lvalue

san0
31.03.2023, 21:12
Ты путаешь категорию значения с типом ссылки. Разыменование дает 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

(
*
ptr
)
;
int
&
ref
=
static_cast

(
*
ptr
)
;




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

kin4stat
01.04.2023, 14:21
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

(
*
ptr
)
;
int
&
ref
=
static_cast

(
*
ptr
)
;




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


Да, ты прав, я там ошибся.

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