Версия 1:
int* work(int** pointer, int offset)
{
return *pointer + (offset/sizeof(int));
}
int main()
{
int** pointer = (int**) 0x4df73c;
int offset = 0xf4;
int* healthLowestPointer = work(pointer, offset);
while(true) {
*healthLowestPointer = 1000;
Sleep(250);
}
}
Версия 2:
int* work(int* pointer, int offset)
{
return (int*) (*pointer + (offset/sizeof(int)));
}
int main()
{
int* pointer = (int*) 0x4df73c;
int offset = 0xf4;
int* healthLowestPointer = work(pointer, offset);
while(true) {
*healthLowestPointer = 1000;
Sleep(250);
}
}
Версия 1 работает правильно, но версия 2, похоже, нет. Я не понимаю, почему версия 2 не работает. Разве разыменование двухуровневого указателя — это не то же самое, что разыменование одноуровневого указателя, т.е. он захватывает значение по адресу памяти, который содержит указатель?
Как написать функцию, которая принимает указатель n-уровня в качестве входных данных и возвращает указатель 1-го уровня, разыменовывая указатель n-уровня n-1 раз?
Это очень разные вещи. int**
это указатель на указатель на int
, Если вы разыграете его, вы получите int*
, int*
это указатель на int
, Если вы разыграете его, вы получите int
,
В первом примере вы передаете int**
со значением 0x4df73c
в качестве первого аргумента work
, Вы тогда разыменовываете это, что должно дать вам int*
, То есть, 0x4df73c
это адрес и адрес *pointer
получил тебе вторую аддессу. Затем вы делаете указательную арифметику с этим int*
добавляя (offset/sizeof(int))
сколько получается int
с есть offset
байт. Поэтому, когда вы добавите это значение, ваш int*
будет двигаться, чтобы указать на int
в этом смещении. Вы тогда вернете это int*
и все хорошо.
Во втором примере вы передаете 0x4df73c
как int*
, Вы тогда разыменовываете это, что дает вам int
, Теперь +
не делает арифметику указателей — она выполняет целочисленную арифметику. (offset/sizeof(int))
все равно даст вам количество int
в offset
байты, но арифметика не будет делать то, что вы хотите. Это не увеличит ценность int
на соответствующую сумму.
Скажем int
4 байта на вашей машине. Когда у вас есть int*
называется p
и вы делаете p + 5
это не просто добавляет 5
по адресу в p
, Добавляет 5
раз размер int
(20 байт), так что теперь он указывает на 5 int
, Однако, если у вас есть int
называется i
и вы делаете i + 5
это действительно просто добавить 5
,
Так что это твоя проблема. Когда вы добавляете в int
, вы не делаете соответствующую арифметику. Я представляю это было бы работать, если вы измените его, предполагая int*
и int
имеют одинаковый размер в вашей системе (как вы говорите, они есть):
return (int*) (*pointer + offset);
но я не рекомендую это. Не используйте int
как будто это указатель. Приведение к (int*)
включает в себя reinterpret_cast
и это ужасно Придерживайтесь своего первого кода.
Арифметика указателей и целочисленная арифметика — это не одно и то же.
Игнорируя вопрос о том, является ли запись в местоположения, которые вы используете, действительны, рассмотрите результат этого:
int i=5;
int j=i+1; // now j==6
int* pi = &i; // let's say pi is 0x1000
int* pj = &i+1 // now pj is 0x1000 + (sizeof int)