Недавно я имел удовольствие объяснять указатели новичку в программировании на C и наткнулся на следующую трудность. Это может показаться не проблемой, если вы уже знаете, как использовать указатели, но постарайтесь внимательно рассмотреть следующий пример:
int foo = 1;
int *bar = &foo;
printf("%p\n", (void *)&foo);
printf("%i\n", *bar);
Для абсолютного новичка результат может быть неожиданным. В строке 2 он / она только что объявил * bar как & foo, но в строке 4 оказалось, что * bar на самом деле foo, а не & foo!
Путаница, можно сказать, проистекает из двусмысленности символа *: в строке 2 он используется для объявления указателя. В строке 4 он используется как унарный оператор, который выбирает значение, на которое указывает указатель. Две разные вещи, правда?
Однако новичку это «объяснение» совершенно не помогает. Он вводит новую концепцию, указывая на небольшое несоответствие. Это не может быть правильным способом учить это.
Итак, как это объяснили Керниган и Ричи?
Унарный оператор * - это оператор косвенного обращения или разыменования; при применении к указателю он обращается к объекту, на который указывает указатель. […]
Объявление указателя ip
int *ip
задумано как мнемоника; он говорит, что выражение*ip
- это int. Синтаксис объявления переменной имитирует синтаксис выражений, в которых может присутствовать переменная .
int *ip
следует читать как " *ip
вернет int
"? Но почему тогда присвоение после объявления не следует этому шаблону? Что, если новичок захочет инициализировать переменную? int *ip = 1
(читать: *ip
вернет int
и int
есть 1
) не будет работать должным образом. Концептуальная модель просто не кажется последовательной. Я что-то упустил?
Изменить: он попытался обобщить ответы здесь .
*
в объявлении есть токен, означающий «объявить указатель», в выражениях - это оператор разыменования, и что эти два элемента представляют разные вещи, которые имеют один и тот же символ. (то же, что и оператор умножения - тот же символ, другое значение). Это сбивает с толку, но все, что отличается от реального положения дел, будет еще хуже.int* bar
, станет более очевидным, что звезда на самом деле является частью типа, а не идентификатора. Конечно, при этом возникают разные проблемы с такими не интуитивными вещами, какint* a, b
.*
может иметь два разных значения в зависимости от контекста. Точно так же, как одна и та же буква может произноситься по-разному, в зависимости от того, в каком слове она содержится, что затрудняет изучение многих языков. Если бы у каждой отдельной концепции / операции был свой собственный символ, нам понадобились бы гораздо большие клавиатуры, поэтому символы перерабатываются, когда это имеет смысл.int* p
), одновременно предостерегая учащегося от использования нескольких объявлений в одной строке, когда задействованы указатели. Когда студент полностью понял концепцию указателей, объясните ему, чтоint *p
синтаксис is эквивалентен, а затем объясните проблему с несколькими объявлениями.Ответы:
Чтобы ваш ученик мог понять значение
*
символа в разных контекстах, он должен сначала понять, что контексты действительно разные. Как только они поймут, что контексты различны (т. Е. Разница между левой частью задания и общим выражением), это не будет большим когнитивным скачком, чтобы понять, в чем заключаются различия.Сначала объясните, что объявление переменной не может содержать операторов (продемонстрируйте это, показав, что размещение символа
-
или+
в объявлении переменной просто вызывает ошибку). Затем покажите, что выражение (т. Е. В правой части присваивания) может содержать операторы. Убедитесь, что учащийся понимает, что выражение и объявление переменной - это два совершенно разных контекста.Когда они поймут, что контексты различны, вы можете продолжить объяснение, что когда
*
символ находится в объявлении переменной перед идентификатором переменной, это означает «объявить эту переменную как указатель». Затем вы можете объяснить, что при использовании в выражении (в качестве унарного оператора)*
символ является «оператором разыменования» и означает «значение по адресу», а не его предыдущее значение.Чтобы по-настоящему убедить вашего ученика, объясните, что создатели C могли использовать любой символ для обозначения оператора разыменования (т.е. они могли бы использовать
@
вместо него), но по какой-либо причине они приняли решение об использовании*
.В общем, невозможно объяснить, что контексты разные. Если ученик не понимает, что контексты разные, он не может понять, почему
*
символ может означать разные вещи.источник
Причина, по которой стенография:
int *bar = &foo;
в вашем примере может сбивать с толку то, что его легко неверно истолковать как эквивалент:
int *bar; *bar = &foo; // error: use of uninitialized pointer bar!
когда это на самом деле означает:
int *bar; bar = &foo;
Написанный таким образом, с разделением объявления и присваивания переменной, нет такой возможности для путаницы, и параллелизм использования ↔, описанный в вашей цитате K&R, работает отлично:
Первая строка объявляет переменную
bar
, так что*bar
этоint
.Вторая строка присваивает адрес
foo
дляbar
, делая*bar
(anint
) псевдонимом дляfoo
(также anint
).При знакомстве с синтаксисом указателя C новичкам может быть полезно изначально придерживаться этого стиля разделения объявлений указателя от назначений и вводить комбинированный сокращенный синтаксис (с соответствующими предупреждениями о его потенциальной путанице) только после того, как основные концепции использования указателя в C. были адекватно интернализованы.
источник
typedef
.typedef int *p_int;
означает, что переменная типаp_int
имеет свойство, которое*p_int
являетсяint
. Тогда у нас естьp_int bar = &foo;
. Поощрять кого-либо создавать неинициализированные данные, а затем назначать их по умолчанию, кажется ... плохой идеей.int a[2] = {47,11};
, что не является инициализация (несуществующей) элементаa[2]
eiher.*
должен быть частью типа, а не привязан к переменной, и тогда вы сможете написатьint* foo_ptr, bar_ptr
для объявления двух указателей. Но на самом деле он объявляет указатель и целое число.Коротко о декларациях
Приятно знать разницу между объявлением и инициализацией. Мы объявляем переменные как типы и инициализируем их значениями. Если мы делаем и то, и другое одновременно, мы часто называем это определением.
1.
int a; a = 42;
int a; a = 42;
Мы заявляем об
int
имени А . Затем мы инициализируем его, задавая ему значение42
.2.
int a = 42;
Мы заявляем и
int
назвали и дать ей значение 42. Это инициализируется . Определение.42
3.
a = 43;
Когда мы используем переменные, мы говорим, что оперируем ими.
a = 43
это операция присваивания. Присваиваем переменной a число 43.Говоря
int *bar;
мы объявляем bar указателем на int. Говоря
int *bar = &foo;
мы объявляем bar и инициализируем его адресом foo .
После того, как мы инициализировали bar, мы можем использовать тот же оператор, звездочку, для доступа и работы со значением foo . Без оператора мы получаем доступ и работаем с адресом, на который указывает указатель.
Кроме того, я позволил картинке говорить.
Какие
Упрощенное ВОСКРЕСЕНИЕ о том, что происходит. (А вот версия плеера, если хотите поставить на паузу и т. Д.)
источник
Второе утверждение
int *bar = &foo;
можно представить в виде наглядности в памяти как,bar foo +-----+ +-----+ |0x100| ---> | 1 | +-----+ +-----+ 0x200 0x100
Теперь
bar
указатель типа ,int
содержащий адрес&
изfoo
. Используя унарный оператор,*
мы предпочитаем извлекать значение, содержащееся в 'foo', с помощью указателяbar
.РЕДАКТИРОВАТЬ : Мой подход к новичкам - объяснить
memory address
переменную, т.е.Memory Address:
Каждая переменная имеет связанный с ней адрес, предоставляемый ОС. Вint a;
,&a
это адрес переменнойa
.Продолжайте объяснять основные типы переменных в
C
виде,Types of variables:
Переменные могут содержать значения соответствующих типов, но не адреса.int a = 10; float b = 10.8; char ch = 'c'; `a, b, c` are variables.
Introducing pointers:
Как сказано выше, переменные, напримерint a = 10; // a contains value 10 int b; b = &a; // ERROR
Назначение возможно,
b = a
но не возможноb = &a
, поскольку переменнаяb
может содержать значение, но не адрес. Следовательно, нам нужны указатели .Pointer or Pointer variables :
Если переменная содержит адрес, она называется переменной-указателем. Используйте*
в объявлении, чтобы сообщить, что это указатель.• Pointer can hold address but not value • Pointer contains the address of an existing variable. • Pointer points to an existing variable
источник
int *ip
«ip is a pointer (*) of type int» возникают проблемы при чтении чего-то вродеx = (int) *ip
.x = (int) *ip;
, получить значение путем разыменования указателяip
и привести значение кint
любому типуip
.int* bar = &foo;
делает нагрузки больше смысла. Да, я знаю, что это вызывает проблемы, когда вы объявляете несколько указателей в одном объявлении. Нет, я не думаю, что это вообще имеет значение.Глядя на ответы и комментарии здесь, кажется, есть общее согласие с тем, что рассматриваемый синтаксис может сбивать с толку новичка. Большинство из них предлагают что-то в этом роде:
Вы можете написать
int* bar
вместо,int *bar
чтобы подчеркнуть разницу. Это означает, что вы не будете следовать подходу K&R «использование имитации декларации», но подходу Stroustrup C ++ :Мы не объявляем
*bar
целое число. Мы заявляем,bar
что мыint*
. Если мы хотим инициализировать вновь созданную переменную в той же строке, ясно, что мы имеем делоbar
, а не*bar
.int* bar = &foo;
Недостатки:
int* foo, bar
vsint *foo, *bar
).Изменить: был предложен другой подход - использовать «имитацию» K&R, но без «сокращенного» синтаксиса (см. Здесь ). Как только вы пропустите объявление и присваивание в одной строке , все будет выглядеть намного более связным.
Однако рано или поздно ученику придется иметь дело с указателями как с аргументами функции. И указатели как возвращаемые типы. И указатели на функции. Вам нужно будет объяснить разницу между
int *func();
иint (*func)();
. Думаю, рано или поздно все развалится. И, может быть, лучше раньше, чем позже.источник
Есть причина, по которой стиль K&R отдает предпочтение
int *p
стилю Страуструпаint* p
; оба действительны (и означают одно и то же) на каждом языке, но, как выразился Страуструп:Теперь, поскольку вы пытаетесь обучить здесь Си, это наводит на мысль, что вам следует уделять больше внимания выражениям, чем типам, но некоторые люди могут легче понять одно ударение быстрее, чем другое, и это касается них, а не языка.
Поэтому некоторым людям будет легче начать с идеи, что an
int*
- это нечто иное, чем an,int
и двигаться дальше.Если кто - то быстро обращали внимание на дорогу смотреть на него , что использует ,
int* bar
чтобы иметьbar
как вещь , которая не является INT, а указательint
, то они быстро поймут , что*bar
это делают что - то дляbar
, а остальное приложится. После того, как вы сделали , что вы можете позже объяснить , почему C кодеры , как правило, предпочитаютint *bar
.Или не. Если бы был один способ, которым все сначала поняли концепцию, у вас не было бы никаких проблем, и лучший способ объяснить это одному человеку не обязательно будет лучшим способом объяснить это другому.
источник
int* p = &a
то можемint* r = *p
. Я почти уверен, что он освещал это в книге «Дизайн и эволюция C ++» , но я давно не читал ее и по глупости отдал кому-то свой экземпляр.int& r = *p
. И держу пари, что заемщик все еще пытается переварить книгу.Var A, B: ^Integer;
дает понять, что тип «указатель на целое число» применяется как к, такA
и кB
. ИспользованиеK&R
стиляint *a, *b
также возможно; но заявление , какint* a,b;
, впрочем, выглядит , как будтоa
иb
оба объявленных какint*
, но в действительности он объявляетa
какint*
иb
какint
.tl; dr:
A: не надо. Объясните указатели новичку и покажите им, как затем представить их концепции указателей в синтаксисе C.
ИМО, синтаксис C не ужасен, но и не прекрасен: это не большая помеха, если вы уже понимаете указатели, и не помощь в их изучении.
Поэтому: начните с объяснения указателей и убедитесь, что они действительно их понимают:
Объясните их с помощью диаграмм в виде прямоугольников и стрелок. Вы можете сделать это без шестнадцатеричных адресов, если они не актуальны, просто покажите стрелки, указывающие либо на другое поле, либо на какой-либо символ нуля.
Объясните с помощью псевдокода: просто напишите адрес foo и значение, хранящееся в bar .
Затем, когда ваш новичок поймет, что такое указатели, почему и как их использовать; затем покажите отображение на синтаксис C.
Я подозреваю, что причина того, что текст K&R не предоставляет концептуальной модели, заключается в том, что они уже понимали указатели и, вероятно, предполагали, что это понимал и любой другой компетентный программист в то время. Мнемоника - это просто напоминание о преобразовании хорошо понятной концепции в синтаксис.
источник
Эта проблема несколько сбивает с толку, когда вы начинаете изучать C.
Вот основные принципы, которые могут помочь вам начать работу:
В C всего несколько основных типов:
char
: целочисленное значение размером 1 байт.short
: целочисленное значение размером 2 байта.long
: целочисленное значение размером 4 байта.long long
: целочисленное значение размером 8 байт.float
: нецелое значение размером 4 байта.double
: нецелое значение размером 8 байт.Обратите внимание, что размер каждого типа обычно определяется компилятором, а не стандартом.
Целочисленные типы
short
,long
иlong long
, как правило , следуютint
.Однако это не обязательно, и вы можете использовать их без
int
.В качестве альтернативы вы можете просто указать
int
, но это может быть по-разному интерпретировано разными компиляторами.Итак, чтобы резюмировать это:
short
то же самое,short int
но не обязательно то же самое, что иint
.long
то же самое,long int
но не обязательно то же самое, что иint
.long long
то же самое,long long int
но не обязательно то же самое, что иint
.В данном компиляторе
int
это либоshort int
или,long int
либоlong long int
.Если вы объявляете переменную какого-то типа, вы также можете объявить другую переменную, указывающую на нее.
Например:
int a;
int* b = &a;
Таким образом, по сути, для каждого базового типа у нас также есть соответствующий тип указателя.
Например:
short
иshort*
.Есть два способа "взглянуть" на переменную
b
(что, вероятно, смущает большинство новичков) :Вы можете рассматривать
b
как переменную типаint*
.Вы можете рассматривать
*b
как переменную типаint
.Следовательно, одни люди будут заявлять
int* b
, тогда как другие заявляютint *b
.Но в том-то и дело, что эти два объявления идентичны (пробелы не имеют смысла).
Вы можете использовать либо
b
как указатель на целочисленное значение, либо*b
как фактическое указанное целочисленное значение.Вы можете получить (прочитать) заостренное значение:
int c = *b
.И вы можете установить (запись) заостренное значение:
*b = 5
.Указатель может указывать на любой адрес памяти, а не только на адрес некоторой переменной, которую вы ранее объявили. Однако вы должны быть осторожны при использовании указателей, чтобы получить или установить значение, расположенное по указанному адресу памяти.
Например:
int* a = (int*)0x8000000;
Здесь у нас есть переменная,
a
указывающая на адрес памяти 0x8000000.Если этот адрес памяти не отображается в пространстве памяти вашей программы, то любая операция чтения или записи, использующая
*a
, скорее всего, приведет к сбою вашей программы из-за нарушения доступа к памяти.Вы можете безопасно изменять значение
a
, но вы должны быть очень осторожны при изменении значения*a
.Тип
void*
является исключительным в том смысле, что он не имеет соответствующего «типа значения», который можно использовать (т. Е. Вы не можете объявитьvoid a
). Этот тип используется только как общий указатель на адрес памяти, без указания типа данных, которые находятся в этом адресе.источник
Возможно, пройдя через это еще немного, станет легче:
#include <stdio.h> int main() { int foo = 1; int *bar = &foo; printf("%i\n", foo); printf("%p\n", &foo); printf("%p\n", (void *)&foo); printf("%p\n", &bar); printf("%p\n", bar); printf("%i\n", *bar); return 0; }
Попросите их сказать вам, что они ожидают от каждой строки, а затем пусть они запустят программу и посмотрят, что получится. Объясните их вопросы (голая версия наверняка подскажет некоторые, но о стиле, строгости и переносимости вы можете побеспокоиться позже). Затем, прежде чем их разум превратится в кашу от чрезмерных размышлений или они превратятся в зомби после обеда, напишите функцию, которая принимает значение, и ту же функцию, которая принимает указатель.
По моему опыту, можно преодолеть вопрос «почему это печатается именно так?» hump, а затем сразу же показывая, почему это полезно в параметрах функций, на практике (в качестве прелюдии к некоторым базовым материалам K&R, таким как синтаксический анализ строк / обработка массивов), что делает урок не просто осмысленным, но и закрепленным.
Следующий шаг - попросить их объяснить вам, как
i[0]
относится к&i
. Если они могут это сделать, они не забудут об этом, и вы можете начать говорить о структурах, даже немного раньше времени, просто чтобы это стало понятным.Вышеупомянутые рекомендации относительно прямоугольников и стрелок тоже хороши, но они также могут привести к полному обсуждению того, как работает память - это разговор, который должен произойти в какой-то момент, но может отвлечь от основной темы. : как интерпретировать обозначение указателя в C.
источник
int foo = 1;
. Теперь это нормально:int *bar; *bar = foo;
. Это не нормально:int *bar = foo;
Тип выражения
*bar
являетсяint
; таким образом, тип переменной (и выражения)bar
равенint *
. Поскольку переменная имеет тип указателя, ее инициализатор также должен иметь тип указателя.Существует несоответствие между инициализацией и назначением переменной-указателя; это просто то, что нужно усвоить на собственном горьком опыте.
источник
Я бы предпочел читать это как первое,
*
относящееся кint
более чемbar
.int foo = 1; // foo is an integer (int) with the value 1 int* bar = &foo; // bar is a pointer on an integer (int*). it points on foo. // bar value is foo address // *bar value is foo value = 1 printf("%p\n", &foo); // print the address of foo printf("%p\n", bar); // print the address of foo printf("%i\n", foo); // print foo value printf("%i\n", *bar); // print foo value
источник
int* a, b
не делает то, что думают.int* a,b
вообще следует использовать. Для лучшей наглядности, обновления и т. Д. В каждой строке должно быть только одно объявление переменной и никогда больше. Новичкам тоже есть что объяснять, даже если компилятор справится с этим.*
как о типе и просто отговариватьint* a, b
. Если вы не предпочитаете говорить, что*a
это тип,int
а неa
указатель наint
...int *a, b;
не следует использовать. Объявление двух переменных с разными типами в одном операторе - довольно плохая практика и хороший кандидат для проблем с обслуживанием в будущем. Возможно, для тех из нас, кто работает во встраиваемой области, все по-другому, где anint*
и anint
часто имеют разные размеры и иногда хранятся в совершенно разных местах памяти. Это один из многих аспектов языка C, который лучше всего преподавать как «это разрешено, но не делай этого».int *bar = &foo;
Question 1
: Что естьbar
?Ans
: Это переменная-указатель (на типint
). Указатель должен указывать на какое-то допустимое место в памяти, а позже его следует разыменовать (* bar) с помощью унарного оператора*
, чтобы прочитать значение, хранящееся в этом месте.Question 2
: Что есть&foo
?Ans
: foo - это переменная типа.,int
которая хранится в некоторой допустимой области памяти, и это место мы получаем от оператора,&
поэтому теперь у нас есть некоторая допустимая область памяти&foo
.Итак, оба собраны вместе, то есть то, что нужно указателю, было действительным местом в памяти, и оно получено,
&foo
поэтому инициализация хорошая.Теперь указатель
bar
указывает на допустимое место в памяти, и значение, хранящееся в нем, можно получить, разыменовав его, т.е.*bar
источник
Вы должны указать новичку, что * имеет разное значение в объявлении и выражении. Как вы знаете, * в выражении является унарным оператором, а * в объявлении - не оператором, а просто своего рода синтаксисом, сочетающимся с типом, чтобы компилятор знал, что это тип указателя. лучше сказать новичку: «* имеет другое значение. Чтобы понять значение *, вы должны найти, где * используется»
источник
Я думаю, что дьявол в космосе.
Я бы написал (не только для новичка, но и для себя): int * bar = & foo; вместо int * bar = & foo;
это должно показать, какова взаимосвязь между синтаксисом и семантикой
источник
Уже отмечалось, что * имеет несколько ролей.
Есть еще одна простая идея, которая может помочь новичку понять суть:
Подумайте, что "=" также имеет несколько ролей.
Когда присваивание используется в одной строке с объявлением, считайте это вызовом конструктора, а не произвольным присваиванием.
Когда ты видишь:
int *bar = &foo;
Думаю, это почти эквивалентно:
int *bar(&foo);
Круглые скобки имеют приоритет над звездочкой, поэтому «& foo» гораздо легче интуитивно отнести к «bar», чем к «* bar».
источник
Я видел этот вопрос несколько дней назад, а затем случайно прочитал объяснение объявления типа Go в блоге Go . Он начинается с описания объявлений типа C, которые кажутся полезным ресурсом для добавления в этот поток, хотя я думаю, что уже даны более полные ответы.
(Далее описывается, как распространить это понимание на указатели функций и т. Д.)
Это способ, о котором я раньше не думал, но он кажется довольно простым способом учета перегрузки синтаксиса.
источник
Если проблема в синтаксисе, может быть полезно показать эквивалентный код с помощью шаблона / using.
template<typename T> using ptr = T*;
Затем это можно использовать как
ptr<int> bar = &foo;
После этого сравните нормальный синтаксис / C с подходом только для C ++. Это также полезно для объяснения константных указателей.
источник
Источник путаницы проистекает из того факта, что
*
символ может иметь разные значения в C, в зависимости от факта, в котором он используется. Чтобы объяснить указатель новичку,*
следует пояснить значение символа в другом контексте.В декларации
int *bar = &foo;
*
символ не оператор разыменования . Вместо этого он помогает указать типbar
информирования компилятора о том, чтоbar
это указатель на файлint
. С другой стороны, когда он появляется в операторе,*
символ (при использовании в качестве унарного оператора ) выполняет косвенное обращение. Следовательно, заявлениебыло бы неправильно, поскольку он присваивает адрес
foo
объекту, на которыйbar
указывает, а не самомуbar
себе.источник
"возможно, если написать его как int * bar, станет более очевидным, что звезда на самом деле является частью типа, а не идентификатора". Так и делаю. И я говорю, что это что-то вроде Type, но только для одного имени указателя.
«Конечно, вы столкнетесь с разными проблемами, связанными с такими неинтуитивными вещами, как int * a, b».
источник
Здесь вы должны использовать, понимать и объяснять логику компилятора, а не человеческую логику (я знаю, вы человек, но здесь вы должны имитировать компьютер ...).
Когда ты пишешь
int *bar = &foo;
компилятор группирует, как
{ int * } bar = &foo;
То есть: вот новая переменная, ее имя
bar
, тип - указатель на int и ее начальное значение&foo
.И вы должны добавить: в
=
выше , означает инициализацию не притворство, а в следующих выражениях*bar = 2;
она является притворствомРедактировать за комментарий:
Осторожно: в случае многократного объявления это
*
относится только к следующей переменной:int *bar = &foo, b = 2;
bar - это указатель на int, инициализированный адресом foo, b - это int, инициализированный значением 2, а в
int *bar=&foo, **p = &bar;
bar в неподвижном указателе на int, а p - указатель на указатель на int, инициализированный адресом или строкой.
источник
int* a, b;
объявляет a как указатель наint
, а b какint
. Этот*
символ имеет два различных значения: в объявлении он указывает тип указателя, а в выражении - это унарный оператор разыменования.*
in rattached к типу, так что указатель инициализируется, тогда как в процессе воздействия затрагивается указанное значение. Но, по крайней мере, ты дал мне хорошую шляпу :-)По сути, указатель не является индикатором массива. Новичку легко понять, что указатель выглядит как массив. большинство строковых примеров с использованием
"char * pstr" похоже, похоже
"char str [80]"
Но, что важно, указатель обрабатывается как просто целое число на нижнем уровне компилятора.
Посмотрим примеры:
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv, char **env) { char str[] = "This is Pointer examples!"; // if we assume str[] is located in 0x80001000 address char *pstr0 = str; // or this will be using with // or char *pstr1 = &str[0]; unsigned int straddr = (unsigned int)pstr0; printf("Pointer examples: pstr0 = %08x\n", pstr0); printf("Pointer examples: &str[0] = %08x\n", &str[0]); printf("Pointer examples: str = %08x\n", str); printf("Pointer examples: straddr = %08x\n", straddr); printf("Pointer examples: str[0] = %c\n", str[0]); return 0; }
Результатам будет это 0x2a6b7ed0 - адрес str []
~/work/test_c_code$ ./testptr Pointer examples: pstr0 = 2a6b7ed0 Pointer examples: &str[0] = 2a6b7ed0 Pointer examples: str = 2a6b7ed0 Pointer examples: straddr = 2a6b7ed0 Pointer examples: str[0] = T
Итак, в принципе, имейте в виду, что указатель - это своего рода целое число. представление Обращения.
источник
Я бы объяснил, что целые числа являются объектами, как и числа с плавающей запятой и т. Д. Указатель - это тип объекта, значение которого представляет адрес в памяти (поэтому указатель по умолчанию имеет значение NULL).
Когда вы впервые объявляете указатель, вы используете синтаксис имени указателя типа. Он читается как «целочисленный указатель с именем, которое может указывать на адрес любого целочисленного объекта». Мы используем этот синтаксис только во время удаления, аналогично тому, как мы объявляем int как int num1, но мы используем только num1, когда мы хотим использовать эту переменную, а не int num1.
int x = 5; // целочисленный объект со значением 5
int * ptr; // целое число со значением NULL по умолчанию
Чтобы указатель указывал на адрес объекта, мы используем символ «&», который можно прочитать как «адрес».
ptr = & x; // теперь значение - это адрес 'x'
Поскольку указатель - это только адрес объекта, чтобы получить фактическое значение, хранящееся по этому адресу, мы должны использовать символ «*», который при использовании перед указателем означает «значение по адресу, на который указывает».
std :: cout << * ptr; // распечатать значение по адресу
Вы можете кратко объяснить, что " " - это "оператор", который возвращает разные результаты с разными типами объектов. При использовании с указателем оператор ' ' больше не означает "умножить на".
Это помогает нарисовать диаграмму, показывающую, как переменная имеет имя и значение, а указатель имеет адрес (имя) и значение, и показывает, что значением указателя будет адрес типа int.
источник
Указатель - это просто переменная, используемая для хранения адресов.
Память в компьютере состоит из байтов (байт состоит из 8 бит), расположенных последовательно. Каждый байт имеет номер, связанный с ним так же, как индекс или индекс в массиве, который называется адресом байта. Адрес байта начинается с 0 на единицу меньше размера памяти. Например, скажем, в 64 МБ ОЗУ 64 * 2 ^ 20 = 67108864 байта. Следовательно, адрес этих байтов будет начинаться с 0 до 67108863.
Посмотрим, что произойдет, когда вы объявите переменную.
int mark;
Как мы знаем, int занимает 4 байта данных (при условии, что мы используем 32-разрядный компилятор), поэтому компилятор резервирует 4 последовательных байта из памяти для хранения целочисленного значения. Адрес первого байта из 4 выделенных байтов известен как адрес меток переменных. Предположим, что адрес четырех последовательных байтов - 5004, 5005, 5006 и 5007, тогда адрес переменных меток будет 5004.
Объявление переменных-указателей
Как уже было сказано, указатель - это переменная, в которой хранится адрес памяти. Как и любые другие переменные, вам нужно сначала объявить переменную-указатель, прежде чем вы сможете ее использовать. Вот как вы можете объявить переменную-указатель.
Синтаксис:
data_type *pointer_name;
data_type - это тип указателя (также известный как базовый тип указателя). pointer_name - это имя переменной, которое может быть любым допустимым идентификатором C.
Возьмем несколько примеров:
int *ip; float *fp;
int * ip означает, что ip - это переменная-указатель, способная указывать на переменные типа int. Другими словами, указатель на переменную ip может хранить адрес переменных только типа int. Точно так же указатель переменной fp может хранить только адрес переменной типа float. Тип переменной (также известный как базовый тип) ip - указатель на int, а тип fp - указатель на float. Переменная-указатель типа указатель на int может быть символически представлена как (int *). Точно так же указатель переменной типа указатель на float может быть представлен как (float *)
После объявления переменной-указателя следующим шагом является присвоение ей действительного адреса памяти. Вы никогда не должны использовать переменную-указатель без присвоения ей действительного адреса памяти, потому что сразу после объявления она содержит значение мусора и может указывать на любое место в памяти. Использование неназначенного указателя может дать непредсказуемый результат. Это может даже привести к сбою программы.
int *ip, i = 10; float *fp, f = 12.2; ip = &i; fp = &f;
Источник: thecguru - это, безусловно, самое простое, но подробное объяснение, которое я когда-либо находил.
источник