Важно отметить, что в памяти компьютера отсутствуют целые числа или указатели - только последовательность байтов. Это ваша программа, интерпретирующая эти байты как int
или int*
. Вне вашей программы это всего лишь байты
Давайте посмотрим на ваш пример шаг за шагом. Предположим, что для демонстрационных целей у вас есть память 256 байт и ваши int
s только 1 байт:
шаг 1: где вы объявляете и присвоение переменной
program:
int x = 1;
...
говорят, ваш компилятор решил выделить ячейку 12 памяти для переменной x
:
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |12:1| ... |255:X|
шаг 2: где вы объявляете указатель
program:
int x = 1;
int *p;
...
отзыв шаг 1 ... и составитель выделяется ячейка памяти 5 для переменной p
(примечание - p
еще не инициализирована, поэтому значение в ячейке 5 является X
!) :
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:X| .. |12:1| ... |255:X|
шаг 3: где вы назначаете указатель
program:
int x = 1;
int *p;
p = &x;
...
отзыв шаг 2 ... и теперь ячейка p
хранит указатель на x
. мы знаем, ячейка 12 была выделена для x
, следовательно, 12 хранится в p
(ячейки 5):
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12| .. |12:1| ... |255:X|
шаг 4: где вы объявляющего второй указатель
program:
int x = 1;
int *p;
p = &x;
int *p2;
...
отзыв этап 3 ...и компилятор выделил ячейку памяти 6 для переменной p2
(это Unassigned еще так значение X
):
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12|6:X| .. |12:1| ... |255:X|
шага 5: где вы назначаете второй (еще не определены!) pointee:
program:
int x = 1;
int *p;
p = &x;
int *p2;
*p2 = *p;
...
отзыв шаг 4, а затем *p
делает компилятор напомним, что p
является ячейка 5, так он генерирует запись для чтения из ячейки 5 (есть значение 12) и использует это значение в качестве адреса фактического результата: чтение из ячейки 12
возвращает 1. Теперь компилятор знает, что имя p2
было предоставлено ячейке # 6 p2
равно 6, но *p2
означает, что вам нужно прочитать значение p2 и использовать его как адрес фактического хранилища. Но в нашем случае это значение X
(undefined)! Однако в реальных микросхемах памяти нет неопределенных значений, и что-то там хранилось во время загрузки (скажем, значение 42
). Все становится более сложным, если затрагивается виртуальная память, но я не хочу обсуждать ее здесь. Итак, допустим, электрические заряды и космические лучи во время загрузки инициализировали эту ячейку случайным значением 42
. Посмотрим, что произойдет, когда будет выполнено *p2 = *p
: мы видели, как *p
вернул нам 1, теперь программа попытается найти, где ее хранить. он будет читать значение p2
, которое дает 42, теперь программа будет хранить 1
в ячейку памяти 42
и результат будет выглядеть:
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12|6:X| .. |12:1| ... |42:1| ... |255:X|
в реальном мире, однако, используя неинициализированное значение будет часто приводит к аварии. Когда в виртуальной памяти задействованы некоторые области адресного пространства (наши 256 адресов), у них не может быть выделенных ячеек памяти, и запись на такой адрес приведет к сбою.
шаг 6: где вы назначаете второй указатель и сделать его определить снова указывая на x
:
program:
int x = 1;
int *p;
p = &x;
int *p2;
p2 = p;
...
отзыв шаг 4 и игнорировать шаг 5, который вызвал вашу программу аварии ... теперь компилятор знает, какие ячейки используются как для p
(ячейка 5), так и для p2
(ячейка 6). поэтому назначение p2=p
работает следующим образом: принимает значение от p
(что равно 12), не интерпретируйте его как указатель, а просто скопируйте его на p2
. Теперь p2
инициализируется указателем на x
(ячейка 12).
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12|6:12| .. |12:1| ... |42:X| ... |255:X|
шаг 7: где вы присваиваете указатель на безымянном месте:
program:
int x = 1;
int *p;
p = &x;
int *p2;
p2 = (int*)100;
...
... давайте сосредоточимся на этой линии: p2 = (int*)100;
это показывает, как присвоить указатель, чтобы указать на неназванный ячейка памяти. Напомним, что компилятор еще не присвоил имя ячейке # 100. Но что, если вы точно знаете, что в этом месте памяти есть что-то интересное, например, компьютерное устройство общается с нами через эту ячейку. Что делать, если клавиатура хранит следующий символ в ячейке памяти # 100, и мы хотели бы ее прочитать?Теперь вы можете прочитать его как *p2
:
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12|6:100| .. |12:1| ... |42:X| ... |100:keyboard| ... |255:X|
шаг 8: где показательны отношение указателей на массивы:
program:
int x = 1;
int *p;
p = &x;
int a[5];
a[2]=18;
int *p2 = a;
p2[2]=3;
...
... давайте сосредоточимся на этой линии: int a[5];
: это строка объявления сообщает компилятору выделить 5 ячеек и использовать для них имя a
. И компилятор выделил ячейки 7, 8, 9, 10, 11 для a
:
memory |address:value| (value of X means unknown):
|0:X|1:X|2:X| ... |5:12|6:100|7:X|8:X|9:X|10:X|11:X|12:1| ... |42:X| ... |100:keyboard| ... |255:X|
отмечают, что он не выделяет указатель - a
будет использоваться буквально, как и любой другой переменной! Вот как работает a[2]=18
: первый компилятор знает, что начинается a
и 7
, поэтому он использует это значение как начало, затем он добавляет 2
, чтобы получить 9
. Таким образом, клетка-хранилище найдена - это ячейка номер 9, так что хранить 18 в эту ячейку:
|0:X|1:X|2:X| ... |5:12|6:100|7:X|8:X|9:18|10:X|11:X|12:1| ... |42:X| ... |100:keyboard| ... |255:X|
так давайте посмотрим, как int *p2 = a;
работы: компилятор знает, что a
начинается с 7. Следовательно 7 хранятся в p2
(напомнит, сот № 6 был выделен p2
на шаге 4)
|0:X|1:X|2:X| ... |5:12|6:7|7:X|8:X|9:18|10:X|11:X|12:1| ... |42:X| ... |100:keyboard| ... |255:X|
после инициализации указателя вы можете использовать его как массив p2[2]=3;
: это короткое для *(p2 + 2) = 3
. Значение p2
7, добавление 2 дает 9, поэтому мы храним 3 в ячейке # 9:
|0:X|1:X|2:X| ... |5:12|6:7|7:X|8:X|9:3|10:X|11:X|12:1| ... |42:X| ... |100:keyboard| ... |255:X|
Извините за столь долгого объяснения я попытался охватить отношения между значениями, переменных, указателей и массивов и наиболее для их использования.
Я думаю, мы не можем ответить на такой вопрос в нескольких параграфах. Проводите недели в чтении книг о программировании, языке C, операционной системе, компьютерной архитектуре, наборе инструкций и т. Д. –
'int * p = x;' не будет работать. это должно быть 'int * p = & x;'. общее правило - «тип» предмета слева должно быть таким же, как «тип» вещи справа. это относится ко всем назначениям и инициализациям не только тех, которые включают указатели – fukanchik
'int * p;' - указатель на целое число. То есть 'p' содержит * адрес * целого числа. Когда вы выполняете 'int * p = x;' вы присваиваете значение 'x' как * адрес * целого числа, что, вероятно, недействительно (если значение в' x' равно 1, то вы говорите адрес целого числа равен 1, что, вероятно, недействительно). Это должно быть 'int * p = & x;', что означает, что вы присваиваете значение 'p' * адресом * переменной' x'. – lurker