 |

30.03.2023, 20:14
|
|
Познающий
Регистрация: 26.10.2022
Сообщений: 57
С нами:
1870038
Репутация:
8
|
|
В языке 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*)".
|
|
|

30.03.2023, 20:43
|
|
Флудер
Регистрация: 02.02.2019
Сообщений: 5,070
С нами:
3831395
Репутация:
183
|
|
Сообщение от voodushevlen
C++:
Код:
// Скорее всего я все не так понимаю. Напишите пожалуйста, что это за конструкция *(int*), как ее можно по другому написать, и почему именно в таком виде "*(int*)".
C++:
Код:
*
reinterpret_cast
(
0xBAB230
)
;
А *(int*) - это вроде как старый вариант переобразования типов
|
|
|

30.03.2023, 20:47
|
|
Познавший АНТИЧАТ
Регистрация: 14.07.2019
Сообщений: 1,097
С нами:
3596877
Репутация:
183
|
|
Да, *(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
|
|
|

31.03.2023, 01:54
|
|
Познавший АНТИЧАТ
Регистрация: 18.09.2017
Сообщений: 1,044
С нами:
4553429
Репутация:
153
|
|
Сообщение от 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;
// Если что подправьте если не правильно понимаю
|
|
|

31.03.2023, 06:50
|
|
Флудер
Регистрация: 06.11.2017
Сообщений: 2,759
С нами:
4483143
Репутация:
183
|
|
C++:
Код:
int
&
addr
=
*
(
int
*
)
0xBAB230
;
- C style cast, в данном случае эквивалентный код выглядит так:
C++:
Код:
int
&
addr
=
*
(
reinterpret_cast
(
0xBAB230
)
)
;
- integer literal, интерпретируется как число.
- преобразование числа 0xBAB230 типа int в указатель на int. Фактически данная конструкция ничего не делает.
В данном случае она нужна для преобразования числа в указатель, на уровне системы типов языка.
В результате этого выражения получается указатель, который указывает на адрес
Далее идет
Код:
*(reinterpret_cast(...));
Разыменование указателя дает lvalue ссылку на тип под указателем.
Фактически работает примерно так: звездочка около типа заменяется амперсандом(ссылкой)
, где T - произвольный тип
После этого ссылка на
присваивается переменной addr.
Если говорить очень грубо - ссылка это всегда разыменованный указатель, который не может быть НЕ ИНИЦИАЛИЗОВАН и НЕ МОЖЕТ БЫТЬ ИЗМЕНЕН(сам адрес ссылки)
В результате выражения, получается ссылка типа
на адрес
. Любые операции с этой переменной будут взаимодействовать с ячейкой памяти по адресу
как с целым числом(
)
|
|
|

31.03.2023, 16:10
|
|
Постоянный
Регистрация: 15.12.2013
Сообщений: 412
С нами:
6530249
Репутация:
133
|
|
Сообщение от kin4stat
Разыменование указателя дает lvalue ссылку на тип под указателем.
Фактически работает примерно так: звездочка около типа заменяется амперсандом(ссылкой)
, где 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;
|
|
|

31.03.2023, 17:33
|
|
Флудер
Регистрация: 26.10.2013
Сообщений: 4,924
С нами:
6603505
Репутация:
183
|
|
Ссылка это обертка над указателем. Для компилятора запись
Код:
int &x = *(int*)0x123
Означает следующее:
Код:
int *x = &(*(int*)0x122)
Тут & и * взаимоуничтожаются, и остается:
Код:
int *x = (int*)0x123
Далее, когда ты пишешь в x, компилятор подставляет * и получается, что ты пишешь в *x = 1337
|
|
|

31.03.2023, 18:35
|
|
Флудер
Регистрация: 06.11.2017
Сообщений: 2,759
С нами:
4483143
Репутация:
183
|
|
Сообщение от san0
Унарный оператор * над указателем на T это T (lvalue).
Ссылка на T (т.е. T&) может быть инициализирована объектом типа T (не T&).
Так как любое lvalue явно идентифицируется в памяти, обращение к ссылке под капотом будет ссылаться к адресу, в котором расположен этот lvalue
Ты путаешь категорию значения с типом ссылки. Разыменование дает lvalue reference, lvalue reference сам по себе является lvalue
|
|
|

31.03.2023, 21:12
|
|
Постоянный
Регистрация: 15.12.2013
Сообщений: 412
С нами:
6530249
Репутация:
133
|
|
Сообщение от 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?
|
|
|

01.04.2023, 14:21
|
|
Флудер
Регистрация: 06.11.2017
Сообщений: 2,759
С нами:
4483143
Репутация:
183
|
|
Сообщение от 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&
|
|
|
|
 |
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|