Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

chast1

.pdf
Скачиваний:
16
Добавлен:
10.06.2015
Размер:
499.82 Кб
Скачать

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

Простейший вариант определения указателей. <тип>*<имя>;

Тип - это любой тип с++, кроме ссылки и битового поля имя - это идентификатор, который будет именем,определяемому указателю.

Смысл символа * заключается в том, что именно онобъявляет следующую за ним переменную указателем. Синтаксический символ * относится к непосредственному следующему за ним имен,поэтому он должен стоять перед каждым определяемым указателем.

double *x,*y,z;//определены два указателя (x,y) на данные типы double,и простая переменная z.

При определении указателя часто целесообразно выполнять его инициализацию. Целесообразность этого действия обусловлено тем, что как показывает опыт распространенной ошибкой является использование неинициализированных указателей. Эта ошибка может привести к неправильной работе с памятью компьютера, к порче памяти.

Допустимы две формы инициализации:

1.< тип>*<имя>=<инициализирующее значение>;

2.< тип>*<имя>(<инициализирующее значение>);

В качестве инициализирующего значения (выражение )должно использоваться константное выражение, которое в частности может быть:

-явно заданным адресом области памяти

-указателем, уже имеющим значение

-выражением,позволяющим получить адрес существующего объекта с помощью операции взятия адреса(&). Если значение константы равно 0, то оно преобразуется к пустому ( нулевому) указателю.

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

Внутреннее(бытовое) представление пустого (нулевого) указателя может отличаться от битового представления целого нуля. Для хранения пустого значения указателя в некоторых компиляторах языка С++ используют именованную константу NULL. Указатель может получить адреспеременной или именованной константы. Указатель можно объявлять с квалификатором const,причем возможны различные варианты применения квалификатора.Например, указатель сам может быть именованной константой,в этом случае, адрес, который он получает в процессе инициализации не можетбыть изменен, в то же время,если такой указатель адресуется к переменной,то значение такой переменной можно изменить.

Указатели содержат толькоадреса. для того , чтобыполучить доступ к той области памяти к которой адресуется данный указатель, к указателю нужно применить операциюразыменования (разадрессация, раскрытие).

Синтаксис и семантика операции разыменования. Пусть сделаны следующие определения

double x=1.23;//инициализированная вещественнаяпеременная

double *pc=&x;//указатель рс на объект типа doubleинициализирован адресом переменной х.

Указатель адресуется к области памяти, в которой хранится значение х.

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

*pc

Унарное выражение*с обладает правами имени переменной, т.е. вместо имени х можно использовать его псевдоним *с. Например правильными будут такиеоператоры

y=*pc;//переменная y получает значение переменной х *рс=3.45;//переменна х получает новое значение

___________________________________________________________________________________

17.Синтаксисинициализации указателей.Способы инициализации указателей.Опасности, связанные с использованием неинициализированных указателей.

__________________________________________________________________________________

Рассмотрим примеры определения указателей и прокомментируем особенности этих указателей:

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

double *pc=&x;

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

2.Инициализация указателя указателем уже имеющем значение

double *pc1=pc;//Смотри предыдущий пример

3.Инициализация указателя константы char *vp=(char*) OxB8000000;

Здесь указателю vp присвоено конкретное значение - шестнадцатеричной константы. константу нужно обязательно приводить к типу "указатель на char". Это делается с помощью операции приведения к типу, имеющему у нас формат (char*).

4.Инициализация указателя пустым значением

char *ptr=NULL;//указатель на объект типа char инициализован константой NULL. char *ptr=0;// ин7ициализация нулем

Последний оператор допустим, поскольку целое нулевое значение и только оно поумолчанию правильно преобразуется к нужному типу.

Для обеспечения переносимости программы рекомендуется именно так инициализировать указатели пустым значением, т.к. в некоторых компиляторах при использовании константы NULL могут возникнуть проблемы.

указатель инициализованный пустым значением несвязан ни с какой областью памяти,а поэтому его бессмысленно разыменовывать.

5.Особенности использования неинициализированных указателей. Пусть вы объявили следующий указатель:

int *p;//если указатель не инициализирован, то его значение не определено, т.е. оно можетбыть любым в зависимости от предыдущих операций с областью памяти отведенной под указатель. Бессмысленно пытаться добраться до области памяти к которой случайно адресуется указатель. Если такую попытку совершить, то будет либо крах программы, либо еще более плохой ошибкой, связанной с порчей памяти. Ощибка может проявится

не сразу или вообще формально не проявится.

__________________________________________________________________________________

18.Использование квалификатора const при определении указателей.Указатели на void.

__________________________________________________________________________________

6.Определения с квалификатором const. Расположение квалификатора const в определении указателя влияет на вид указателя. Рассмотрим следующие ситуации:

а.const int*i1;//(1)определили указатель на целую константу типа int const int k=2;//(2)обычная инициализованная целая константа i1=&k;//(3)указатель i1 получает адресконстанты k.

Определение в строке 1 создает указатель на константу, т.е. на объект значение которого запрещено менять.

Таким образом недостающий оператор: *i1=5;//(5)Ошибка. Попытка изменить значение константы.

Здесь принципиальным является наличие квалификатора const при определении типа данных к которым будет адресовываться указатель.

-если квалификатор const в строке 1 убрать, то строка 3 станет ошибочной т.к. указателю переменной нельзя присвоить адрес константы.

-с другой стороны если сохранить определение в строке 1, то указателю i1 можно передавать адрес не только константы,но и переменной.

Например правильнымибудут такие операторы: int m=4;//(5)Обычная целая переменная i1=&m;//(6)указательi1 получает адрес переменной

Ошибочный оператор.

*i1=5;//(7) Ошибка: изменение значения переменной таким способом запрещено,т.е. псевдоним переменной *i1 будет интерпретироваться как константа. парадокс в том,что значение, хранящееся в области памяти переменной m можно изменить,если обратиться к ней обычнчм способом. m=5;//(8) допустимый оператор

Определение в строке 1 порождает указатель на константу и соответственно содержимое области памяти, к которой адресуется указатель, с помощью указателя менять нельзя, но сам указатель является указателем-переменной,т.е.его значение, как адрес можно менять (см. строки 3 и 6)

б.int*const i1=&m;//это указатель константы на объект типа int.

Здесь квалификатор const стоит между * и именем указателя i1. Инициализация указателя константы является обязательной. Его основное свойство заключается в том,что нельзя менять именно его значение, но при этом значение самой переменной на области памяти к которой относится указатель можно менять, т.е. правильнымбудет такой оператор:

*i1=100;//(10)

m=1000;//(11)обычный оператор тоже разрешен

Указателю объявленному в стиле строки 9 нельзя передавать адрес какой то другой константы,т.е. например такой оператор неправилен:

int*const i2=&k;//(12)ошибка, такой оператор не может ссылаться на константу.

в.Если мы хотим создать указатель константы ссылающейся на константу,то определение должно выглядеть так:

const int*const i2=&k;//(13)

7.Указатели на void/

При работе с указателями С++ как и при работе с обычной переменной и константами разных типов необходимо соблюдать правила преобразования типов. Во всех приведенных выше простых примерах операндами в левых и правыхчастях оператора присваивания имели один тип,поэтому проблема преобразования типовне возникала.

Правила преобразования рассмотрим позже, а здесь приведем только особенности работы с указателем на vpid:

-такому указателю можно присвоить значение любого не константного и не имеющего модификатор volatile указателя, при этом не надо заботится о преобразовании типа,т.к. все необходимые преобразования осуществляются по умолчанию. Нина какие другие случаи преобразование по умолчанию не распространяется.

int i=100; void*v_ptr; int*i_ptr=&i;

v_ptr=i_ptr;//правильно

i_ptr=v_ptr;//неправильно

__________________________________________________________________________________

19.Динамическое управление памятью спомощью указателей (на примере базовых типов).Опасности связанные сиспользованием динамических переменных.

__________________________________________________________________________________

Приведенные в предыдущем пункте примеры иллюстрируют какие синтаксические и семантические особенности работы с указателями. Эти примеры не раскрывают и не могут раскрыть практических преимуществ,обеспечиваемых прямой работой с адресами через указатели. Полномасштабностьбудет раскрыта при изучение структурно-сложных типовданных, а также функций и классов с++. Однако один из важных практических аспектов, оставаясь в рамках базового типа является аспект динамичного управления памятью. Мы говорили,что атрибут (продолжительность существования) может иметь три значения: статическое, локальное и динамическое.

Первые два мы подробно рассмотрели. Краткое напоминание.

-Статическую продолжительность существования имеют локальные переменные,объявленные со спецификатором static и глобальные переменные.Время жизни такой переменной - это все время работы программы. Этот фактор, с одной стороны упрощает работу с переменной,т.е. она доступна всегда и если она глобальна и без static почти в любом месте программы, но с другой стороны такой вариант определения переменной можетбыть неэкономичным. Возможно,что глобальная переменнаянужна только на первом коротком этапе работы программы, а на остальных этаах она будет занимать часть оперативной памяти.

-Локальную продолжительность существования имеет локальные переменные явно или не явно определяемые со спецификатором auto. Время жизни от момента срабатывания оператором определения до выхода из блока,где находится определение.Самый распространенный случай - это определение локальной переменной в блоке, который является телом функции. В этом случае переменная возникает каждый раз, как функция вызывается в ходе вычислительного процесса и исчезает, как только в данном вызове функция закончит работать. Это более экономичный способ использования памяти, т.е. память тратится н6а интервале работы функции. В то же время,если функция работаетбольшую часть времени работы программы,то экономия может быть несущественна. Информация,связанная с переменной может оказаться необходимойне только в интервалах виртуальной жизни локальной переменной и не только внутри данной функции.

Желателен такой вариант продолжительности существования и области действия переменной, когда как время жизни так и область действия переменной могут регулироваться программистом существенно в более широком диапазоне, чем это обеспечивают обычные глобальные и локальные переменные. Такая возможность обеспечивается динамическим управлением памяти. Для такого управления с++ используют две операции new и delete/

new - это для выделения памяти под динамическуюпеременную.

delete - для освобождения памяти, ранее выделенной под динамическую переменную.

Форматы применения этих операций рассмотрим на примере: int*id1;//(1) определение указателя может быть и локальным и глобальным

id1=new int;//(2) операция new выделяет область памяти,необходимую для хранения значения типа int и возвратит адрес этой области. Таким образом оператор id1 будет адресоваться к этой области. *id1=11;//(3) в выделенную область памяти заносим конкретное значение

ciaut<<" *id1 = "<<*id1<<endl;//(4) в данном примере динамической переменной является созданная операцией new область памяти,а в качестве имени этой переменной используется псевдоним *Id1, который образован с помощью операции разыменования. Если id1 определен локально,то созданная операцией new динамическая переменная тоже локальна. Если id1 определена глобально, то и динамическая переменная глобальная. После того как необходимость в раннее созданной динамической

переменной отпадает,ее целесообразно уничтожить, т.е. вернуть ранее выделенную память оперативной системе. Для этого необходимо применить операцию:

delite и d1;//(6)

опасности.

-Необходимо помнить,что указатели переменной,как54 и другие любые переменные имеют определенную область действия и определенную продолжительность существования, поэтому, если указатель является локальным и определен без спецификатора static (т.е. имеет локальную продолжительность существования) , то при выходе вычислительного процеса из его области действия, отведенная позднее указателю,память теряется. В результате над областью памяти, созданной операцией new теряется контроль. В частности ее нельзя уничтожить операцией delite. В результате образуется ничья область памяти, которая не может быть использована операционной системой компьютера в других целях. Образуется мусор.

-С учетом вышеуказанного ясно, что при работе с локальной динамической переменной операцию delite нужно обязательно применять до окончания блока,в котором определен локальный указатель.

-Если создано динамическая переменная id1=new int, а после этого присвоим ему другой адрес,то контроль над областью памяти, созданной операцией new также теряются. Это тоже источник возникновения мусора.

Приведенная выше операции new и delite рекомендуется использовать,как основные под компиляторы с++,возможно также использование средств,унаследованных от с. Для выделения памяти под динамическую переменную используется функцияmallouc, а для освобождения free

int*id1=(int*)malloc(sizeof)(int));//(1) определение указателя с одновременной ее инициализацией адреса, создаваемой областью памяти

...

free(id1);//(2) входным параметром функции malloc является размер требуемой области памяти (байт),а функция возвращает адресс этой области. Этот адрес надо привести к типу int. Функция free освобождает память ранее выделенной функцией malloc.

__________________________________________________________________________________

20.Общее определение понятия массив.Одномерные массивы, синтаксисих определения. инициализация массива.Переменная синдексом.Объявлениевнешнего массива.Особенности указания количества элементов массива в этом случаи.

_______________________________________________________________________________

Массив - это именованный упорядоченныйнабор значений, которые называются элементами массива. Все элементы должны принадлежать к одному типу данных. Тип элементов порождает тип массива. Соответственно, говорят о массиве целых чисел типа int(целом массиве), о массиве данных типа char(о символьном массиве)и.т.д. В общем случае элементы массива-это значение не только базовых, но и структурно сложных типов. В частности элементы сами могут быть массивами (массив массивов).

Ограничения. В качестве элементов массива нельзя использовать ссылки и функции.

Массивы полезны,где приходится иметь дело с большим числом однородных величин.С помощью массива легче, чем с помощью данныхбазовых типов организовать перебор каких-то однородных величин и производить над этими величинами однотипныеоперации в рамках операторов циклов.

Массивы могут быть одномерными и многомерными. Они могут быть статическими или динамическими конструкциями. При этом динамической может быть не только продолжительность существования, но и длина массива.

В данном пункте рассмотрим одномерные массивыфиксированной длины. <тип><имя массива>[<количество элементов>] Тип - тип элементов массива

Имя массива - правильный идентификатор в категории имен, это имя переменной,которая сама имеет сложную структуру.

Количество элементов - константное выражение,задающее длину массива. Длина массива можно не задавать,если массив при определении инициализируется, в этом случае длина массива устанавливается автоматически, но квадратные скобки обязательны.

Переменная-массив при определении можно инициализировать. Формат соответствующих определений поясним на примере

const int razm=5;//(1)

dauble mass11[razm*2(константное выражение)]={1.1,2.2,3.3,4.4,5.5};//(2)

Длина массива задана константным выражением и равно 10. Явно приэтом инициализируется только первые 5 элементов массива. Остальные,в случае, если массив глобальный, инициализируется нулями, а если массив локальный, то случайными значениями.

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

Если массив инициализируется, то длина массива может не указываться. int massI[]={10,20,30};//(3)

В этом случае длина массива будет определяться тем сколько значений указано в инициализаторе Если массив неинициализирован, то длину указывать обязательно.

Как и любая переменная массив может быть локальным или глобальным и его можно определить или объявить с допустимыми спецификаторами,при этом если это есть объявление внешней переменной массива, то константное выражение в квадратных скобках можно опускать.

dauble mass[10]={1.1,2.2,3.3,4.4,5.5};//(4) определяем массив с инициализацией в каком-то файле проекта

...

extern dauble mass[];//(5) это объявление массива,как внешней переменной в другом файле.

Если глобальная переменная-массив явно не инициализируется,то инициализация нулями проводится неявно,а локальная переменная-массив неявно неинициализируется.

При работе с массивом правила синтаксиса языка с++ разрешает доступ к элементам массива только по отдельности,а именно с помощью переменных с индексом. Формат переменных с индексом таков: в начале указывается имя массива, а далее в квадратных скобках индекс элемента.

cout<<mass[4];//(6) на экран будет выведен 5-й элемент массива (индексацияначинается с нуля)

Опасность!

Вс++ не осуществляется контроль выхода индекса за допустимые границы. Если это произойдет, то возникнет ошибка,которая непосредственно средствами среды программирования не перехватывается. Эта ошибка может породить другую, которая может быть перехвачена,но не обязательно. Тогда формально аварийной ситуации не возникнет, но результат будет абсурден.

___________________________________________________________________________________

21.Соотношение между массивами и указателями.

__________________________________________________________________________________

Особенности использования имени массива в с++ такова:

При определении массива, например, как в строке 4компилятор выделит память на массив,как на структурно сложную переменную в соответствии с текстом определения. В нашем примере на переменную масс будет выделено 80 байт. После определения переменной массива, имя массива,за исключением некоторых ситуаций, воспринимается, как константный указатель на данные того типа,к которому относятся элементы массива. Исключением является,например, использование имени статического массива в операции cizeof. В этом случае имя массива воспринимаетсякак имя массива в целом,и операция вернет размер в байтах области памяти выделенной под массив

Востальных случаях значением имени массива является адрес первогоэлемента массива.

Рассмотрим кратко возможности связанные с тем, что имя массиваэто указатель. В частности, проанализируем с адресной точки зрения понятиепеременная с индексом. Для этого необходимо четко понять, каков смыслинтерпретации пары квадратных скобок, как операции.

Например выражение mass[4] это выражение с операцией [] и двумя операндами.

Первый ( mass) это адрес начала массива, а второй (4) это смещение соответствующего элемента от начала массива, оно показывает,что адресэлемента имеющего индекс 4 увеличен на 4*(cizeof(double))байт по сравнению со значением mass. Соответственно 5-го элемента массива mass можно получить не только привычным способом:

double x;//(7) определим вспомогательную вещественную переменную x=mass[4];//(8)

но и следующим образом x=*(mass+4);//(9)

в последнем выражении учтен смысл понятия смещения и соответствующая особенность"адресной арифметики": выражение (mass+4), в котором к указателю прибавляется целое значение возвращает адрес, который относительно указателя mass возрастает не на 4, а на число, кратное количеству байт необходимых для хранения того типа, который использован при определении указателя. В нашем примере это тип doublle. Поэтому если значение указателя mass =N(адрес начала массива),то выражение (mass+4) вернет значение n=32. Соответственно благодаря операции разыменования *(mass+4) мы получим фактически адрес элемента массива с индексом 4. Таким образом выражение *(mass+4) можно считать именем элемента массива. Сказанное иллюстрирует, почему в с++ индексация элементов массива начинается с нуля: Первый элемент имеет нулевое смещение относительно начала массива и его индекс разумно полагать равным нулю.

Поскольку операция сложения коммутативна, то вместо строки 9 допустимо написать: x=*(4+mass);//(10)

Соответственно вместо строки 8 допустимо: x=4[mass];//(11)

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

int *pI;//(12) определили обычный указатель на данные типаint int massI[3];//(13) трехэлементный массив

pI=massI;//(14) после этого оператора имя pI становится псевдонимом имени massI, т.е. через него можно обращаться к элементам массива (без операции разыменования):

pI[1]=55;//(15)

________________________________________________________________________________

22.Опасности, связанные свыходом индекса массива за допустимые пределы.Способпредотвращения такого выхода.

_______________________________________________________________________________

В с++ нет автоматического контроля за нахождением индекса массива в допустимых пределах. Поскольку соответствующие ошибки чреваты трудноразрешимыми проблемами в работе с массивом целесообразно самостоятельно организовать такой контроль,используя операцию sizeof.

Пусть необходимо вывести на экран значения элементов массива.

int Nmass(кол-во эл)=sizeof(mass)(80 байт)/sizeof(duable)(8байт);//(16) зафиксировали длину массива for(inti=0;i<Nmass;i++);//(17)

cout<<" mass["<<i<<"] = "<<mass[i]<<endl;//(18) при выполнении for условия i<Nmass не позволит индексу i

выйти за допустимые пределы.

Замечания.

Если строки 16-18 написаны в файле ,в котором массив mass является внешнем и он определен в другом файле в виде строки 5,то в данном файле в объявлении массива длина обязательно должна быть указана, т.е. объявление должно иметь вид

extern duable mass[10];//(5')

иначе операция sizeof(mass) будет интерпретироваться как ошибка.

_________________________________________________________________________________

23.Строки в стилеС.

__________________________________________________________________________________

При выполнении определенного требования одномерный массив элементы которогоотносятся к типам char,unsigned char, signed char,wchar_t называют строкой в стиле С, поскольку именно так строки, т.е. упорядоченные последовательности символов реализуются в С. Указанное требование состоит в том, что в конце последних символов составляющих строку должен стоять терминальный ноль, т.е. символ имеющий нулевой код и играющий роль правой границы строки, соответственно такие строки называют нультерминальными.

В тексте С++ терминальный ноль задается управляющей конструкцией '\0'. Вносить терминальный ноль в символьный массив можно двумя способами:

1.Автоматический:

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

char str[10]="строка";(1)

после выполнения этого оператора в массиве str будет содержаться такая последовательность символов: 'с' 'т' 'р' 'о' 'к' 'а' '\0'

они будут занимать первые семь элементов массива.

Если массив определен без указателя количества элементов, но с обязательной инициализацией char str[]="строка";(1')

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

2.Символьный массив можно наполнить данными как и любой другой массив, но при этом если мы хотим чтобы он интерпретировался как строка в стиле С мы должны сами добавить терминальный ноль.

char an[5]={'a','b','c','\0'};//(2)

будет создан массив из пяти элементов причем первые 4 будут сразу заняты символами 'a','b','c','\0'. Если бы мы не добавили терминальный ноль, то это был бы обычный символьный массив, но не строка в стиле С.

Значение символьного массива как и любого другого можно менять посимвольно: str[3]='ы';//(3)

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

Пример поэлементного вывода строки в консольное окно с помощью цикла в котором используется терминальныйноль:

int i=0;//(4)

while (*(str+i)!='\0')//(5) cout<<*(str+i); cout<<endl;//(6)

__________________________________________________________________________________

24.Многомерные массивы.

__________________________________________________________________________________

В соответствии с синтаксисим С++ многомерный массив это массив массивов, т.е. это одномерный массив элементами которого являются массивы.

Синтаксис определения массива предписывает задание размерности массива, типа элементов и количество элементов по каждой размерности:

<type><имя массива>[N1][N2]...[NL];//формат определения L-мерного массива. <type> - тип элементов массива, любой тип С++

L - размерность массива

N1 - количество элементов массива размерности L-1 каждый

N2 - количество элементов массива размерности L-2 каждый и т.д.

Двухмерный массив определенныйнапример так: double mass2[3][2];//(1)

проще всего представить себе матрицу 3*2,т.е. матрицу с 3 строками и 2 столбцами в которой элементы массива (переменные с индексами) будут расположены так:

0 1

0mass2[0][0] mass2[0][1]

------------>

1mass2[1][0] mass2[1][1]

------------>

2mass2[2][0] mass2[2][1] (*)

При этом в непрерывную область памяти компьютера элементы массива будут переписаны по строчкам, т.е. в следующем порядке:

mass2[0][0] mass2[0][1] mass2[1][0] ...

Стандартная характеристика массива (*) должна быть такой:

это одномерный массив из трех элементов причемкаждый элемент в данном примере это одномерный массив из двух вещественных чисел.

Общее правило.

В памяти компьютера элементы многомерного массива располагаются в таком порядке, что при переходе к очередному элементубыстрее всего изменяется младший символ (правый индекс), во вторую очередь меняется второй с права индекс и т.д.

При инициализации многомерного массива допустимы две его интерпретации:

1.Как "массив массивов" при этом значение каждого элемента массива заключают в фигурные скобки. 2.Как общего списка элементов в котором размерность массива явно не просматривается.

Примеры. 1 способ.

double mass2[3][2]={{10,20},{30,40},{50,60}};//(2) все элементы массива инициализируются последовательно

(см построчное расположение эл в массиве *)

duable mass2[3][2]={{10},{30,40},{50,60}};//(3) Первый элемент массива (первая строка матрицы *) явно инициализируется частично, т.е. значение получаеттолько первый элемент (mass2[0][0]). Остальные элементы массива (вторая и третья строчки матрицы*) инициализируется полностью.

double mass2[3][2]={{10,20},{30,40}};//(4) явно инициализируем первые два элемента массива.

2 способ

duable mass2[3][2]={10,20,30};//(5) элементы многомерного массива здесь инициализируются последовательно,причем инициализировано будетстолькоподряд расположенных элементов, начиная с элемента mass2[0][0] сколько значений указано в списке.

Значение элементов массива,как переменных с индексом могут задаваться ипо-отдельности, т.е. допустимы, например,такие операторы

mass2[1][0]=100;//(6) duable x;//(7) x=mass2[1][0];//(8)

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

Напомним суть этих правил для случая трехмерного статического массива.

duable mass3[N][M][L];//(9)

тогда при обращении к элементам mass3[i][j][k] компилятор выполнит такую последовательность действий:

-берется адрес начала массива, т.е. целочисленноезначение указателя mass3=(unsigned long)mass3. Здесь выражение (unsigned long) - операция приведения значения к нужному типу.

-добавляется смещение равное i*(M*L)*sizeof(duable) для вычисления начального адреса i-того элементамассива размером M*L входящего в исходный трехмерный массив.

-Добавляется смещение для вычисленияначального адреса j-той строки двухмерного массива, т.е. одномерного массива, включающего l элементов. Теперь смещение станет равным(i*(M*L)+j*L)*sizeof(duable)

-добавляется смещение для получения адреса k -гоэлемента-строки. В результате получаем адрес этого элемента ADR=(unsigned long)(i*(M*N)+j*L+k)*sizeof(duable)

-Применяется операция разыменования *ADS т.е. обеспечивается доступ к значению элемента.

Все перечисленные операции включая операцию разыменования осуществляются неявно.

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

Пример.

int massM[7][5][3];//(10) статический массив массивов, т.е. одномерный массив из 7 элементов,причем каждый элемент - это двухмерный массив 5*3 состоящий из элементов типа int.

int (*pM)[5][3];//(11) это указатель на двухмерный массив элементов типа int

pM=massM;//(12) передача значения одного указателя другому. После этого pM становится псевдонимом имени massM,т.е. через него можно обращаться к элементам массива (без операцииразыменования, здесь ее применять нельзя)

pM[2][1][0]=777;//(13) разыменование будет проводиться неявно.

__________________________________________________________________________________

25.Динамические массивы.

__________________________________________________________________________________

Операции new и delete для работы с динамической авмятью можно применять и к массивам. Рассмотрим случай с одномерным массивом. Формат применения операции new рассмотрим на следующем примере

int*t=new int[3];//(1) определен указатель на объекты типа int и ему передается адрес первого элемента динамического массива, создаваемого операцией new. Массив должен состоять из элементов того же типа,который былиспользован при определении указателя. Допустимы разбиения:

int*t;//(1') определен указатель на объект типа int/ t=new int[3];//(1'') Передаем адрес первого элемента

Вобоих случаях нужно обратить внимание на синтаксис выражения,в которое входит операция new: после слова new указывается опять тип элемента массива и в квадратных скобкахколичество элементов массива.

Вотличие от статических массивов динамические массивы нельзя инициализировать и они автоматически не обнуляются. Это правило распространяется на динамические массивы любой размерности.

Пример обращения к элементам массива через указатель:

t[i]=10;//(2) не нужна операция разыменования, т.к.t это указатель и он используется вместо имени массива, который по смыслу также является указателем.

Допустим и способ обращения к элементам массива с помощью операции разыменования в рамках адресной арифметики.

*(t+i)=10;//(2')

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

delete[]t;//(3) здесь квадратные скобки обязательны ,но количество элементов и их тип не указываются, т.к. он был указан ранее и количество элементов тоже.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]