ANTICHAT

ANTICHAT (https://forum.antichat.xyz/index.php)
-   Общие вопросы программирования (https://forum.antichat.xyz/forumdisplay.php?f=206)
-   -   C/C++. ASI. ВОПРОС. Конструкция *(int*) (https://forum.antichat.xyz/showthread.php?t=1472645)

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

Цитата:

Сообщение от voodushevlen

C++:





Код:

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


C++:





Код:

*
reinterpret_cast

(
0xBAB230
)
;



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

Digger Man52 30.03.2023 20:47

Да, *(int*)0xBAB230 это разыменование указателя на int, *(int*) используется для приведения указателя на void к указателю на int

Цитата:

Сообщение от voodushevlen

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

Цитата:

Сообщение от ARMOR

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

Цитата:

Сообщение от kin4stat

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

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

Код:

*(T*) -> (T&)
, где T - произвольный тип

Не, что-то не так. Поправьте, если неправильно понял.

Унарный оператор * над указателем на T это T (lvalue).

Ссылка на T (т.е. T&) может быть инициализирована объектом типа T (не T&).

Так как любое lvalue явно идентифицируется в памяти, обращение к ссылке под капотом будет ссылаться к адресу, в котором расположен этот lvalue

Цитата:

Сообщение от voodushevlen

В языке 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

Цитата:

Сообщение от san0

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

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

san0 31.03.2023 21:12

Цитата:

Сообщение от kin4stat

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

Цитата:

Сообщение от san0

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&


Время: 06:19