Почему я получаю случайное число при увеличении целочисленного значения указателя?

Я опытный программист на C#, но я очень новичок в C++. Я прекрасно понимаю основную идею указателей, но я играл. Вы можете получить фактическое целочисленное значение указателя, приведя его как int:

int i = 5;
int* iptr = &i;
int ptrValue = (int)iptr;

Что имеет смысл; это адрес памяти. Но я могу перейти к следующему указателю и привести его к int:

int i = 5;
int* iptr = &i;
int ptrValue = (int)iptr;
int* jptr = (int*)((int)iptr + 1);
int j = (int)*iptr;

и я получаю, казалось бы, случайное число (хотя это не очень хорошая PSRG). Что это за номер? Это другой номер, используемый тем же процессом? Возможно, это из-за другого процесса? Это плохая практика или запрещено? А если нет, то есть ли в этом польза? Это круто.


person Alexander Kvenvolden    schedule 23.12.2013    source источник
comment
Обратите внимание, что когда вы приводите указатель к типу int, а затем добавляете 1, это создаст в памяти другое значение, чем если бы вы добавили к нему 1 в качестве указателя. Когда вы добавляете 1 к int *, компьютер добавляет sizeof(int) к значению в памяти. Когда вы приводите его к int, а затем добавляете 1, компьютер просто добавляет 1. (По крайней мере, это то, что обычно происходит. При наличии неопределенного поведения компьютер может делать все, что угодно). , и даже когда поведение определено, компьютеру нужно сделать только то, что дает такие же видимые результаты, как и написанная вами программа.)   -  person user2357112 supports Monica    schedule 23.12.2013


Ответы (7)


Что это за номер? Это другой номер, используемый тем же процессом? Возможно, это из-за другого процесса?

Обычно вы не можете приводить указатели к целым числам и обратно и ожидать, что они будут разыменовываемыми. Целые числа — это числа. Указатели есть указатели. Это совершенно разные абстракции и несовместимы.

Если целые числа недостаточно велики для хранения внутреннего представления указателей (что, скорее всего, так и есть; целые числа обычно имеют длину 32 бита, а указатели обычно имеют длину 64 бита), или если вы изменяете целое число перед приведением его обратно к указатель, ваша программа демонстрирует неопределенное поведение и как такое всякое бывает.

См. < em>C++: безопасно ли привести указатель к типу int, а затем снова вернуться к указателю?

Это плохая практика или запрещено?

Запрещено? Нет.
Плохая практика? Ужасная практика.

person rightfold    schedule 23.12.2013

Вы перемещаетесь за пределы указателя i на 4 или 8 байт и печатаете число, которое может быть другим числом, хранящимся в вашем программном пространстве. Значение неизвестно, и это неопределенное поведение. Также есть большая вероятность, что вы можете получить ошибку (это означает, что ваша программа может взорваться) [Вы когда-нибудь слышали о SIGSEGV? Проблема нарушения сегментации]

person Aniket Inge    schedule 23.12.2013

Вы обнаруживаете, что случайные места в памяти содержат «неизвестные» данные. Не только это, но вы можете обнаружить, что указываете на память, на которую ваш процесс не имеет «прав», так что даже акт чтения содержимого адреса может вызвать ошибку сегментации.

В общем, если вы выделяете некоторую память указателю (например, с помощью malloc), вы можете взглянуть на эти места (в которых могут быть случайные данные «из последнего времени») и изменить их. Но данные, которые явно не принадлежат блоку памяти указателя, могут вести себя всеми королями неопределенного поведения.

Кстати, если вы хотите посмотреть на «следующее» место просто для того, чтобы

NextValue = *(iptr + 1);

Не делайте никакого приведения - арифметика указателя точно знает (в вашем случае), что означает вышеизложенное: «содержимое следующего места, на которое я ссылаюсь».

person Floris    schedule 23.12.2013

int i = 5;
int* iptr = &i;
int ptrValue = (int)iptr;
int* jptr = (int*)((int)iptr + 1);
int j = (int)*iptr;
  1. Вы можете привести int к указателю и обратно, и это даст вам то же значение
  2. Возможно, это из-за другого процесса? нет, и вы не можете получить доступ к памяти другого процесса, кроме как с помощью readProcessMemmory и writeProcessMemory под win32 API.
  3. Вы получаете другое число, потому что вы добавляете 1 к указателю, пытаетесь вычесть 1, и вы получите то же значение.
person SRedouane    schedule 23.12.2013

Когда вы определяете целое число с помощью

int i = 5;

это означает, что вы выделяете место в стеке потоков и инициализируете его как 5. Затем вы получаете указатель на эту память, которая на самом деле является позицией в текущем стеке потоков.

Когда вы увеличиваете свой указатель на 1, это означает, что вы указываете на следующее место в стеке потоков и снова анализируете его как целое число,

int* jptr = (int*)((int)iptr + 1);
int j = (int)*jptr;

Затем вы получите целое число из стека потоков, которое близко к тому, где вы определили свой int i.

Конечно, это не рекомендуется делать, если только вы не хотите стать хакером и использовать переполнение стека (здесь это означает, что это такое, а не название сайта, ха!)

person Daniel King    schedule 23.12.2013
comment
Вы можете использовать переполнение стека с этим? Я думал, что переполнение стека означает конец игры для моей программы. - person Alexander Kvenvolden; 23.12.2013
comment
Если вы используете memcpy для копирования огромного буфера в jptr или iptr, вы определенно можете испортить стек, но если вы являетесь экспертом, вы можете использовать буфер, который может просто изменить адрес возврата, чтобы он указывал на вашу функцию, где вы можете делать свои ПЛОХИЕ вещи, но не делайте этого! - person Daniel King; 23.12.2013

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

Это, например, когда вы хотите получить элементы массива. Но не может привести указатель к целому числу. Вы просто указываете на начало массива и увеличиваете свой указатель на 1, чтобы получить следующий элемент.

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;

printf("%d", *p); // this will print 1

p++; // pointer arithmetics

printf("%d", *p); // this will print 2
person rullof    schedule 23.12.2013

Это не "случайно". Это просто означает, что есть какие-то данные по следующему адресу

Чтение 32-битного слова с адреса A скопирует 4 байта по адресам [A], [A+1], [A+2], [A+3] в регистр. Но если вы разыменовываете int в [A+1], тогда ЦП загрузит байты с [A+1] по [A+4]. Поскольку значение [A+4] неизвестно, вы можете подумать, что это число "случайно".

В любом случае, это КРАЙНЕ опасно ????, так как

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

person phuclv    schedule 23.12.2013