2016-04-22 5 views
1

Я пытался понять это на некоторое время.Каков правильный способ использования указателей. И как они действительно работают?

Из того, что я понимаю указатель объявлен как это:

int x =1; 
int* p; 

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

затем передать адрес й в р, так что она может указывать на его значение

p = &x 

Теперь все, что х равно также равно * р, и наоборот.

После этого поезда логики, если вы хотите, чтобы инициализировать указатель вы могли бы сделать так, как это:

int x = 1; 
int* p = x; 

Но это заблуждение меня. Теперь я понимаю, что значение p (на что он указывает) является значением x, 1, но не указывает на x фактически. Так как это работает? p не указывает на что-либо, но на что он указывает, равен 1? Я не понимаю.

Еще одна вещь, которую я хотел знать, - это то, как работает указатель = указатель?

Давайте посмотрим другой пример вместе с моим поездом логики:

int x = 1; 
int* p; 
int* p2; 
p = &x; 
*p2 = *p; 

Это переводится мне, как: заостренный значение при p2 равно значение указал на р, но не указывает на переменную, которая p указывает на (что есть x), просто значение x.

Может кто-нибудь прояснить это для меня? Неправильно ли мой логический ход? И если да, то как я должен смотреть на это?

+0

Я думаю, мы не можем ответить на такой вопрос в нескольких параграфах. Проводите недели в чтении книг о программировании, языке C, операционной системе, компьютерной архитектуре, наборе инструкций и т. Д. –

+0

'int * p = x;' не будет работать. это должно быть 'int * p = & x;'. общее правило - «тип» предмета слева должно быть таким же, как «тип» вещи справа. это относится ко всем назначениям и инициализациям не только тех, которые включают указатели – fukanchik

+1

'int * p;' - указатель на целое число. То есть 'p' содержит * адрес * целого числа. Когда вы выполняете 'int * p = x;' вы присваиваете значение 'x' как * адрес * целого числа, что, вероятно, недействительно (если значение в' x' равно 1, то вы говорите адрес целого числа равен 1, что, вероятно, недействительно). Это должно быть 'int * p = & x;', что означает, что вы присваиваете значение 'p' * адресом * переменной' x'. – lurker

ответ

2

Важно отметить, что в памяти компьютера отсутствуют целые числа или указатели - только последовательность байтов. Это ваша программа, интерпретирующая эти байты как 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| 

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

4

После

int x = 1; 

, чтобы получить указатель на x бы вы сказали

int * p = &x; 

и не

int * p = x; 

так что вы были правы, чтобы найти последнее заблуждение.

В вашем втором примере, когда вы говорите

*p2 = *p; 

, который будет взять вещь p очков (это значение 1, содержащееся в переменной x) и попытаться сохранить его там, где p2 точек. К сожалению, p2 не был инициализирован и не указывает на то, что полезно, поэтому результаты вряд ли будут чем угодно. Итак, опять же, если вы были смущены тем, что это будет делать, вы были бы правы, чтобы смутиться.

+0

, но не 'int * p = & x;' означает, что любое p, указывающее на, является адресом x? Как если бы я напечатал значение указателя, не напечатал бы адрес x, а не value? – Delupara

+1

Когда вы пишете 'int * p = & x',' int * '- это просто тип' p', это не означает, что в это время выполняется указатель 'p' (но позже если вы пишете '* p = 3;', это значит, что вы привыкнете к этому ...) –

+0

@GarethMcCaughan Итак, оператор «value at address» «*» не означает то же самое при инициализации? – Delupara

0

Я думаю, что это помогает запомнить, что указатели - это просто целые числа. Поэтому, когда вы создаете указатель, это действительно просто номер смещения в памяти. Тип указателя просто сообщает компилятору, как интерпретировать данные, когда указатель разыменован.Так что, когда вы делаете что-то вроде этого:

int *p = &x; 

Вы просто присваивая целое число в p, который является адресом памяти (смещение) от й. В некотором смысле, все указатели действительно имеют один и тот же «тип», либо 32 или 64 битные целые числа, в зависимости от архитектуры. Разыменование указатель, как это:

int y = *p; 

Просто означает «смещение в память р байт, и дать мне значение там в виде межд (который 4 байта)». Тип указателя просто сообщает компилятору, как читать данные из памяти после смещения.

+0

Это помогает, учитывая, что в прошлом я делал манипуляции с памятью, спасибо за это. – Delupara

+0

Одно очень важное предупреждение: C позволяет делать арифметику с использованием указателей, и для этой цели, если вы думаете о них как целые числа, представляющие машинные адреса, вы иногда получаете неправильный ответ. Зачем? Поскольку, например, если 'p' является указателем на' '', то 'p + 1' означает« один 'int дальше в памяти, чем' p' ", который на типичной машине означает, 's обычно 4 байта. (Хотя 2 байта и 8 байтов не являются неуслышанными.) –

+0

Аналогично, если 'p' и' q' являются указателями на '' '' (строго, они лучше также будут находиться в пределах одного массива или ' блок памяти malloc), тогда 'qp' измеряется в единицах размера' int', а не в байтах. Так, например, если вы говорите 'int x [10]; p = &x[1]; q = &x[3]; 'then' p' может быть 0x23006C, 'q' может быть 0x230074', а' q-p' будет 2 (а не 8). –

 Смежные вопросы

  • Нет связанных вопросов^_^