Разыменование двухуровневого указателя приводит к отличному поведению от разыменования одноуровневого указателя

Версия 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 раз?

-4

Решение

Это очень разные вещи. 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 и это ужасно Придерживайтесь своего первого кода.

2

Другие решения

Арифметика указателей и целочисленная арифметика — это не одно и то же.

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

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)
0

По вопросам рекламы [email protected]