C указатели: Указатель в языке Си : обозначение и использование
Содержание
Указатели на указатели — многочисленное перенаправление
Концепция массивов указателей открыта и проста, поскольку индексы имеют вполне определенное значение. Тем не менее, в случае, когда один указатель указывает на другой, могут возникнуть проблемы. Указатель на указатель является формой многочисленного перенаправления или цепочки указателей. Рассмотрим рис.
В случае обычных указателей, указатель содержит адрес некоторого участка памяти, содержащего некоторое значение. В случае указателя на указатель, первый указатель содержит адрес второго, который в свою очередь содержит адрес участка памяти, содержащего некоторое значение.
Многочисленное перенаправление может и дальше расширяться. Но существует немного случаев, когда необходимо что-то более мощное, чем указатель на указатель. Излишнее перенаправленние приводит к концептуальным ошибкам, которые очень трудно исправлять. (Не надо путать многочисленное перенаправление со связанными списками, которые используются в базах данных.)
Переменная, являющаяся указателем на указателе должна быть описана следующим образом. Это выполняется путем помещения двух звездочек перед именем. Например, следующее объявление сообщается компилятору, что newbalance — это указатель на указатель типа float:
float **newbalance;
Важно понимать, что newbalance — это не указатель на число с плавающей точкой, а указатель на указатель на вещественное число.
Для получения доступа к целевому значению, косвенно указываемому указателем на указатель, следует применить оператор * два раза, как показано в следующем примере:
#include <stdio.h>
int main(void)
{
int x, *p, **q;
x = 10;
p = &x;
q = &p;
printf («%d», **q) ; /* вывод значения x */
return 0;
}
Здесь p объявляется как указатель на целое, a q — это указатель на указатель на целое. Вызов printf() выводит число 10 на экран.
C Урок 26. Указатели и адреса. Часть 1 |
 
 
 
На данном уроке мы рассмотрим интересную тему. Это указатели и адреса.
На данной теме очень много начинающих программистов впадают в ступор, особенно когда видят звёздочки и амерсанды, которые для этого используются в языке C.
Вернее, мы, конечно же, данную тему не рассмотрим. Мы её только легонько коснёмся. Рассмотреть в рамках одного урока такую серьёзную тему не получится. По ней будет как минимум 4 урока. Мы лишь проведём сегодня первое знакомство. Но это будет самое главное. Так как понимание указателей и адресов начинается именно здесь.
Надеюсь, если вы попали сюда, то вы решили освоить программирование на языке, что говорит о том, что с информатикой вы уже знакомы и преподавать её здесь я, конечно же, не буду ибо это огромная наука, что вы уже знаете, как организована архитектура компьютера, что такое память физическая, виртуальная, адресная шина, магистраль, процессор и т.д.
Напомню то, что память, предоставляемая программе или процессу операционной системой, имеет адресацию совершенно не ту, какую этот предоставленный участок занимает физически. То есть адреса совершенно другие и не факт, что кажущийся непрерывным участок памяти в виртуальном адресном пространстве будет также непрерывен в физическом. Он может быть фрагментирован. Но это нам лишь облегчает работу, так как нам не нужно озадачиваться тем, каким образом организована память физически. Поэтому, если я буду рассказывать здесь о памяти, то буду иметь в виду именно виртуальную, имеющую те адреса, которые мы видим в среде программирования в отладке.
Также, думаю, практически все в курсе, что у меня есть некоторое количество уроков по приобретающим всё большую популярность микроконтроллерам. Вот там как раз мы работаем напрямую с памятью физической и в отладке видим именно её. Но это не важно. Урок данный будет вполне актуален и для МК. Нам же абсолютно нет дела до того, какую память мы видим. А видим мы именно ту память, с которой мы работаем.
Организована память, предоставленная программе, примерно вот таким образом
У каждой ячейки памяти существует адрес.
Почему адреса следуют друг за другом не подряд, а пропускаются по 4 байта?
Можно конечно показать и подряд, но я показал именно с учётом того, что мы пишем приложение под 32-разрядную систему. Хотя у нас практически у большинства установлены операционные системы 64-разрядные, но приложения, написанные под 32-битные системы, там прекрасно работают. У нас даже компилятор mingw предназначен для 32-разрядных систем. Пока мы будем писать с расчётом именно на 32-разрядные системы, так как, во-первых, они легче для понимания, во-вторых, из соображений совместимости, так как 32-рязрядные системы пока ещё существуют и хочется, чтобы наша программа запускалась и прекрасно работала и на них. Также у нас получается то, что наш урок актуален и для 32-разрядных МК, например для тех же stm32.
Так вот к чему я это всё?
А к тому, что считаю, что удобнее работать также с 32-битными ячейками памяти и зачастую к ним идёт приравнивание.
Вообще в одной такой ячейке получается по 4 ячейки 8-битных. Например, если мы объявляем переменную типа char и присваиваем ей какое-то значение, то это значение займёт только такую ячейку.
Ну что ж. Представим, что мы объявили переменную типа int, которая скорей всего занимает в памяти 32 бита (но не факт, это иногда проверять надо), назвав её, например a. Затем присвоили ей какое-то значение. В данном случае нам операционная система выделит какую-то ячейку памяти, ну пусть, например вот эту
Теперь у нашей переменной появился адрес, так как операционная система предоставила ей определённую ячейку памяти. Операционная система не знает имя этой переменной, оно ей не нужно, даже и процессор при выполнении программы не знает имени переменной, он работает с её значением и обращается к ней именно по адресу. А как же мы можем узнать адрес этой переменной?
Мы конечно же зададимся вопросом. А зачем нам адрес переменной? Мы же знаем её имя и в процессе написания программы мы прекрасно можем обратиться к нашей переменной по имени. Скажу лишь, что есть такие ситуации, когда нам требуется именно адрес переменной, так как, например, если мы передали значение переменной в другую функцию в качестве параметра, то у нас в данной функции создастся копия этой переменной и мы будем работать с ней, поэтому, если мы вдруг решим изменить значение нашей переменной, то мы не сможем этого сделать, имя её в другой функции не видно, то есть переменная не попала в область видимости. Но если мы как-то передадим адрес, то мы сможем уже работать с реальной переменной. И это лишь одна ситуация, таких очень много.
Чтобы нам как-то запомнить адрес нашей переменной, то есть тот, который в нашем случае 0x0061FF14, то мы можем создать для нашей переменной указатель.
Для этого мы можем заранее объявить переменную-указатель типа int. Указатель такого типа будет указывать на переменные именно такого типа.
Объявляется переменная-указатель какого-либо типа данных вот таким образом
Объявление указателя очень похоже на объявление обычной переменной, только тут появляется наша пресловутая звёздочка, которая почему-то всех путает. А почему именно, да потому что ставится она возле имени переменной, а после типа данных ставится пробел. Можно, конечно, поступить и наоборот, поставить эту звёздочку возле типа данных, потом поставить пробел и лишь потом имя. Всё работать будет точно так же и это было бы правильнее для понимания, так как звёздочка относится именно к типу данных. Именно тип данных вместе со звёздочкой и есть тип переменной-указателя. Но существует соглашение, согласно которому звёздочка ставится перед именем. Поэтому мы просто должны чётко понимать, что тип данных вместе со звёздочкой – это то, что мы объявили переменную-указатель, а её имя – это только имя указателя. Потом ещё будет причина непонимания смысла указателей, когда мы встретим звёздочку немного в другом случае. Но это чуть позже.
Как это всё будет выглядеть практически?
Сначала давайте объявим нашу обычную переменную a и присвоим ей какое-нибудь значение. Пусть она будет даже беззнаковая
unsigned int a;
a = 0xFE340056;
А теперь мы объявим переменную-указатель такого же типа
unsigned int *p_a;
Имя нашей переменной p_a. Я специально дал такое имя, чтобы показать то, что указатель будет у нас хранить адрес переменной a. Пока мы этот указатель только объявили, мы ему не присваивали никаких адресов. Это обычная переменная, которой также выделена ячейка памяти, но она пока не хранит никакой информации, пока там какое-то случайное значение.
Вообщем, в памяти у нас пока примерно вот такая картина
То есть, у нас есть переменная типа указателя на unsigned int, которая пока не хранит практически ничего, а переменная a имеет уже значение, хранящееся по определённому адресу.
Как же присвоить адрес нашей переменной a нашему указателю p_a?
А делается это вот таким вот образом.
Существует специальная операция, называемая операцией взятия адреса.
Оператор данной операции выглядит в виде амперсанда
Данная операция является унарной, используется в выражениях и оператор амперсанд ставится перед переменной, адрес которой мы хотим получить
Здесь тоже присутствует некая путаница. Кто-то путает данный оператор с точно таким же оператором побитового И, ровно как звёздочку изредка расценивают как операцию умножения.
Давайте теперь присвоим адрес нашей переменной a переменной-указателю p_a
p_a = &a;
Здесь мы больше никаких звёздочек не используем. Звёздочка была нужна только при объявлении переменной-указателя. Теперь мы с ней работаем, как с обычной переменной и присваиваем ей адрес другой переменной, используя операцию взятия адреса.
Вот теперь переменная p_a хранит в себе адрес переменной a
Получается, что p_a знает теперь адрес a. То есть если, как в жизни, у нас есть такой человек, который знает адрес другого человека.
И теперь у нас наступила такая ситуация. Если нам потребовался адрес другого человека, то как мы его можем узнать у того человека, который этот адрес знает.
Ну конечно же любой ответит: Да как! Спросить у него надо!
Ну это в жизни. А здесь как? Да вообще элементарно. У нас p_a его и хранит, он является его значением. Просто присваиваем значение этого p_a любым переменным.
Тогда будет другой вопрос. А можем ли мы узнать у p_a не адрес, а значение нашей переменной a, адрес которой он хранит? Ну это типа спросить у человека, знающего адрес человека: А не знаешь ли ты, сколько лет другому человеку?
Ответ: мы это также можем сделать. Для этого мы должны произвести операцию разыменования указателя p_a.
Для разыменования у нас существует вот такой оператор
Да автор просто над нами прикалывается! – скажете вы.
Нет, не прикалывается. Опять эта звёздочка!
Но здесь она уже служит унарным оператором разыменования указателя, который используется вот таким образом
Используется также в выражениях, но не только. Использование такой конструкции возможно перед оператором присваивания. Но об этом чуть позже.
Как же отличить звёздочку, обозначающую указатель, от звёздочки, являющейся оператором разыменования указателя? А очень просто. Во втором случае перед звёздочкой не будет типа данных.
Поэтому, чтобы получить значение нашей переменной a, используя её указатель, мы можем сделать, например вот так
unsigned int b = *p_a;
Теперь переменная b получит значение переменной a, которое мы взяли у указателя на эту переменную, применив операцию разыменования.
С помощью разыменования мы можем также и изменить значение переменной a, используя указатель p_a, который хранит её адрес
*p_a = 0xE2222222;
Может также возникнет вопрос. Если указатель – это также переменная, которая тоже имеет в памяти свой адрес. Она же находится в памяти и занимает ячейку. А можем ли мы создать и на неё указатель?
Ну конечно же можем!
Указатель на указатель мы можем объявить с помощью двух звёздочек, вот таким вот образом
unsigned int **p_p_a;
Наш новый указатель теперь тоже получит своё место в памяти
Только этот указатель пока ни на что не показывает, то есть он пока не хранит никаких адресов.
И теперь мы вот таким образом присваиваем адрес указателя p_a, который указывает на переменную a, нашему новому указателю
p_p_a = &p_a;
Получится теперь у нас вот такая картина в памяти
То есть теперь p_p_a хранит адрес p_a, который в свою очередь хранит адрес a.
Вообщем какой-то человек знает адрес другого человека, который в свою очередь знает адрес третьего человека.
Ну и тогда резонный вопрос: а можем ли мы у этого человека узнать возраст третьего человека, ведь он знает адрес другого человека, который знает адрес третьего человека и который может спросить у третьего человека его возраст и сказать первому человеку?
Ну конечно можем. И причём в нашем случае программирования на языке C всё гораздо проще.
Мы также используем две звёздочки в операции разыменования
unsigned int b = **p_p_a;
У нас получилось как бы двойное разыменование. Мы разыменовали адрес указателя p_a, получив его значение, являющееся адресом a и второй звёздочкой разыменовали и этот адрес, получив значение уже самой переменной a.
Вот так. Теперь, надеюсь, к вам немного пришло понимание физики адресов. У адресов существует также ещё и арифметика, но об этом в другом уроке.
Теперь давайте немного поговорим о массивах.
Например существует у нас массив данных типа unsigned char, мы его объявили и сразу проинициализировали
unsigned char uch[10] = {0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC};
Данный массив также занял в памяти какое-то место и у каждого элемента массива теперь есть свои адреса в памяти
Теперь нам удобнее смотреть в памяти все адреса, а не каждый четвёртый, так как у нас массив такого типа. Причём мы можем получить указатель на каждый его элемент, но нам это не нужно пока.
А как же получить указатель на массив, ну, то есть на то место с которого он начинается, на первый его элемент, вернее, если быть точным, на нулевой?
А, в принципе, его получать-то особо не всегда надо, так как имя массива и есть указатель на массив. Вот такая интересная получается история.
Но если нам вдруг нужно назначить указатель на массив другой переменной, то мы этот также можем прекрасно сделать, например, вот так.
unsigned char *p_uch = &uch[0];
Мы в данном случае получаем адрес его нулевого элемента и присваиваем его переменной-указателю такого же типа, как и наш массив.
Теперь переменная p_uch хранит в себе адрес массива, то есть является указателем на массив uch
Здесь конечно же возникнуть может ещё один резонный вопрос. Почему указатель на наш массив занял в памяти 4 ячейки, а, следовательно, и 4 байта?
А потому, что указатель хоть и является указателем на тип unsigned char, но сама переменная-указатель всегда будет 32-битной, так как по-другому и не получится. Каким образом мы в 8 битах можем хранить число 0x0061FF11? Да никаким, вот поэтому и 4 ячейки.
Причём, наш указатель на массив, который мы только что создали, мы также можем использовать как имя нашего же массива. Если мы, например обратимся с помощью выражения p_uch[5], то мы получим имя 5 элемента массива uch. Это конечно уже тема не данного урока, а больше арифметика указателей, но тем не менее мы можем это делать.
В следующей части нашего урока мы отработаем все изученные в данной части урока приёмы с указателями и адресами на практике.
Предыдущий урок Программирование на C Следующая часть
Смотреть ВИДЕОУРОК (нажмите на картинку)
Post Views:
6 471
Урок #9 — Указатели и ссылки
В уроке мы с вами изучим указатели, а также ссылки в языке C++. Благодаря указателям, а также ссылкам мы можем передавать данные через код без особой нагрузки на процессор.
Каждая переменная или объект хранит данные в определенной ячейке в памяти компьютера. Каждый раз, создавая новую переменную, мы создаем новую ячейку в памяти, с новым значением для неё. Чем больше ячеек, тем больше компьютерной памяти будет занято.
Адрес в памяти компьютера это число, к которому мы можем получить доступ. Указатель — это тот же адрес в памяти, по которому мы получаем переменную и по итогу её значение.
Чтобы работать с указателями необходимо воспользоваться двумя специальными символами: &
и *
. Пример использования:
int t = 237; // Простая переменная
int *p; // Создание указателя, который принимает лишь адрес другой переменной
p = &t; // Устанавливаем адрес нашей первой переменной
Переменные t
и p
будут равны числу 237, так как оба ссылаются на одну ячейку. Сам же компьютер на вычислении обеих переменных потратит меньше усилий, ведь обе переменные ссылаются на одно и то же.
Ссылки в C++
Ссылки и указатели схожи между собой, так как оба в качестве значения имеют лишь адрес некого объекта.
Указатель хранит адрес ячейки и если мы захотим изменить значение этой ячейки, то нам придется выполнить операцию «разыменования»:
float some = 391; // Простая переменная
float *u = &some; // Указатель на переменную
*u = 98; // Изменение значения переменной
В ссылках такого понятия нет, так как меняя ссылку вы автоматически меняете и переменную. Ссылки напрямую ссылаются к переменной, поэтому их синтаксис проще:
char symbol = 'A'; // Простая переменная
char &ref = symbol; // Создание ссылки на переменную
// Поскольку мы ссылаемся на переменную, то можем её использовать
// как отдельно взятую переменную
cout
Использование ссылок и указателей оправдано в случае передачи данных в функции или же в объекты. Также данные технологии отлично подойдут для передачи большого объема данных в ходе программы.
C++ — Указатели
C ++ указатели легко и интересно учиться. Некоторые задачи C ++ легче выполняются с указателями, а другие задачи C ++, такие как распределение динамической памяти, не могут выполняться без них.
Как вы знаете, каждая переменная является местом памяти, и каждая ячейка памяти имеет свой адрес, который можно получить, используя оператор ampersand (&), который обозначает адрес в памяти. Рассмотрим следующее, которое будет печатать адрес определенных переменных —
#include <iostream>
using namespace std;
int main () {
int var1;
char var2[10];
cout << "Address of var1 variable: ";
cout << &var1 << endl;
cout << "Address of var2 variable: ";
cout << &var2 << endl;
return 0;
}
Когда приведенный выше код компилируется и выполняется, он производит следующий результат:
Address of var1 variable: 0xbfebd5c0
Address of var2 variable: 0xbfebd5b6
Что такое указатели?
Указатель является переменной, значение которого является адрес другой переменной. Как и любая переменная или константа, вы должны объявить указатель, прежде чем сможете с ним работать. Общая форма объявления переменной указателя —
type *var-name;
Здесь тип — это базовый тип указателя; он должен быть допустимым типом C ++, а var-name — это имя переменной-указателя. Звездочкой, которую вы использовали для объявления указателя, является та же самая звездочка, которую вы используете для умножения. Однако в этом утверждении звездочка используется для обозначения переменной как указателя. Ниже приведена действительная декларация указателя —
int *ip; // pointer to an integer
double *dp; // pointer to a double
float *fp; // pointer to a float
char *ch // pointer to character
Фактический тип данных для всех указателей, будь то целое число, float, character или other, является тем же самым, длинным шестнадцатеричным числом, которое представляет адрес памяти. Единственное различие между указателями разных типов данных — это тип данных переменной или константы, на которые указывает указатель.
Использование указателей в C ++
Существует несколько важных операций, которые мы будем делать с указателями очень часто. (a) Мы определяем переменную указателя. (b)Назначьте адрес переменной указателю. (c) Наконец, получите доступ к значению по адресу, доступному в переменной указателя. Это делается с помощью унарного оператора *, который возвращает значение переменной, расположенную по адресу, указанному его операндом. В следующем примере используются эти операции —
#include <iostream>
using namespace std;
int main () {
int var = 20; // actual variable declaration.
int *ip; // pointer variable
ip = &var; // store address of var in pointer variable
cout << "Value of var variable: ";
cout << var << endl;
// print the address stored in ip pointer variable
cout << "Address stored in ip variable: ";
cout << ip << endl;
// access the value at the address available in pointer
cout << "Value of *ip variable: ";
cout << *ip << endl;
return 0;
}
Когда приведенный выше код скомпилирован и исполнен, он производит результат следующим образом:
Value of var variable: 20
Address stored in ip variable: 0xbfc601ac
Value of *ip variable: 20
Указатели в C ++
У указателей есть много, но простых понятий, и они очень важны для программирования на С ++. Существует несколько важных понятий указателей, которые должны быть понятны программисту на C ++ —
Нулевые указатели
C ++ поддерживает нулевой указатель, который является константой со значением нуля, определенным в нескольких стандартных библиотеках.
Арифметика указателей
Существует четыре арифметических оператора, которые могут использоваться для указателей: ++, -, +, —
Указатели против массивов
Существует тесная связь между указателями и массивами.
Массив указателей
Вы можете определить массивы для хранения нескольких указателей.
Указатель на указатель
C ++ позволяет иметь указатель на указатель и так далее.
Передача указателей на функции
Передача аргумента по ссылке или по адресу позволяет включить переданный аргумент, который будет изменен вызывающей функцией вызываемой функцией.
Возвращаемый указатель из функций
C ++ позволяет функции возвращать указатель на локальную переменную, статическую переменную и динамически распределенную память.
Объявление указателя
— cppreference.com
Указатель — это тип объекта, который ссылается на функцию или объект другого типа, возможно с добавлением квалификаторов. Указатель также может ни на что не ссылаться, на что указывает специальное значение нулевого указателя.
[править] Синтаксис
В грамматике объявления объявления указателя последовательность спецификатора типа обозначает указываемый тип (который может быть типом функции или объекта и может быть неполным), а декларатор имеет форму:
* attr-spec-seq (необязательно) квалификаторы (необязательно) декларатор | |||||||||
где декларатор может быть идентификатором, который называет объявляемый указатель, включая другой декларатор указателя (который будет указывать указатель на указатель):
float * p, ** pp; // p - указатель на float // pp - указатель на указатель на float интервал (* fp) (интервал); // fp - указатель на функцию с типом int (int)
Квалификаторы, которые появляются между *
и идентификатором (или другим вложенным декларатором), определяют тип объявляемого указателя:
int n; const int * pc = & n; // pc - неконстантный указатель на const int // * pc = 2; // Ошибка: n нельзя изменить через pc без приведения pc = NULL; // ОК: сам компьютер можно изменить int * const cp = & n; // cp - константный указатель на неконстантный int * cp = 2; // ОК, чтобы изменить n через cp // cp = NULL; // Ошибка: сам cp не может быть изменен int * const * pcp = & cp; // неконстантный указатель на константный указатель на неконстантный int
attr-spec-seq (C23) — это необязательный список атрибутов, применяемых к объявленному указателю.
[править] Объяснение
Указатели используются для косвенного обращения, что является повсеместным методом программирования; их можно использовать для реализации семантики передачи по ссылке, для доступа к объектам с динамической продолжительностью хранения, для реализации «необязательных» типов (с использованием значения нулевого указателя), отношения агрегации между структурами, обратных вызовов (с использованием указателей на функции), общих интерфейсов (используя указатели на void) и многое другое.
[править] Указатели на объекты
Указатель на объект может быть инициализирован результатом оператора адресации, примененного к выражению типа объекта (которое может быть неполным):
int n; int * np = & n; // указатель на int int * const * npp = & np; // неконстантный указатель на константный указатель на неконстантный int int a [2]; int (* ap) [2] = & a; // указатель на массив int struct S {int n; } s = {1} int * sp = & s.n; // указатель на int, который является членом s
Указатели могут появляться как операнды для оператора косвенного обращения (унарный *
), который возвращает lvalue, идентифицирующий указанный объект:
int n; int * p = & n; // указатель p указывает на n * р = 7; // сохраняет 7 в n printf ("% d \ n", * p); // преобразование lvalue-to-rvalue считывает значение из n
Указатели на объекты типа структуры и объединения могут также отображаться как левые операнды доступа к члену через оператор указателя ->
.
Из-за неявного преобразования массива в указатель указатель на первый элемент массива может быть инициализирован выражением типа массива:
int a [2]; int * p = a; // указатель на [0] int b [3] [3]; int (* строка) [3] = b; // указатель на b [0]
Определенные операторы сложения, вычитания, составного присваивания, приращения и декремента определены для указателей на элементы массивов.
Операторы сравнения определены для указателей на объекты в некоторых ситуациях: два указателя, которые представляют один и тот же адрес, сравнивают равные, два значения нулевого указателя сравнивают равные, указатели на элементы одного и того же массива сравнивают так же, как индексы массива этих элементов, и указатели на члены структуры сравниваются в порядке объявления этих членов.
Многие реализации также обеспечивают строгий общий порядок указателей случайного происхождения, например если они реализованы как адреса в непрерывном («плоском») виртуальном адресном пространстве.
[править] Указатели на функции
Указатель на функцию может быть инициализирован адресом функции. Из-за преобразования функции в указатель оператор адресации не является обязательным:
void f (int); void (* pf1) (int) = & f; void (* pf2) (int) = f; // то же, что и & f
В отличие от функций, указатели на функции являются объектами и поэтому могут храниться в массивах, копироваться, присваиваться, передаваться другим функциям в качестве аргументов и т. Д.
Указатель на функцию может использоваться в левой части оператора вызова функции; это вызывает указанную функцию:
#includeint f (int n) { printf ("% d \ n", n); вернуть n * n; } int main (пусто) { int (* p) (int) = f; интервал х = р (7); }
Разыменование указателя функции дает указатель функции для указанной функции:
int f (); int (* p) () = f; // указатель p указывает на f (*п)(); // функция f вызывается через указатель функции п(); // функция f вызывается напрямую через указатель
Операторы сравнения равенства определены для указателей на функции (они сравнивают равные, если указывают на одну и ту же функцию).
Поскольку совместимость типов функций игнорирует квалификаторы верхнего уровня параметров функции, указатели на функции, параметры которых отличаются только квалификаторами верхнего уровня, являются взаимозаменяемыми:
int f (интервал), fc (константный интервал); int (* pc) (константа int) = f; // ХОРОШО int (* p) (интервал) = fc; // ХОРОШО pc = p; // ОК
[править] Указатели на аннулирование
Указатель на объект любого типа может быть неявно преобразован в указатель на void (необязательно const или изменяемый), и наоборот:
int n = 1, * p = & n; void * pv = p; // int * в void * int * p2 = pv; // от void * до int * printf ("% d \ n", * p2); // выводит 1
Указатели на void используются для передачи объектов неизвестного типа, что является обычным явлением в универсальных интерфейсах: malloc возвращает void *, qsort ожидает предоставленный пользователем обратный вызов, который принимает два аргумента const void *.pthread_create ожидает предоставленный пользователем обратный вызов, который принимает и возвращает void *. Во всех случаях вызывающая сторона обязана преобразовать указатель в правильный тип перед использованием.
[править] Нулевые указатели
Указатели каждого типа имеют специальное значение, известное как значение нулевого указателя этого типа. Указатель, значение которого равно null, не указывает на объект или функцию (разыменование нулевого указателя является неопределенным поведением) и сравнивает равные со всеми указателями того же типа, значение которых также равно null .
Чтобы инициализировать указатель равным нулю или присвоить нулевое значение существующему указателю, может использоваться константа нулевого указателя (NULL или любая другая целочисленная константа с нулевым значением). статическая инициализация также инициализирует указатели их нулевыми значениями.
Нулевые указатели могут указывать на отсутствие объекта или могут использоваться для обозначения других типов ошибок. В общем, функция, которая получает аргумент указателя, почти всегда должна проверять, является ли значение нулевым, и обрабатывать этот случай по-другому (например, free ничего не делает, когда передается нулевой указатель).
[править] Примечания
Хотя любой указатель на объект может быть приведен к указателю на объект другого типа, разыменование указателя на тип, отличный от объявленного типа объекта, почти всегда является неопределенным поведением. См. Подробные сведения о строгом псевдониме.
Можно указать функции, которая обращается к объектам через указатели, что эти указатели не являются псевдонимами. Подробности см. В разделе restrict. | (начиная с C99) |
lvalue-выражения типа массива при использовании в большинстве контекстов подвергаются неявному преобразованию в указатель на первый элемент массива.См. Подробности в массиве.
char * str = "abc"; // "abc" - массив char [4], str - указатель на 'a'
Указатели на char часто используются для представления строк. Чтобы представить допустимую байтовую строку, указатель должен указывать на char, который является элементом массива char, и должен быть char со значением ноль по некоторому индексу, большему или равному индексу элемента, на который ссылается указатель.
[править] Ссылки
Стандарт
- C17 (ISO / IEC 9899: 2018):
- 6.7.6.1 Деклараторы указателей (стр. 93-94)
- стандарт C11 (ISO / IEC 9899: 2011):
- 6.7.6.1 Деклараторы указателя (p: 130)
- Стандарт
- C99 (ISO / IEC 9899: 1999):
- 6.7.5.1 Деклараторы указателя (стр: 115-116)
- Стандарт C89 / C90 (ISO / IEC 9899: 1990):
- 3.5.4.1 Деклараторы указателя
[править] См. Также
упражнений по программированию на C: указатель — w3resource
C Pointer [22 упражнения с решением]
1. Напишите программу на C, чтобы показать базовое объявление указателя. Перейти в редактор
Ожидаемый результат :
Pointer: Показать базовое объявление указателя: -------------------------------------------------- ----- Здесь m = 10, n и o - две целые переменные, а * z - целое число. z хранит адрес m = 0x7ffd40630d44 * z сохраняет значение m = 10 & m - это адрес m = 0x7ffd40630d44 & n сохраняет адрес n = 0x7ffd40630d48 & o сохраняет адрес o = 0x7ffd40630d4c & z сохраняет адрес z = 0x7ffd40630d50
Щелкните меня, чтобы увидеть решение
2. Напишите программу на языке C, чтобы продемонстрировать, как работать с указателями в программе. Перейти в редактор
Ожидаемый результат :
Адрес м: 0x7ffcc3ad291c Стоимость м: 29 Теперь ab присваивается адрес m. Адрес указателя ab: 0x7ffcc3ad291c Содержимое указателя ab: 29 Теперь значение m присвоено 34.Адрес указателя ab: 0x7ffcc3ad291c Содержимое указателя ab: 34 Переменной-указателю ab теперь присвоено значение 7. Адрес м: 0x7ffcc3ad291c Стоимость м: 7
Щелкните меня, чтобы увидеть решение
3. Напишите программу на языке C, чтобы продемонстрировать использование операторов & (адрес) и * (значение в адресе). Перейти в редактор
Ожидаемый результат :
Указатель: Продемонстрируйте использование операторов & и *: -------------------------------------------------- ------ м = 300 fx = 300.600006 cht = z Использование оператора &: ----------------------- адрес m = 0x7ffda2eeeec8 адрес fx = 0x7ffda2eeeecc адрес cht = 0x7ffda2eeeec7 Использование оператора & и *: ----------------------------- значение по адресу m = 300 значение по адресу fx = 300.600006 значение по адресу cht = z Использование только переменной-указателя: ---------------------------------- адрес m = 0x7ffda2eeeec8 адрес fx = 0x7ffda2eeeecc адрес cht = 0x7ffda2eeeec7 Использование только оператора указателя: ---------------------------------- значение по адресу m = 300 значение по адресу fx = 300.600006 значение по адресу cht = z
Щелкните меня, чтобы увидеть решение
4. Напишите программу на языке C для сложения двух чисел с помощью указателей. Перейдите в редактор
Test Data:
Введите первое число: 5
Введите второе число: 6
Ожидаемый результат :
Сумма введенных чисел: 11
Щелкните меня, чтобы увидеть решение
5. Напишите программу на языке C для добавления номеров с помощью вызова по ссылке. Перейдите в редактор
Test Data:
Введите первое число: 5
Введите второе число: 6
Ожидаемый результат :
Сумма 5 и 6 равна 11
Щелкните меня, чтобы увидеть решение
6. Напишите программу на языке C, чтобы найти максимальное число между двумя числами с помощью указателя. Перейдите в редактор
Test Data:
Введите первое число: 5
Введите второе число: 6
Ожидаемый результат :
6 - максимальное число.
Щелкните меня, чтобы увидеть решение
7. Напишите программу на языке C для хранения n элементов в массиве и печати элементов с помощью указателя. Перейдите в редактор
Test Data:
Введите количество элементов для хранения в массиве: 5
Введите 5 количество элементов в массиве:
элемент — 0: 5
элемент — 1: 7
элемент — 2: 2
элемент — 3: 9
элемент — 4: 8
Ожидаемый результат :
Введенные вами элементы: элемент - 0: 5 элемент - 1: 7 элемент - 2: 2 элемент - 3: 9 элемент - 4: 8
Щелкните меня, чтобы увидеть решение
8. Напишите программу на языке C для печати всех перестановок заданной строки с помощью указателей. Перейти в редактор
Ожидаемый результат :
Перестановки строки следующие: abcd abdc acbd acdb adcb adbc bacd badc bcad bcda bdca bdac cbad cbda cabd cadb cdab cdba db ca dbac dcba dcab dacb dabc
Щелкните меня, чтобы увидеть решение
9. Напишите программу на языке C, чтобы найти самый большой элемент, используя динамическое распределение памяти.Перейти в редактор
Тестовые данные:
Введите общее количество элементов (от 1 до 100): 5
Число 1: 5
Число 2: 7
Число 3: 2
Число 4: 9
Число 5: 8
Ожидается Выход :
Самый большой элемент: 9.00
Щелкните меня, чтобы увидеть решение
10. Напишите программу на языке C для вычисления длины строки с помощью указателя. Перейдите в редактор
Test Data:
Введите строку: w3resource
Ожидаемый результат :
Длина данной строки w3resource составляет: 10
Щелкните меня, чтобы увидеть решение
11. Напишите программу на языке C для обмена элементами с помощью вызова по ссылке. Перейти в редактор
Тестовые данные:
Введите значение 1-го элемента: 5
Введите значение 2-го элемента: 6
Введите значение 3-го элемента: 7
Ожидаемый результат :
Значения до замены: элемент 1 = 5 элемент 2 = 6 элемент 3 = 7 Значения после обмена: элемент 1 = 7 элемент 2 = 5 элемент 3 = 6
Щелкните меня, чтобы увидеть решение
12. Напишите программу на языке C, чтобы найти факториал заданного числа с помощью указателей. Перейдите в редактор
Test Data:
Введите число: 5
Ожидаемый результат :
Факториал 5: 120
Щелкните меня, чтобы увидеть решение
13. Напишите программу на языке C для подсчета количества гласных и согласных в строке с помощью указателя. Перейдите в редактор
Test Data:
Введите строку: string
Ожидаемый результат :
Количество гласных: 1 Число констант: 5
Щелкните меня, чтобы увидеть решение
14. Напишите программу на языке C для сортировки массива с помощью указателя. Перейти в редактор
Test Data:
testdata
Ожидаемый результат :
Тестовые данные:
Введите количество элементов для хранения в массиве: 5
Введите 5 количество элементов в массиве:
элемент — 1: 25
элемент — 2: 45
элемент — 3: 89
элемент — 4:15 Элемент
— 5: 82
Ожидаемый результат :
Элементы в массиве после сортировки: элемент - 1:15 элемент - 2: 25 элемент - 3:45 стихия - 4: 82 элемент - 5: 89
Щелкните меня, чтобы увидеть решение
15. Напишите программу на C, чтобы показать, как функция возвращает указатель. Перейдите в редактор
Test Data:
Введите первое число: 5
Введите второе число: 6
Ожидаемый результат :
Число 6 больше.
Щелкните меня, чтобы увидеть решение
16. Напишите программу на языке C для вычисления суммы всех элементов в массиве с помощью указателей. Перейдите в редактор
Test Data:
Введите количество элементов для хранения в массиве (максимум 10): 5
Введите 5 количество элементов в массиве:
элемент — 1: 2
элемент — 2: 3
элемент — 3: 4
элемент — 4: 5
элемент — 5: 6
Ожидаемый результат :
Сумма массива: 20
Щелкните меня, чтобы увидеть решение
17. Напишите программу на языке C для печати элементов массива в обратном порядке. Перейдите в редактор
Test Data:
Введите количество элементов для хранения в массиве (максимум 15): 5
Введите 5 количество элементов в массиве:
элемент — 1: 2
элемент — 2: 3
элемент — 3: 4
элемент — 4: 5
элемент — 5: 6
Ожидаемый результат :
Элементы массива в обратном порядке: элемент - 5: 6 элемент - 4: 5 элемент - 3: 4 элемент - 2: 3 элемент - 1: 2
Щелкните меня, чтобы увидеть решение
18. Напишите программу на языке C, чтобы показать использование указателя на структуру. Перейти в редактор
Ожидаемый результат :
Джон Альтер с Корт-стрит
Щелкните меня, чтобы увидеть решение
19. Напишите программу на C, чтобы показать указатель на объединение. Перейти в редактор
Ожидаемый результат :
Джон Мак Джон Мак
Щелкните меня, чтобы увидеть решение
20. Напишите программу на языке C, чтобы показать указатель на массив, содержимое которого является указателем на структуру.Перейти в редактор
Ожидаемый результат :
Имя бывшего сотрудника: Алекс ID сотрудника: 1002
Щелкните меня, чтобы увидеть решение
21. Напишите программу на языке C для печати всех алфавитов с помощью указателя. Перейти в редактор
Ожидаемый результат :
Алфавиты: АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ
Щелкните меня, чтобы увидеть решение
22. Напишите программу на языке C для печати строки в обратном порядке с помощью указателя. Перейдите в редактор
Test Data:
Введите строку: w3resource
Ожидаемый результат :
Указатель: напечатайте строку в обратном порядке: ------------------------------------------------ Введите строку: w3resource Реверс строки: ecruoser3w
Щелкните меня, чтобы увидеть решение
Редактор кода программирования C:
Еще больше впереди!
Не отправляйте здесь какие-либо решения вышеуказанных упражнений, если вы хотите внести свой вклад, перейдите на соответствующую страницу упражнений.
Фундаментальный C — начальные указатели
Стр. 1 из 3
Этот отрывок из моей книги по программированию C в контексте Интернета вещей углубляется в указатели. Тема, которую можно расширять почти вечно, но с чего-то нужно начинать.
Фундаментальный C: приближение к машине
Теперь доступен в мягкой обложке и в электронной книге на Amazon.
- О C
Извлечь зависимый v Независимый
и неопределенное поведение - Начало работы с C с использованием NetBeans
- Управляющие структуры и данные
- Переменные
Извлечь переменные - Арифметика и представление
Извлечь Арифметика и представление - Операторы и выражение
Извлечение побочных эффектов, точек последовательности и отложенного вычисления
Первый черновик главы: Низкие данные - Объем функций и срок службы
- Массивы
Извлечь простые массивы
Извлечь перечисления - Строки
Извлечь простые строки - Указатели
Извлечение начальных указателей
Извлечение указателей, преобразование типов и типов - Структуры
Извлечь основные структуры
Извлечь Typedef - Обработка битов
Извлечение базовых битов *** НОВИНКА
Извлечение сдвигает и поворачивает - Файлы
Извлечь файлы Компиляция C — препроцессор, компилятор, компоновщик
Компиляция извлечения и препроцессор
См. Также сопутствующий том: Применение C
Указатели — это конструкция, которая делает C таким полезным для низкоуровневого и системного программирования.Они позволяют делать то, что языки высокого уровня обычно запрещают или затрудняют. В некотором смысле это указатель, который позволяет вам использовать C вместо того, чтобы прибегать к машинно-зависимому ассемблеру. Конечно, такая мощь влечет за собой большую ответственность за то, чтобы не вывести из строя вашу программу или даже всю машину. Вероятно, именно указатель и его неправильное или неосторожное использование заставляет считать C «опасным» языком.
В этой главе мы рассмотрим указатель C и его связь с моделью памяти C.
Объявление указателя — оператор разыменования
Вы, вероятно, использовали переменные с тех пор, как научились программировать, и вы, вероятно, думаете о них как о некоем «ящике», в котором может храниться значение. То есть, когда вы пишете:
int myVar = 123;
вы думаете, что myVar в некотором смысле «содержит» или хранит значение 123.
На самом деле происходит то, что объявление типа int заставляет компилятор выделить два (или более) байта и сохранить в нем двоичный эквивалент 123.С этого момента метка myVar является синонимом адреса выделенной области памяти — как объяснялось в главе 4, переменная является постоянным указателем.
Это тонкий сдвиг в понимании. Для хранения адреса переменной не выделяется память, она просто вставляется в машинный код как часть инструкции. Когда вы пишете:
int myVar; myVar = 0;
компилятор выдает ассемблер:
основной + 14: movl $ 0x0,0xc (% esp)
Это перемещает нулевое значение в память на 0xC относительно текущего кадра стека.Вы можете видеть, что myVar заменяется вне программы за счет использования ячейки памяти 0xC в кадре стека.
Стандартные переменные подходят для большинства задач, но иногда вам нужно обращаться к области памяти, которая изменяется в ходе выполнения программы. Для этого вам понадобится указатель.
Указатель — это переменная, в которой хранится адрес ячейки памяти, и она отличается от переменной тем, что для хранения адреса выделено некоторое хранилище.
В C вы используете * или оператор разыменования, чтобы указать, что переменная является указателем.
Например:
int * myPointer;
объявляет myPointer как указатель на int. Обратите внимание, что этим объявлением не создается int. Создается область памяти, в которой может храниться адрес типа int, а myPointer — это метка, дающая адрес этой области. То есть myPointer — это переменная, в которой хранится адрес типа int.
Это означает, что все указатели занимают одинаковый объем памяти независимо от того, на что они указывают, поскольку адрес почти всегда занимает одинаковое количество бит.
Почему тогда мы должны утверждать, что myPointer является указателем на int, а не на что-то еще?
В конце концов, myPointer просто будет хранить адрес, а все адреса являются одним и тем же и занимают одинаковое пространство для хранения. Причина в том, что компилятор должен знать, что делать с памятью, на которую указывает указатель.
Точнее, оператор разыменования обращается к той памяти, на которую указывает указатель. То есть:
myPointer
— это указатель на int и:
* myPointer
— это фактический int, на который указывает myPointer.
После разыменования указателя вы можете использовать его, как если бы это был тип, являющийся целью указателя.
Например:
* myPointer = 42;
хранит 42 как целое число в ячейке памяти, адресованной myPointer.
Прежде чем вы поспешите попробовать — это не работает, потому что myPointer, как мы его определили, может быть указателем, но на самом деле он не указывает на какую-либо полезную память. Объявление указателя создает неинициализированный указатель, точно так же, как объявление любой переменной объявляет неинициализированную переменную.
Итак, как мы можем инициализировать указатель?
Основы работы с указателями
Этот документ знакомит с основами указателей, поскольку они работают на нескольких компьютерных языках — C, C ++, Java и Pascal. Этот документ является сопутствующим документом для
Pointer Fun с цифровым видео Binky или его можно использовать отдельно.
Это документ 106 в Стэнфордской образовательной библиотеке CS. Этот и другие бесплатные материалы доступны в cslibrary.stanford.edu. Некоторые документы, связанные с этим, включают …
Раздел 1 — Правила указателей
Одна из приятных особенностей указателей заключается в том, что правила, регулирующие их работу, довольно просты. Правила можно сгруппировать вместе, чтобы получить сложные результаты, но отдельные правила остаются простыми.
1) Указатели и указатели
Указатель хранит ссылку на что-то. К сожалению, не существует фиксированного термина для того, на что указывает указатель, и на разных компьютерных языках существует множество вещей, на которые указывают указатели.Мы используем термин указатель для того, на что указывает указатель, и придерживаемся основных свойств отношения указатель / указатель, которые истинны для всех языков. Термин «ссылка» означает почти то же самое, что и «указатель» — «ссылка» подразумевает обсуждение более высокого уровня, в то время как «указатель» подразумевает традиционную реализацию на компилируемом языке указателей как адресов. Для основных правил указателя / указателя, описанных здесь, термины фактически эквивалентны.
На приведенном выше рисунке показан указатель с именем x
, указывающий на указатель, который хранит значение 42.Указатель обычно изображается в виде прямоугольника, а сохраненная в нем ссылка — в виде стрелки, начинающейся в поле и ведущей к его указателю.
Выделение указателя и выделение указателя, на который он будет указывать, — это два отдельных шага. Вы можете думать о структуре указателя / указателя как о работе на двух уровнях. Оба уровня должны быть настроены, чтобы все работало. Самая распространенная ошибка — это сосредоточение внимания на написании кода, который манипулирует уровнем указателя, но при этом забывают установить уровень указателя.Иногда операции с указателями, которые не касаются указателей, называются «мелкими», а операции с указателями — «глубокими».
2) Разыменование
Операция разыменования начинается с указателя и следует по его стрелке, чтобы получить доступ к своему указателю. Целью может быть просмотр состояния указателя или изменение состояния указателя.
Операция разыменования указателя работает только в том случае, если указатель имеет указатель — указатель должен быть выделен, а указатель должен быть установлен так, чтобы указывать на него.Самая распространенная ошибка в коде указателя — это забыть настроить указатель. Наиболее частый сбой во время выполнения из-за этой ошибки в коде — это неудачная операция разыменования. В Java неправильное разыменование будет вежливо помечено системой выполнения. В скомпилированных языках, таких как C, C ++ и Pascal, неправильное разыменование иногда приводит к сбою, а в других случаях к повреждению памяти каким-то незаметным случайным образом. По этой причине может быть сложно отследить ошибки указателя в скомпилированных языках.
3) Назначение указателя
Назначение указателя между двумя указателями заставляет их указывать на один и тот же указатель.Итак, присвоение y = x;
делает y
точкой к той же точке, что и x
. Назначение указателя не касается указателей. Он просто изменяет один указатель, чтобы он имел ту же ссылку, что и другой указатель. Говорят, что после назначения указателя два указателя «совместно используют» указатель.
Раздел 2 — Пример кода Бинки
В этом разделе представлен тот же пример кода, который используется в
Pointer Fun с видео Binky. Есть версии кода на нескольких компьютерных языках.Все версии имеют одинаковую структуру и демонстрируют одни и те же основные правила и уроки по указателям; они просто различаются по синтаксису. Независимо от какого-либо конкретного языка, основная структура примера …
Предположим, у вас есть указатель типа «Узел», который содержит две вещи: int и указатель на другой узел (объявление для такого типа узла приведено ниже). С таким типом указателя вы можете расположить три указателя узла в структуре, где они указывали бы друг на друга вот так…
Указатель с именем x
указывает на первый указатель узла. Первый узел содержит указатель на второй, второй содержит указатель на третий, а третий содержит указатель на первый. Эту структуру можно построить, используя только те правила выделения, разыменования и присваивания, которые мы видели. Используя приведенное ниже объявление, каждый узел содержит целое число с именем , значение
и указатель на другой узел с именем следующий
.
Напишите код для построения конструкции на рисунке выше. Для удобства вы можете использовать временные указатели в дополнение к x
. Единственный требуемый новый синтаксис состоит в том, что в C оператор ->
разыменовывает указатель для доступа к полю в указателе — поэтому -> значение
обращается к полю с именем , значение
в указателе x
.
Ответ
Основные шаги …
Представленная здесь структура узла на самом деле является реальным типом данных, используемым для построения структуры данных «связанный список».Связанные списки представляют собой реалистичное прикладное использование указателей и являются отличной областью для развития навыков указателя. См. Разделы «Основы работы со связанными списками» и «Проблемы со связанными списками» в Стэнфордской образовательной библиотеке для получения большого количества материалов, связанных со связанными списками.
Авторские права Ник Парланте, 1999. Этот материал можно копировать и распространять при условии сохранения стандартного уведомления Stanford CS Education Library на первой странице: «Это документ 106 в Stanford CS Education Library. Этот и другие бесплатные материалы доступны по адресу cslibrary.stanford.edu. »
Я надеюсь, что вы извлечете пользу из этого материала в духе доброй воли, с которой он дается. Тот, кто ищет образование, должен иметь возможность его найти.
Указатели C: 5-минутный справочник
Деннис Кубес, программист из Техаса, специализирующийся на поиске в реальном времени, предлагает учебник C.
Программирование на C может быть трудным для новичков в этом языке, поэтому Деннис Кубес написал «5-минутное руководство по указателям C» и разместил его в своем блоге.Кубесу нужно всего четыре фрагмента кода, чтобы проиллюстрировать его руководство.
Тем, кому нужен расширенный обзор, стоит посмотреть видео с сайта simplecool под названием «Указатели учебников в C ++ part1» на YouTube. Это почти восьмиминутное видео охватывает C и C ++ и сразу же говорит: «Указатель — это переменная, которая содержит адрес другой переменной».
Очень полезно
По моему мнению, указатели были самым сложным предметом C, пока я не прочитал ваше руководство.
dirtycoder на деннискубах.com
Что за? босс, я хочу, чтобы этот парень прочитал мне лекцию: D
fr00tcrunchStarcraft на youtube.com
Хороший урок!
Может быть, это показывает мой возраст, но я еще не понимаю, почему так много разработчиков в наши дни с трудом понимают указатели, независимо от языка, на котором их обучают.
pjmllp на news.ycombinator.com
Вопросы и ответы
это очень помогло, спасибо человеку! ! теперь я знаю, как использовать указатели, но все еще не знаю? почему мы используем их !!
xbadnerd18x на YouTube.com
Массивы не являются указателями; указатели — это не массивы. Это, пожалуй, наиболее распространенное заблуждение относительно C, и «Руководство по указателям C» не должно его распространять.
_kst_ на news.ycombinator.com
Позвольте мне добавить…
Привет, хороший учебник, хотя есть одно предложение: многие люди, изучающие указатели на c, не уверены в некоторых достоинствах того, почему они должны использовать их, когда сталкиваются с этими новичками учебные пособия по сульфиду
на denniskubes.com
Это номера почтовых ящиков.
В этой аналогии почта — это вся ваша оперативная память. Большая часть, в которой моя аналогия немного нарушается, заключается в том, что в этом маленьком мире почтовое отделение будет хранить вещи больше, чем один ящик, в нескольких смежных ящиках, поэтому, чтобы доставить что-либо в почтовое отделение или из него, вы должны указать, какой ящик вы хотите, чтобы они начали, и сколько ящиков им придется использовать.
derleth на news.ycombinator.com
y — это имя целого числа. это как алгебра
y + x = 3
y = 1
x = 2
x и y оба целые числа, буквы просто представляют числа?
убийство на YouTube.com
Каждый раз, когда кто-то пытается предложить упрощенное объяснение указателей, я возражаю старому буддийскому высказыванию, что «Указывающий палец — это не луна», за которым следует краткий обзор синтаксиса и операторов, например,
луна * палец = & luna;
Просветление бывает очень часто.
rosser на news.ycombinator.com
Какой учебник или книга вы считаете наиболее полезными для C или C ++?
Чтобы следить за последними новостями ИТ, аналитикой и практическими рекомендациями, следите за ITworld в Twitter, Facebook и Google+.
Теперь прочтите это:
Разработчик заявляет: «Я покончил с бизнес-моделью Freemium»
Khan Academy предлагает JavaScript в качестве своего первого компьютерного языка
Исследование показало, что профиль в Facebook может прогнозировать эффективность работы
Этот рассказ «Указатели С: 5-минутное руководство» был первоначально опубликован
ITworld.
Авторские права © 2012 IDG Communications, Inc.
Исследование CS50.
Банкноты
Язык программирования C дает нам возможность напрямую управлять содержимым адресов памяти с помощью указателей. К сожалению, эта мощь дает возможность резко облажаться, поскольку простые ошибки могут испортить состояние вашей программы, что затруднит отладку. Цель этих слайдов — убедиться, что вы точно понимаете, что делаете, когда используете указатели в своем коде.
Знаменитая глиняная фигура, изображенная на этом слайде, — это Бинки из глиняной фигурки Pointer Fun с Бинки.
Код и данные для вашей программы хранятся в оперативной памяти (RAM), которая по сути представляет собой огромный массив блоков размером 1 байт (8 бит). Каждый блок связан с шестнадцатеричным числом, которое представляет его адрес в памяти.
Подобно тому, как int — это переменные, хранящие целые числа, а числа с плавающей запятой — это переменные, которые хранят значения с плавающей запятой, указатель — это переменная, которая хранит адреса памяти.В устройстве (32-битный режим работы) адрес памяти составляет 4 байта, поэтому имеет смысл, чтобы указатель также был 4 байта.
Теперь вы должны понимать этот комикс!
Вот как объявить указатель в C.
Помните, что значение указателя является адресом памяти. Его тип затем описывает данные, расположенные по этому адресу.
Итак, в первом примере int * x объявляет указатель на 4 байта памяти, которые будут содержать целое число.
& — это ссылка или адрес оператора . Он возвращает адрес в памяти, по которому хранится переменная.
* — оператор разыменования . Значение указателя — это адрес памяти. Когда оператор разыменования применяется к указателю, он возвращает данные, хранящиеся по этому адресу памяти.
Вот примеры используемых операторов обращения и разыменования.Давайте внимательно подумаем о том, что происходит под капотом при выполнении каждой строки.
- В первой строке объявляется целое число x. 4 байта отведены для x по адресу памяти 0x04, и значение 5 сохраняется там.
- Вторая строка объявляет указатель на int с именем ptr. 4 байта отведены для ptr по адресу памяти 0x08, и адрес x хранится там.
- В третьей строке объявляется целое число, называемое копией. 4 байта выделены для копирования по адресу памяти 0x0C, и значение целого числа, на которое указывает ptr, сохраняется там.
Давайте отслеживать значения этих переменных по мере выполнения каждой строки:
- В первой строке x инициализируется значением 5.
- Во второй строке ptr назначается адрес памяти x. x остается 5.
- В третьей строке мы разыменовываем ptr (то есть переходим к значению, на которое он указывает) и меняем это значение на 35.
Проверьте себя, отслеживая значения этих переменных, как мы это делали на предыдущем слайде.
Ответы на упражнение на последнем слайде!
Вы можете настроить указатели, добавляя или вычитая целое число.
Добавление или вычитание n изменяет указатель на n * sizeof (<тип указателя>) байтов. Например, в коде на этом слайде y — указатель на int с именем x, который хранится по адресу 0x04. Следовательно, добавление 1 к y сдвигает значение y на 1 * sizeof (int) или 4 байта.
Обратите особое внимание на синтаксис этого цикла for.ptr начинает указывать на первый символ строки. При увеличении ptr указатель сдвигается на 1 * sizeof (char) или на 1 байт, перемещая ptr к следующему символу в строке.
После выполнения цикла будет напечатана длина строки.
Под капотом массив обрабатывается как указатель на его первый элемент, поэтому применяется стандартная арифметика указателя:
* массив эквивалентен массиву [0]
* (массив + 1) эквивалентен массиву [1]
* (массив + 2) эквивалентен массиву [2].
.