
- •Максимов м.Н.
- •3. Скалярные типы и выражения 51
- •5. Адреса, указатели, массивы, память 95
- •6. Функции, указатели, ссылки 132
- •7 Структуры, объединения и классы 170
- •Введение
- •Модуль 1
- •1.2. Этапы подготовки исполняемой программы
- •1.3. Системы счисления
- •Представление чисел от 0 до 16 в разных системах счисления
- •2.1. Общие сведения о программах, лексемах и алфавите
- •2.2. Идентификаторы и служебные слова
- •2.3. Типы данных
- •2.4. Константы
- •Типы, выбираемые компилятором по умолчанию для целых констант
- •Данные вещественного типа
- •2.5. Операции
- •2.6. Разделители
- •3. Скалярные типы и выражения
- •3.1. Определение и описание переменных
- •3.2. Явное и неявное преобразование типа
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Модуль 2
- •4.1. Последовательно выполняемые операторы
- •4.2. Операторы выбора
- •If( выражение) оператор_1 else оператор_2
- •If( выражение) оператор_1
- •4.3. Операторы цикла
- •4.4. Операторы передачи управления
- •If (условие) break;
- •4.5. Примеры численного моделирования цепей первого порядка
- •5. Адреса, указатели, массивы, память
- •5.1. Указатели и адреса объектов
- •5.2. Адресная арифметика, типы указателей и операции над ними
- •5.3. Свойства указателя типа void*
- •5.4. Свойства объекта cout
- •5.5. Массивы и указатели
- •5.6. Многомерные массивы, массивы указателей, динамические массивы
- •Проектные задания к модулю
- •Тесты рубежного контроля
- •6.2. Функции с переменным количеством параметров
- •6.3. Рекурсивные функции
- •6.4. Подставляемые (инлайн-) функции
- •6.5. Функции и массивы
- •6.6. Указатели на функции
- •Void f3(float) (...) // Определение функции
- •Int* f4(char *){...} // Определение функции
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Модуль 4
- •7 Структуры, объединения и классы
- •7.1 Структура как тип и совокупность данных
- •7.3 Объединения разнотипных данных
- •7.4 Деревья
- •7.5 Битовые поля структур и объединений
- •7.6 Компонентные функции структурированных объектов
- •7.7 Расширение действия (перегрузка) стандартных операций
- •7.8 Доступ к компонентам структурированного объекта
- •7.9 Классы и шаблоны
- •Проектные задания
- •Тесты рубежного контроля
- •Квалиметрическая оценка
- •Список литературы
- •Приложение 1
- •Приложение 2 Стандартная библиотека функций языка Си
5.4. Свойства объекта cout
С помощью объекта cout класса iostream и перегруженной операции << на экран монитора выводится значение выражения стоящего справа от <<, причем формат вывода на экран значения выражения зависит от его типа. Особо следует выделить случай, когда значение выражения имеет тип char, unsigned char, char*, unsigned char*.
Например
char st1 = ‘S’, st2 = 83;/*Определение и инициализация переменных st1 и st2. Причём st1 и st2 равны друг другу, так как символьная константа ‘S’ в соответствии с таблицей ASCII имеет код 83.*/
int i = 83;
cout<<”\n st1 = “<<st1<<”\t st2 = “<<st2<<”\t i = “<<i; /*Вывод на экран значений st1, st2 и i. Поскольку st1 и st2 имеют тип char, то печатается не их значение, а соответствующий этому значению по таблице ASCII символ, т е. будет напечатано st1 = S, st2 = S. Переменная i имеет тип int, поэтому печатается просто её значение i = 83.*/
char *pc = “Язык Си++”; /*Строковая константа, состоящая из 9 байт. Первые восемь байт содержат числовые коды соответствующих символов, а последний девятый байт содержит код 00 и является признаком конца строковой константы. Указатель pc инициализируется адресом первого байта. */
int i = 83, *pi=NULL;
pi = &i;
cout<<”\n pc = “<< pc<<”\t pi = “<<pi; /* На экране печатается pc = Язык Си++ pi = 532465*/
Объект cout по-разному ведет себя c указателями типов char*, unsigned char* и указателями других типов. Так, вместо значения pc (адреса), объект cout начиная с адреса, записанного в pc и до байта с кодом 00, воспринимает фрагмент памяти как фрагмент, содержащий строковую константу, и печатает её на экране. Если по каким-либо причинам строковая константа не была завершена кодом 00, то объект cout выведет строковую константу и будет выводить далее содержимое всех байтов в символьном виде до тех пор, пока не наткнется на байт с кодом 00, т.е. за строковой константой будет выведен какой-то мусор. При выводе значения указателя pi типа int на экран будет выведено просто значение pi, т. е. число являющееся адресом переменной i. Пример:
char p[5] = {‘М’, ‘и’, ‘р’}; /*определен массив и инициализированы первые три его элемента */
cout<<p; //Печатает Мир и далее мусор
p[3] = ‘\0’; //Управляющий символ ‘\0’, который записывает в p[3] код 0x00
cout<<p; //Печатает просто Мир без мусора
5.5. Массивы и указатели
Определение массива
type имя_массива [константное_выражение] инициализатор;
где type – тип элементов массива; имя_массива – идентификатор массива; инициализатор – не обязательное инициализирующее выражение; константное_выражение – выражение, значение которого вычисляется во время компиляции, а не во время выполнения программы. Это значит, что во время выполнения программы нельзя увеличивать размерность массива. Поэтому такие массивы называются статическими.
Инициализировать элементы массива можно следующим образом:
int f[] = {2, 5, ‘S’}; /*Вместо символа S компилятор подставит соответствующее ему по таблице ASCII число. Размер массива равен трем, задаётся неявно с помощью инициализатора.*/
char a[5] = {2, 5, ‘S’}; /* Размер массива равен 5 задаётся явно. Первые три элемента массива инициализируются соответствующими значениями. */
char a[2] = {2, 5, ‘S’};// Ошибка инициализации
Для массивов типа char принят ещё один способ инициализации:
char pc[] = “Привет всем”;/*Размер массива равен 12, задаётся неявно и определяется количеством символов, заключенных в кавычки, плюс невидимый, так называемый нулевой символ ‘\0’. Полным аналогом такого способа инициализации является строка char pc[] = {‘П’, ‘р ’, ’и’, ’в’, ’е’, ’т’,’ ’,’в’,’с’,’е’,’м’,’\0’};*/
Также как переменная, массив может быть определен в программе один раз, но много раз может быть описан следующим образом:
extern type имя_массива[];
где type и имя_массива должны совпадать с типом и именем массива определенного где-то в программе. Type можно опустить, в этом случае по умолчанию будет предполагаться, что type имеет тип int. Пример:
extern f[]; //Описание массива типа int, определенного где-то в программе.
extern char pc[];/*Описание массива типа char, определенного где-то в программе.*/
Если массив был описан, но не определен, то на этапе компоновки будет выдано сообщение об ошибке.
Над именем массива определена операция sizeof(имя_массива). Результатом этой операции является количество байт, выделенных под массив. Используя эту операцию можно определить, сколько элементов имеется в массиве, например:
double k[5];
cout<<”\n Количество элементов в массиве k = “;
cout<<sizeof(k)/sizeof(double); //
Результат работы программы:
Количество элементов в массиве k = 5
int ar []= { 4, 6,7,8,7};
for(int i = 0; i<sizeof(ar)/sizeof(int); i ++) cout<<”\t ar[ “<< i<<”]= ”<<ar[i];
Доступ к конкретному элементу массива можно выполнить с помощью операции имя_массива[ индекс], причём программа должна быть написана так, чтобы изменение индекса лежало в пределах интервала [0, константного_выражения–1]. При выходе индекса за пределы интервала программа начинает обращаться к памяти, которая не была выделена под массив и, следовательно, результаты работы такой программы непредсказуемы. Ни компилятор, ни компоновщик не проверяют диапазон изменения индекса, программист сам должен обеспечивать выполнение правила обращения к элементам массива.
Имя массива – это константный указатель, значение которого равно адресу первого элемента массива. Единственное отличие, которое есть, например, между именем массива ai и константным указателем pi, определенным ниже как
long ai[5];
const long * pi = &ai[0];/* или const long * pi = ai; т.е. &ai[0] = = ai заключается в результате выполнения операции sizeof(), так при выполнении операции sizeof(ai) результат будет равен 20, а sizeof(pi) результат будет равен 4. Поскольку ai это имя массива, которое является константным указателем, а pi – константный указатель, настроенный на первый элемент массива. */
Во всем остальном они подобны. Это означает, что к указателям можно применять операцию [ ], а к именам массивов все правила адресной арифметики, связанной с указателями. Более того, запись имя_массива[ индекс] является выражением с двумя операндами. Первый из них, т.е. имя_массива – это константный указатель – адрес начала массива в основной памяти; индекс это выражение целого типа, определяющее смещение от начала массива. Используя операцию обращения по адресу *, действие бинарной операции [] можно объяснить так: *(имя_массива + индекс). Поскольку операция сложения коммутативна, то можно записать и так *(индекс+ имя_массива), а, следовательно, операцию [], можно представить в виде индекс[имя_массива]. Например, обращение к элементам массива ai через имя массива или указатель pi, которые были определены выше, можно записать
ai[0] == pi[0] == *ai == *pi == 0[ai] == 0[pi],
ai[1] == pi[1] == *(ai+1) == *(pi+1)== 1[ai] == 1[pi],
ai[4] == pi[4] == *(ai+4) == *(pi+4)== 4[ai] == 4[pi].
Но ошибкой будет запись *(ai++), или *(pi++), или *(pi = pi+4), или *(ai = ai+4), так как ai и pi константные указатели. Необходимо также помнить, что при прибавлении целого числа (индекса) к указателю на самом деле прибавляется число равное индекс*sizeof(type), где type тип объекта, к которому отнесен указатель.
Использовать операцию [] c указателями очень удобно. Она коротка с точки зрения записи, интуитивно понятна, не изменяет значение указателя и может использоваться и с неконстантными указателями. Например, напишем функцию, которая в обратном порядке выводит на экран содержимое строки.
//Программа 5.4
#include "stdafx.h"
#include <iostream>
void fstr(char* pst){
int strlen = 0;
for(; pst[strlen] != '\0'; strlen++); /*Определяем длину строки. Телом цикла является пустой оператор. */
for(int i = strlen-1; i >= 0; i--) std::cout<< pst[i]; /*Выводим строку посимвольно в обратном порядке на экран, используя указатель pst и операцию []. */
}
void main(){
char p [] = "Example work of operation []";
fstr(p);/*При вызове функции формальному параметру pst присваивается значение фактического параметра p. Таким образом, указатель pst настраивается на первый элемент массива р.*/
getchar();
}