[ВУЗ] Программирование | Лекция 06 | Массивы, указатели | PPTX
- Количество слайдов: 38
Лекция 6 Адреса, указатели, массивы, память
1 Указатели - это переменные, показывающие место или адрес памяти, где расположены другие объекты (переменные, функции и др. ). • При объявлении переменной типа указатель, необходимо определить тип объекта данных, адрес которого будет содержать переменная, и имя указателя с предшествующей звездочкой (или группой звездочек). Формат объявления указателя спецификатор типа [ модификатор ] * описатель 08: 03
2 Адреса, указатели Так как указатель содержит адрес некоторого объекта, то через него можно обращаться к этому объекту. Унарная операция & дает адрес объекта, поэтому оператор у = &х; присваивает адрес переменной х переменной у. Операцию & нельзя применять к константам и выражениям; конструкции вида &(х+7) или &28 недопустимы. #include
3 Адреса, указатели Унарная операция * воспринимает свой операнд как адрес некоторого объекта и использует этот адрес для выборки содержимого, поэтому оператор z = *y; присваивает z значение переменной, записанной по адресу у. y = &x; z = *у; Равнозначно z = x; int *а, *b, *с; char *d; 08: 03
4 Адреса, указатели Над указателями определено 5 основных операций: • Определение адреса указателя: &p, где p – указатель (&p – адрес ячейки, в которой находится указатель). • Присваивание. Указателю можно присвоить адрес переменной p=&q, где p – указатель, q – идентификатор переменной. • Определение значения, на которое ссылается указатель: *p (операция косвенной адресации). • Увеличение (уменьшение) указателя. – Увеличение выполняется как с помощью операции сложения (+), так и с помощью операции инкремента (++). – Уменьшение – с помощью операции вычитания (–) либо декремента (––). 08: 03
Адреса, указатели 5 *у = 7; *x *=5; (*z)++; • В последнем случае круглые скобки необходимы, так как операции с одинаковым приоритетом выполняются справа налево. В результате если, например, *z = 5, то (*z)++ приведет к тому, что *z = 6, а *z++ всего лишь изменит сам адрес z (операция ++ выполняется над адресом z, а не над значением *z по этому адресу). 08: 03
6 Адреса, указатели • Указатели можно использовать как операнды в арифметических операциях. • Если у - указатель, то унарная операция y++ увеличивает его значение; теперь оно является адресом следующего элемента. • Указатели и целые числа можно складывать. Конструкция у + n (у - указатель, n - целое число) задает адрес n-гo объекта, на который указывает у. Это справедливо для любых объектов (int, char, float и др. ); транслятор будет масштабировать приращение адреса в соответствии с типом, указанным в определении объекта. 08: 03
7 Адреса, указатели • Любой адрес можно проверить на равенство (==) или неравенство (!=) со специальным значением NULL, которое позволяет определить ничего не адресующий указатель. 08: 03
8 Адреса, указатели • Разность двух указателей. Пусть р1 и р2 – указатели одного и того же типа. Можно определить разность р1 и р2, чтобы найти, на каком расстоянии друг от друга находятся элементы массива. 08: 03
9 Переменные перечислимого типа Переменная, которая может принимать значение из некоторого списка значений, называется переменной перечислимого типа или перечислением. • Объявление перечисления начинается с ключевого слова enum и имеет два формата представления. Формат 1. • enum [имя-тега-перечисления] {списокперечисления} описатель[, описатель. . . ]; Формат 2. • enum имя-тега-перечисления описатель [, описатель. . ]; 08: 03
10 Переменные перечислимого типа enum cardsuit { Clubs, Diamonds, Hearts, Spades }; enum cardsuit Clubs = Diamonds = Hearts = Spades = }; 08: 03 { 1, 2, 4, 8
11 Массивы • Массив состоит из элементов одного и того же типа. Ко всему массиву целиком можно обращаться по имени. Кроме того, можно выбирать любой элемент массива. Для этого необходимо задать индекс, который указывает на его относительную позицию. Число элементов массива назначается при его определении и в дальнейшем не изменяется. • Если массив объявлен, то к любому его элементу можно обратиться следующим образом: указать имя массива и индекс элемента в квадратных скобках. 08: 03
12 Массивы Объявление массива имеет два формата: спецификатор-типа описатель [константное - выражение]; спецификатор типа описатель [ ]; Описатель - это идентификатор(имя) массива. 08: 03
13 Массивы определяются так же, как и переменные: int a[100]; char b[20]; float d[50]; • В первой строке объявлен массив а из 100 элементов целого типа: а[0], а[1], . . . , а[99] (индексация всегда начинается с нуля). Во второй строке элементы массива b имеют тип char, а в третьей - float. 08: 03
14 Массивы • Отдельный элемент массива определяется именем массива и индексом элемента в квадратных скобках, например: int b = a[0]; a[1] = b*2; 08: 03
15 Массивы Элементы массива обладают теми же свойствами, что и переменные соответствующего типа. Массив имеет следующие свойства: • Элементы массива имеют одинаковый тип, поэтому занимают одинаковый объем памяти. • Массив располагается в оперативной памяти. • Элементы массива занимают подряд идущие ячейки 08: 03
16 Массивы • В языке СИ определены только одномерные массивы, но поскольку элементом массива может быть массив, можно определить и многомерные массивы. • Они формализуются списком константных-выражений следующих за идентификатором массива, причем каждое константное-выражение заключается в свои квадратные скобки, поэтому двумерный массив представляется как одномерный, элементами которого так же являются массивы. • Например, определение char а[10][20]; задает такой массив. 08: 03
Массивы 17 Пусть задан массив: int array[4][4]; Тогда элементы массива а будут размещаться в памяти следующим образом: 1 2 3 0 array[0][0] array[0][1] array[0][2] array[0][3] 1 array[1][0] array[1][1] array[1][2] array[1][3] 2 array[2][0] array[2][1] array[2][2] array[2][3] 3 08: 03 0 array[3][0] array[3][1] array[3][2] array[3][3]
18 Массивы • Имя массива - это константа, которая содержит адрес его первого элемента (в данном примере переменная а содержит адрес элемента аrray[0][0]). • Предположим, что array = 1000. Тогда адрес элемента аrray[0][1] будет равен 1004 (элемент типа int занимает в памяти 4 байта), адрес следующего элемента аrray[0][2] - 1008 и т. д. • Что же произойдет, если выбрать элемент, для которого не выделена память? К сожалению, компилятор не отслеживает данной ситуации. В результате возникнет ошибка и программа будет работать неправильно. 08: 03
Массивы 19 В языке Си существует сильная взаимосвязь между указателями и массивами. Любое действие, которое достигается индексированием массива, можно выполнить и с помощью указателей, причем последний вариант будет работать быстрее. • Определение int a[5]; • задает массив из пяти элементов а[0], a[1], a[2], a[3], a[4]. 08: 03
Массивы 20 Если объект *у определен как int *у; то оператор у = &a[0]; присваивает переменной у адрес элемента а[0]. Если переменная у указывает на очередной элемент массива а, то y+1 указывает на следующий элемент, причем здесь выполняется соответствующее масштабирование для приращения адреса с учетом длины объекта (для типа int - 4 байта, long - 8 байт, double - 8 байт и т. д. ). 08: 03
21 Массивы Так как само имя массива есть адрес его нулевого элемента, то оператор у = &a[0]; можно записать и в другом виде: у = а. Тогда элемент а[1] можно представить как *(а+1). С другой стороны, если у - указатель на массив a, то следующие две записи: a[i] и *(у+i) - эквивалентны. Между именем массива и соответствующим указателем есть одно важное различие. Указатель - это переменная и у = а; или y++; - допустимые операции. Имя же массива - константа, поэтому конструкции вида a = y; a++; использовать нельзя, так как значение константы постоянно и не может быть изменено. 08: 03
Массивы 22 • Язык Си позволяет инициализировать массив при его определении. Для этого используется следующая форма: тип имя_массива[. . . ]. . . [. . . ] = {список значений}; • Примеры: int a[5] = {0, 1, 2, 3, 4}; char ch[3] = {'d', 'e', '9'}; int b[2][3] = {1, 2, 3, 4, 5, 6}; • В последнем случае: b[0][0] = 1, b[0][1] = 2, b[0][2] = 3, b[1][0] = 4, b[1][1] = 5, b[1][2] = 6. 08: 03
Массивы 23 В языке допускаются массивы указателей, которые определяются, например, следующим образом: char *m[5]; • Здесь m[5] - массив, содержащий адреса элементов типа char. Так же двумерный массив можно инициализировать так: int b[2][3] = { {1, 2, 3}, {4, 5, 6} }; 08: 03
Массивы 24 В многомерном массиве размер самого левого измерения также можно не указывать. Допустима следующая запись: int b[][3] = { {1, 2, 3}, {4, 5, 6} }; 08: 03
Массивы 25 Пример, поиск суммы элементов одномерного массива #include
26 Классы памяти • Класс памяти определяет порядок размещения объекта в памяти. Различают автоматический и статический классы памяти. C++ располагает четырьмя спецификаторами класса памяти: auto, register, static, extern, по два для обозначения принадлежности к автоматическому и статическому классам памяти. • Статический класс памяти может быть локальным (внутренним) или глобальным (внешним). 08: 03
Классы памяти 27 • Таблица иллюстрирует иерархию классов памяти. Динамический класс памяти Статический класс памяти Автоматический Регистровый Локальный Глобальный auto register static extern • Спецификаторы позволяют определить класс памяти определяемого объекта 08: 03
28 Класс памяти «auto» • Этот спецификатор автоматического класса памяти указывает на то, что объект располагается в локальной памяти. Он используется в операторах объявления в теле функций, а также внутри блоков операторов. • Объекты, имена которых объявляются со спецификатором auto, размещаются в локальной памяти непосредственно перед началом выполнения функции или блока операторов. При выходе из блока или при возвращении из функции, соответствующая область локальной памяти освобождается и все ранее размещённые в ней объекты уничтожаются. Таким образом, спецификатор влияет на время жизни объекта. • Вне блоков и функций этот спецификатор не используется. 08: 03
29 Класс памяти «register» • Ещё один спецификатор автоматического класса памяти. Применяется к объектам, по умолчанию располагаемым в локальной памяти. Представляет из себя "ненавязчивую просьбу" к транслятору (если это возможно) о размещении значений объектов, объявленных со спецификатором register в одном из доступных регистров, а не в локальной памяти. • Если по какой-либо причине в момент начала выполнения кода в данном блоке операторов регистры оказываются занятыми, транслятор обеспечивает с этими объектами обращение, как с объектами класса auto. Очевидно, что в этом случае объект располагается в локальной области памяти. 08: 03
30 Класс памяти «static» Спецификатор внутреннего статического класса памяти. Применяется только к именам объектов и функций. • В C++ этот спецификатор имеет два значения. • Первое означает, что определяемый объект располагается по фиксированному адресу. Тем самым обеспечивается существование объекта с момента его определения до конца выполнения программы. • Второе значение означает локальность. Объявленный со спецификатором static объект локален в одном программном модуле или в классе. Может использоваться в объявлениях вне блоков и функций. Также используется в объявлениях, расположенных в теле функций и в блоках операторов. 08: 03
31 Класс памяти «extern» • Спецификатор внешнего статического класса памяти. Обеспечивает существование объекта с момента его определения до конца выполнения программы. Объект, объявленный со спецификатором extern доступен во всех модулях программы, то есть глобален. 08: 03
32 Классы памяти • В языке С при редактировании связей к переменной может применяться одно из трех связываний: внутреннее, внешнее или не относящееся ни к одному из этих типов. В общем случае для имен функций и глобальных переменных предназначено внешнее связывание. Это означает, что после компоновки они будут доступны во всех файлах, составляющих программу. 08: 03
33 Классы памяти • К объектам, объявленным со спецификатором static и видимым на уровне файла, применяется внутренне связывание, после компоновки они будут доступны только внутри того файла, в котором объявлены. • К локальным переменным связывание не применяется, и поэтому они доступны только внутри своих блоков. 08: 03
34 Классы памяти • Спецификатор extern указывает на то, что к объекту применяется внешнее связывание. • Объявляются (декларируются) имя и тип объекта. Описание (определение, дефиниция) выделяет для объекта участок памяти, где он будет находиться. Один и тот же объект может быть объявлен неоднократно в разных местах, но описываться будет только один раз. 08: 03
Классы памяти 35 • Рассмотрим пример применения спецификатора extern к глобальным переменным #include
36 Классы памяти При компиляции выполняются следующие правила: • если компилятор находит переменную, не объявленную внутри блока, он ищет ее объявление во внешних блоках; • если не находит ее там, то ищет среди объявлений глобальных переменных. Спецификатор extern играет большую роль в программах, состоящих из многих файлов. В языке С программа может быть записана в нескольких файлах, которые компилируются раздельно, а затем компонуются в одно целое. В этом случае необходимо как-то сообщить всем файлам о глобальных переменных программы. Самый лучший способ сделать это – определить (описать) все глобальные переменные в одном файле и объявить их со спецификатором extern в остальных файлах. Все ссылки на внешние переменные распознаются в процессе редактирования связей. 08: 03
37 Классы памяти • Выбор класса памяти, помимо явных спецификаторов, зависит от размещения определения или объявления в тексте программы. • Модуль, функция, блок могут включать соответствующие операторы объявления или определения, причём всякий раз определяемый объект будет размещаться в строго определённых областях памяти. 08: 03