c ++ указатель-головоломка - PullRequest
0 голосов
/ 09 января 2020
#include <iostream>

using namespace std;

int main()
{
    int *pn;
    char *pc;
    char c = 0x65;
    pn = (int *)&c;
    int n = *pn;
    *pn = 0x12345678;
    cout << "*pn: " << hex << *pn << dec << endl;
    cout << "n: " << hex << n << dec << endl;
    return 0;
}

Результат выполнения этой программы:

*pn: 12345678
n: 123456

env: я использую Windows 10 и Clion для написания программы.
Более озадачен:
1. это предложение int n = * pn; означает, что значение n равно * pn, а значение * pn равно 0x65, то есть значение n равно 0x65. После этого он не должен изменяться, почему * pn = 0x12345678; После этого значение n становится 0x123456?
2.При запуске int n = *pn; значение *pn становится de c 25957, что hex 0x6565, как это происходит? Вы можете увидеть это на рисунке 3!
Я надеюсь, что эксперты могут ответить на их сомнения, спасибо вам здесь!
Изображение отладки:
1
the first debug img
2
the second debug img
3
the third debug img
4
the fourth debug img

Ответы [ 3 ]

4 голосов
/ 09 января 2020

&c - это указатель на один байт памяти. Если преобразовать его в int*, вы меняете его на указатель (обычно) на 4 байта памяти, однако в памяти остается только один байт памяти. c переменная. Любое изменение, выполненное с помощью указателя pn, будет пытаться записать 4 байта памяти в c, это неопределенное поведение.

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

3 голосов
/ 09 января 2020
char c = 0x65;
pn = (int *)&c;
int n = *pn;

Вы переинтерпретируете char* как int*. Когда вы пытаетесь получить доступ к несуществующему int через переинтерпретированный (и потенциально смещенный) указатель, поведение программы не определено.

, но оно перезаписывается. Почему это происходит?

Это происходит потому, что поведение программы не определено.

значение * pn становится de c 25957, что в шестнадцатеричном виде 0x6565, как это происходит? это случилось?

Это происходит потому, что поведение программы не определено.

2 голосов
/ 09 января 2020

Ради аргумента скажем, что c занимает один байт в памяти. Также предположим, что int занимает 4 байта в памяти.

Выражение int * pn = (int *) &c; делает указатель на 4-байтовую память , указывающую на однобайтовую память. Пока проблем нет (указатели могут указывать куда угодно).

Оператор: *pn = 0x12345678; присваивает 4-байтовое значение одной ячейке памяти (помните, что c - это только один байт в памяти).

   +---+  
c: |   |  
   +---+  

     +---+---+---+---+  
pn-> |   |   |   |   |  
     +---+---+---+---+  

int требует больше места в памяти, чем вы выделили.

Изменение типа указателя не выделяет больше памяти.

Редактировать 1: возможная перезапись
Один из возможных сценариев - перезапись памяти.

      100   101   102   103  
     +-----+-----+-----+-----+  
c:   | 65  |     |     |     |  
pn-> |     |     |     |     |  
     +-----+-----+-----+-----+  

После *pn = 0x12345678;:

      100   101   102   103  
     +-----+-----+-----+-----+  
c:   | 12  | 34  | 56  | 78  |  
pn-> |     |     |     |     |  
     +-----+-----+-----+-----+  

Если перезапись не вызывает никаких исключений, вы не знаете, какие переменные расположены по адресам 101, 102 или 103. может перезаписывать другие переменные.
(приведенная выше схема предполагает макет с прямым порядком байтов).

Существуют другие сценарии ios (например, переменная c выделяется в конце памяти), поэтому вы будете писать после конца памяти.

Поведение не определено.

Редактировать 2: Чтение - неопределенное поведение
Как правило, память содержит случайные значения (значения из других программ, значения, которые не инициализированы).

Заданная память со случайными значениями (после инициализации переменной c):

      100   101   102   103  
     +-----+-----+-----+-----+  
c:   | 65  | ?F0 | ?0D | ?CD |  
pn-> |     |     |     |     |  
     +-----+-----+-----+-----+  

Разыменование указателя pn (как 4-байтового) количества приводит к 0x65F00DCD (Big Endian). Разыменование в платформах Little Endian: 0xCD0DF065. '?' представляет неинициализированное значение.

Это только один из возможных сценариев. Если c находится в последней адресуемой ячейке памяти, ваша программа может сгенерировать SEGFAULT для доступа к памяти за пределами границы.

Чтение несколько раз
Множество возможных сценариев ios , Другой сценарий заключается в том, что одно и то же местоположение читается несколько раз. Таким образом, на приведенных выше диаграммах разыменование pn (он же *pn) может привести к значению 0x65656565.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...