Оператор безусловного перехода

ВВЕДЕНИЕ

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

Кризис программного обеспечения привел к необходимости создания нового способа разработки программ, который снижал бы общие затраты на протяжении всего цикла – от замысла до завершения эксплуатации. Такая технология появилась в начале 70-х годов ХХ в. и была названа структурным программированием. В его основе лежит сочетание теории программирования и личного опыта высококвалифицированных программистов, а также учет современных требований к программам и промышленного характера их производства.

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

Данные методические указания посвящены основам структурного программирования на языке С++.

В последующих частях рассматривается развитие идеи структурного программирования – модульное программирование и объектно-ориентированное программирование.

Состав языка

Алфавит языка

Алфавит языка включает:

- строчные и прописные латинские буквы и знак подчеркивания;

- арабские цифры;

- специальные знаки;

- пробельные символы (символ табуляции, перехода на новую строку и пробел).

Из символов алфавита формируются лексемы языка:

- идентификаторы:

- ключевые (зарезервированные) слова;

- знаки операций;

- константы;

- разделители (скобки, точка с запятой, пробельные символы).

Идентификаторы

Идентификатор – это имя объекта программы. В имени могут использоваться латинские буквы, цифры и знак подчеркивания. Прописные и строчные буквы различаются, например, max, MAX, Max – три различных имени. Первым символом не может быть цифра, но может быть знак подчеркивания. Нельзя использовать внутри имени пробелы. Длина идентификатора не ограничена по стандарту, но некоторые компиляторы налагают на нее ограничения.

!!! В качестве имени нельзя использовать зарезервированные слова.

Зарезервированные (ключевые) слова

Ключевые слова – это зарезервированные идентификаторы. Их можно использовать только в том смысле, в котором они определены. В таблице представлен список зарезервированных слов языка С++.

Список ключевых слов С++

Ключевое слово Ключевое слово Ключевое слово Ключевое слово
asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit extern export false float for friend goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this threw true try tyopedef typeid typename union unsigned using virtual void volatile wchar_t while  

Знаки операций

Знак операции – это один или несколько символов, определяющих действие над данными (операндами). Внутри знака операции пробелы не допускаются. Операции делятся на унарные (имеющие один операнд), бинарные (два операнда) и тернарную (три операнда). Один и тот же знак может интерпретироваться по- разному, в зависимости от контекста.

Константы

Константы – это неизменяемые величины. Различают целые, вещественные, символьные и строковые константы.

Примеры

8, 0, 223196 – целые (десятичные)

0хА, 0хВ8 – целые шестнадцатеричные

5.7 .001 35. – вещественные

‘A’ ‘z’ ‘db’ – символьные (один или два символа)

"Ivan" – строковая.

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

- кодов, не имеющих графического изображения (\ а – звуковой сигнал);

- символов апострофа (‘), обратной косой черты (\), знака вопроса (?) и кавычки (“).

Последовательности символов, начинающихся с обратной косой черты, называют управляющими или escape-последовательностями. Управляющая последовательность интерпретируется как один символ. Допустимые значения символов приведены ниже.

Управляющие последовательности символов Значение
\a \b \f \n Звуковой сигнал Возврат на шаг Перевод формата (страницы) Перевод строки  

Окончание таблицы

\r \t \v \\ \’ \” \? Возврат каретки Горизонтальная табуляция Вертикальная табуляция Обратная косая черта Апостроф Кавычка Вопросительный знак

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

“Издательство \ ” Наука \ ” ”

Комментарии

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

// Это комментарий

Для комментирования части строки используются двойные символы /* и */:

/*Это комментарий*/.

Типы данных

Тип данных определяет:

- внутреннее представление данных в памяти ЭВМ;

- множество значений, которые могут принимать величины этого типа;

- операции и функции, которые можно применять к величинам этого типа.

Все типы языка С++ можно разделить на основные и составные. В языке С++ определено шесть основных типов данных для представления целых, вещественных, символьных и логических величин. На основе этих типов программист может вводить описание составных типов. К составным типам относятся массивы, перечисления, функции, структуры, ссылки, указатели, объединения и классы.

Основные типы данных

Для описания основных (стандартных) типов определены следующие ключевые слова:

- int (целый);

- char (символьный);

- wchar_t (расширенный символьный);

- bool (логический);

- float (вещественный);

- double (вещественный с двойной точностью).

Первые четыре типа называют целочисленными (целыми), последние два − типами с плавающей точкой.

Существуют четыре спецификатора типа, уточняющие внутреннее представление и диапазон значений стандартных типов:

- short (короткий);

- long (длинный);

- signed (знаковый);

- unsigned (беззнаковый).

Размер типа int не определен стандартом, а зависит от компьютера и компилятора. Для 16-разрядного процессора под величины этого типа отводится 2 байта, для 32-разрядного − 4 байта. Спецификатор short перед int указывает компилятору, что под число требуется выделить 2 байта.

При использовании спецификатора signed старший бит числа интерпретируется как знак ( 0 − положительное число, 1 − отрицательное). Спецификатор unsigned позволяет представлять только положительные числа. По умолчанию все целые типы считаются знаковыми, т.е. спецификатор signed можно опускать.

Под величину символьного типа char отводится количество байт, достаточное для размещения любого символа из набора символов для данного компьютера. Как правило, это 1 байт. Тип char может быть со знаком и без знака. В величинах со знаком можно хранить значения от -127 до +127. При использовании спецификатора unsigned значения этого типа могут находиться в пределах от 0 до 255.

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

Логический тип bool может принимать значения только true (истина) и false (ложь). Внутренне представление false − 0. Любое другое значение интерпретируется как true.

Cтандарт C++ определяет три типа данных для хранения вещественных чисел: double, float и long double. В IBM − совместимых компьютерах величины типа float занимают 4 байта, double − 8 байтов, а long double − 10 байтов

Тип void

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

Структура программы

Программа на языке С++ состоит из функций, описаний и директив препроцессора. Одна из функций должна иметь имя main. Выполнение этой программы начинается с первого оператора этой функции. Простейшее определение функции имеет вид:

тип_возвращаемого_результата имя_функции ([параметры])

{ операторы, составляющие тело функции

}

В определении функции следует отметить следующие моменты:

1. Если функция не должна возвращать результат, указывается тип void.

2. Тело функции является блоком и заключается в фигурные скобки.

3. Функции не могут быть вложенными.

4. Каждый оператор заканчивается точкой с запятой (кроме составного оператора).

Пример. Структура программы, содержащей функции main, f1 и f2.

Директивы препроцессора

Описания

int main ( ) {

операторы главной функции

}

int f1 ( ) {

операторы функции f1

}

int f2 ( ) {

операторы функции f2

}

Программа может состоять из нескольких модулей (исходных файлов).

Переменные и выражения

Переменные

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

Общий вид оператора описания переменных:

[класс памяти] [const] тип имя [инициализатор];

Здесь квадратные скобки [ ] указывают на необязательный параметр, т.е. параметр, который может опускаться. Класс памяти может принимать значения: auto, extern, static и register (см. далее). Модификатор const показывает, что значение переменной изменять нельзя. Такую переменную называют константой. При описании переменной можно присвоить начальное значение, это называется инициализацией. Инициализатор можно записывать либо со знаком равенства, либо в круглых скобках. Константа должна быть инициализирована при объявлении.

Примеры

short int a=1;

const char c='c';

char s, sf='f'; //инициализация относится только к sf

int t(54);

float d=0.22, x(3), sum;

!!! Если тип инициализирующего значения не совпадает с типом переменной, выполняются преобразования типа по определенным правилам (см. далее).

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

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

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

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

Время жизни может быть постоянным (в течение выполнения всей программы) и временным (в течение выполнения блока).

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

Для задания класса памяти используются следующие спецификаторы:

- auto − автоматизированная переменная. Время жизни от начала до конца блока;

- extern − переменная определена в другом месте программы (в другом файле или дальше по тексту);

- static − статическая переменная. Время жизни постоянное;

- register − аналогично auto.

Примеры

int a; // 1 глобальная переменная

int main ( ) {

int b; // 2 локальная переменная

extern int x; // 3 определена в другом месте

static int c; // 4 статическая локальная

a=1; // 5 присваивание глобальной переменной

int a; // 6 локальная переменная а

a=2; // 7 присваивание локальной переменной а

return 0;

}

int x=4; // 8 определение и инициализация х

!!! Имя переменной должно быть уникальным в своей области действия (т. е. в одном блоке нельзя описать две переменные с одинаковыми именами).

Операции

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

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

Основные операции С++

Операция Описание
Унарные операции
++ увеличение на 1
-- уменьшение на 1
sizeof размер данного в байтах
~ поразрядное отрицание
! логическое отрицание
- унарный минус
+ унарный плюс
& взятие адреса

Продолжение таблицы

* разадресация
new выделение памяти
delete освобождение памяти
(type) преобразование типов
Бинарные операции и тернарная операция
* умножение
/ деление
% остаток от деления
+ сложение
- вычитание
<< сдвиг влево
>> сдвиг вправо
< меньше
<= меньше или равно
> больше
>= больше или равно
== равно
!= не равно
& поразрядная конъюнкция (и)
^ поразрядное исключающее или
| поразрядная дизъюнкция (или)
&& логическое и
|| логическое или
? : условная операция (тернарная)
= присваивание

Окончание таблицы

*= умножение с присваиванием
/= деление с присваиванием
%= остаток от деления с присваиванием
+= сложение с присваиванием
-= вычитание с присваиванием
<<= сдвиг влево с присваиванием
>>= сдвиг вправо с присваиванием
&= поразрядное и с присваиванием
|= поразрядное или с присваиванием
= поразрядное исключающее или с присваиванием
, последовательное выполнение

Примеры операций

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

...

int x = 3, y = 3;

printf ("Значение префиксного выражения: % d \ n", ++ x);

printf ("Значение постфиксного выражения: % d \ n", y ++ );

...

На экране:

Результат префиксного значения: 4

Результат префиксного значения: 3

Операция определения размера sizeof предназначена для вычисления размера объекта или типа в байтах и имеет две формы:

sizeof выражение

sizeof (тип)

Операция sizeof (float) вернет значение 4 (длина в байтах типа float).

Операция sizeof (х + 0.1) вернет значение 8 (если х описана как float) − эта величина получается потому, что вещественные константы по умолчанию описываются как double, т. е. имеют длину 6 байтов.

Операции присваивания могут использоваться в программах как законченные операции. В сложных операциях присваивания (+=, -= и т.д.) при вычислении значения в правой части используется значение из левой части:

...

int a = 3, b = 5, c =4;

c = b + c;

a += b; // это более компактная запись a = a + b

...

Условная операция ? : − это тернарная операция. Ее формат:

операнд_1 ? операнд_2 : операнд_3

Первый операнд оценивается с точки зрения эквивалентности его нулю (операнд, равный нулю, оценивается как false, не равный нулю − как true). Если результат вычисления операнда 1 равен true, то результатом условной операции будет значение операнда 2, иначе − операнда 3. Условная операция является сокращенной формой оператора if.

Пример

int a = 11, b = 4, max;

max = (b > a)? b : a;

printf ("Наибольшее число: % d", max);

На экране будет напечатано:

Наибольшее число: 11

Выражения

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

Примеры

(a + 0.12) / 6

x && y || ! z

(t + sin (x) - 1.5e4) / (2*k + 2 )

Операции выполняются в соответствии с приоритетом. Для изменения порядка выполнения операций используются круглые скобки. Если в одном выражении записано несколько операций одинакового приоритета, унарные операции, условная операция и операции присваивания выполняются справа налево, остальные − слева направо.

Примеры

a = b = c; // Данная запись означает a = (b = c)

a + b + c; // Это (a + b) + c

Результат вычисления выражения характеризуется значением и типом. Например, если a, b − переменные целого типа:

int a = 2, b =5;

то выражение a + b имеет значение 7 и тип int.

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

Преобразования бывают двух типов:

- изменяющие внутреннее представление величины;

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

К первому типу относится, например, преобразование целого типа в вещественное; ко второму − преобразование знакового целого в беззнаковое.

В программе можно задавать преобразования типов явным образом (см. тему “Преобразования типов” далее).

Функции ввода-вывода

Основные функции ввода-вывода в стиле С

int scanf (const char* format, ...) // ввод

int printf (const char* format, ...) // вывод

Данные функции выполняют форматированный ввод и вывод произвольного количества величин в соответствии со строкой format. Строка формата содержит символы, которые при выводе копируются в поток (на экран) или запрашиваются из потока (с клавиатуры) при вводе, а также спецификации преобразования, начинающиеся со знака %, которые при вводе и выводе заменяются конкретными величинами.

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

# include <stdio.h>

int main ( ) {

int i;

printf ("Введите целое число \n");

scanf ("%d", & i);

printf ("Вы ввели %d", i);

return 0;

}

Пояснения

В этой программе в первой строке подключается заголовочный файл, содержащий описания функций ввода-вывода. Функция printf в четвертой строке выводит сообщение на экран и переходит на новую строку в соответствии с управляющей последовательностью \n. Функция scanf заносит введенное с клавиатуры число в переменную i (знак & означает операцию получения адреса). Последовательность символов %d является спецификацией формата десятичных чисел.

Спецификации формата

c − аргумент рассматривается как отдельный символ.

d, i − аргумент преобразуется к десятичному виду.

e, E − аргумент преобразуется в десятичную форму в экспоненциальном виде.

f − аргумент преобразуется в десятичную форму с фиксированной десятичной точкой.

g, G − используется формат %e или %f, который короче; незначащие нули не печатаются.

O − аргумент преобразуется в беззнаковую восьмеричную форму.

p − вывод указателя в шестнадцатеричном формате.

s − аргумент является строкой.

u − аргумент преобразуется в беззнаковую десятичную форму.

x, X − аргумент преобразуется в беззнаковую шестнадцатеричную форму.

% − выводится символ %.

Модификаторы формата

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

- % - minC или %minC

- % - min.precisionC или % min.precisionC

Здесь С − это спецификации формата; min − число, задающее минимальную ширину поля. Смысл модификатора precision, задаваемого десятичным числом, зависит от спецификации формата, с которой он используется:

- при выводе строки (спецификация %s) precision указывает максимальное число символов для вывода;

- при выводе вещественного числа (спецификации %f или %e) precision указывает количество цифр после десятичной точки;

- при выводе целого числа (спецификации %d или %i) precision указывает минимальное количество выводимых цифр. Если число представляется меньшим числом цифр, чем указано в precision, выводятся ведущие нули.

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

Вывод в стиле С++

Для ввода-вывода в стиле С++ используются стандартные объекты-потоки cin для ввода с клавиатуры и cout для вывода на экран, а также операции помещения в поток << и чтения из потока >> (см. методические указания “Программирование на яыке С++. Часть 4. Стандартная библиотека)”.

Пример

# include <iostream.h>

int main ( ) {

int i;

cout << "Введите целое число \n";

cin >> i;

cout << "Вы ввели число" << i;

return 0;

}

Операторы

Оператор "выражение"

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

Примеры

i ++; // операция инкремента

a = d + c; // присваивание

fun (i, k); // вызов функции

Условный оператор if

Условный оператор if используется для разветвления процесса вычислений на два направления. Формат оператора:

if (выражение) оператор_1; [else оператор_2;]

Сначала вычисляется выражение. Если оно не равно нулю (имеет значение true), выполняется первый оператор, иначе − второй. Ветвь else может отсутствовать (квадратные скобки в данном случае означают необязательный параметр). Если в какой-либо ветви требуется выполнить несколько операторов, их необходимо заключить в блок (в фигурные скобки). Блок может содержать любые операторы, в том числе и описания.

Примеры

if (a < 0) b = 1;

if (a ++) b ++;

if (b > a) max = b; else max = a;

if (a < b && a > d) b ++; else {b = a; a =0;}

Распространенная ошибка при записи условных операторов − использование в выражениях вместо проверки на равенство ( = = ) простого присваивания ( = ), например:

if (a = 1) b =0;

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

Вторая ошибка − неверная запись проверки на принадлежность диапазону. Например, чтобы проверить условие 0 < x < 1, нельзя его записать в условном операторе непосредственно:

if ( 0 < x < 1)...;

Здесь тоже нет синтаксической ошибки. Правильный способ записи:

If (0<x && x<1) ...;

Задание

Написать программу, вычисляющую корни квадратного уравнения. Для вычисления квадратного корня воспользуйтесь функцией sqrt (заголовочный файл <math.h>).

Оператор switch

Оператор switch (переключатель) предназначен для разветвления процесса вычислений на несколько направлений. Формат оператора:

switch (выражение ) {

case константное_выражение_1 : [список_операторов_1]

case константное_выражение_2 : [список_операторов_2]

...

case константное_выражение_N : [список_операторов_N]

[default : операторы]

}

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

Пример. Программа, печатающая название нажатой пользователем цифры:

# include <iostream.h>

int main ( ) {

char op;

cout << "Нажмите любую цифру"; cin >>op;

switch (op) {

case '0' : cout << "Это цифра 0"; break;

case '1' : cout << "Это цифра 1"; break;

// Здесь надо поместить остальные ветви оператора switch

case '9' : cout << "Это цифра 9"; break;

default : cout << "Это не цифра ";

}

return 0;

}

Пояснения

Первая строка программы − это директива препроцессора. Она подключает заголовочный файл iostream.h, содержащий описания объектов-потоков ввода cin и вывода cout.

Оператор break прерывает дальнейшее выполнение ветвей переключателя.

Цикл с предусловием

Формат оператора:

while (выражение) оператор;

Выражение определяет условие повторения цикла, представленного простым или составным оператором. Если оно истинно, выполняется оператор тела цикла. Если при первой проверке выражение равно false, цикл не выполнится ни разу.

Задание

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

Цикл с постусловием

Формат оператора:

do оператор while (выражение) ;

Сначала выполняется простой или составной оператор, составляющий тело цикла, а затем вычисляется выражение. Если оно истинно, тело цикла выполняется снова. Цикл завершается, когда выражение станет равно false.

Цикл с параметром for

Формат оператора:

for (инициализация; выражение; модификации ) оператор;

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

for (int i = 0, j = 2; ...

int k, m;

for (k = 1, m = 0; ...

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

Выражение определяет условие выполнения цикла: если его результат, приведенный к типу bool, равен true, цикл выполняется. Цикл с параметром реализован как цикл с предусловием.

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

Тело цикла представляется простым или составным оператором.

Пример. Оператор, вычисляющий сумму чисел от 1 до 100:

int s=0;

for (int i = 1; i <=100; i ++) s += i;

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

Задание

Написатьпрограмму, вычисляющую таблицу значений функции y=sin(x), a<=х<=b, где границы интервала a, b и шаг изменения аргумента х задает пользователь с клавиатуры.Программу написать тремя способами, используя разные операторы цикла.

Операторы передачи управления

Оператор безусловного перехода

Формат:

goto метка;

В теле той же функции, где использован оператор goto, должна присутствовать одна конструкция вида:

метка: оператор;

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

!!! Не следует передавать управление внутрь операторов if, switch и циклов. Нельзя переходить внутрь блоков, содержащих инициализацию переменных, на операторы, расположенные после нее, поскольку в этом случае инициализация не будет выполнена.

Оператор break

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

Оператор continue

Оператор перехода к следующей итерации цикла continue пропускает все операторы, оставшиеся до конца тела цикла, и передает управление на начало следующей итерации.

Оператор return

Оператор возврата из функции return завершает работу функции и передает управление в точку ее вызова.

Формат оператора:

return [выражение];

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

Указатели и ссылки

При выполнении оператора определения переменной, например,

int i =10;

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

В С++ различают три вида указателей − указатели на объект, на функцию и на void, отличающиеся свойством и набором операций.

Указатели на функции будут рассмотрены в теме "Функции" методических указаний “Программирование на языке С++. Часть 2. Модульное программирование”.

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

тип * имя;

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

Примеры

int *a;

float *b, *c, *d;

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

Указатель может быть константой или переменной, а также указывать на константу или переменную.

Примеры

int i; // целая переменная

const int ci; // целая константа

int *pi; // указатель на целую переменную

const int *pci; // указатель на целую константу

int *const cp=&i; // указатель-константа на целую переменную

const int *const cpc=&ci; // указатель-константа на целую константу

Инициализация указателей

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

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

Существуют следующие способы инициализации указателей:

1. Присваивание указателю адреса существующего объекта:

int a = 5;

int *p = &a;

int *p1(&a); // то же, что и предыдущая запись

int *r = p;

2. Присваивание указателю адреса области памяти в явном виде:

char *vp = (char*)0xD8000000;

здесь 0xD8000000 − шестнадцатеричная константа; (char*) − операция приведения типа: константа преобразуется к типу "указатель на тип char".

3. Присваивание пустого значения:

int *s = 0;

int *p = NULL;

здесь NULL − это константа-указатель, равный 0.

4. Выделение участка динамической памяти и присваивание ее адреса указателю:

int *n = new int;

int *m = new int (10); // используется операция выделения памяти

int *u = (int*) malloc (sizeof (int)); // используется функция из библиотеки С

Освобождение памяти, выделенной с помощью операции new, должно осуществляться с помощью операции delete, а памяти, выделенной функцией malloc, − с помощью функции free. При этом переменная-указатель сохраняется и может повторно быть инициализирована.

Примеры

delete n;

delete m;

free (u);

Операции с указателями

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

Операция разадресации, или разыменования, предназначена для доступа к величине, адрес которой хранится в указателе:

char a;

char *p = new char;

*p = 'A'; a = *p;

unsigned long int A = 147483647;

unsigned short int *pi = (unsigned short int*)&A;

В последней строке происходит явное приведение типов.

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

Ссылки

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

тип & имя;

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

Примеры

int kol;

int & pal = kol; // ссылка pal − альтернативное имя kol

const char & cr = '\n'; // ссылка на константу

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

1. Переменная-ссылка должна явно инициализироваться при ее описании.

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

3. Тип ссылки должен совпадать с типом величины, на которую он ссылается.

4. Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

Ссылки чаще всего применяются в качестве параметров функций и типов возвращаемых функциями значений (см. методические указания “Программирования на языке С++”. Часть 2. “Модульное программирование”).

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

Массивы

Одномерные массивы

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

float a[10];

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

int b[5] = {3, 2, 1} // элементы b[3] и b[4] обнуляются

Размерность массивов предпочтительнее задавать с помощью именованных констант:

const int n = 10;

int marks[n]={4, 4, 5, 3, 4};

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

Динамические массивы создаются с помощью операции new, при этом необходимо указать тип и размерность, например:

int n = 100;

float *p = new float [n];

В этом примере создается переменная-указатель на float, в динамической памяти отводится непрерывная область, достаточная для размещения 100 чисел вещественного типа, и адрес ее начала записывается в указатель p. Динамические массивы нельзя при создании инициализировать, и они не обнуляются.

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

p[5] или через указатель *(p+5).

Альтернативный способ создания динамического массива − использование функции malloc библиотеки С:

int n =100;

float *q = (float*)malloc(n*sizeof(float));

Память, зарезервированная под динамический массив с помощью new [ ], должна освобождаться с помощью оператора delete [ ], а память, выделенная с помощью функции malloc, − посредством функции free:

delete [ ] p;

free (q);

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

Инкремент перемещает указатель к следующему элементу массива, декремент − к предыдущему. Фактически значение указателя изменяется на величину

sizeof (тип). Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженной на размер объекта данного типа, например:

short int *p = new short [5];

p++; // значение p увеличивается на 2 (байта)

long *q = new long [5];

q++; // значение q увеличивается на 4 (байта)

Выражение (*p)++ инкрементирует значение, на которое ссылается указатель.

При записи выражения с указателями следует обращать внимание на приоритет операций. Рассмотрим такой пример:

*p++ = 10;

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

*p = 10; p ++;

Задания

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

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

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

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

Многомерные массивы задаются указателем каждого измерения в квадратных скобках, например:

int matr [6] [8];

Здесь задается описание двумерного массива из 6 строк и 8 столбцов. В памяти такой массив располагается в последовательных ячейках построчно. Для доступа к элементу массива указываются все его индексы: matr [i] [j] или *(matr[i] + j)

или *(*matr + i ) +j).

!!! При обращении к элементам массива автоматический контроль выхода индекса за объявленные границы не производится.

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

int mass1 [3] [2] = {{1, 1}, {0, 1}, {1, 0}};

int mass2 [3] [2] = {1, 1, 0, 1, 0, 1};

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

int nstr = 5;

int **m = (int**) new int [nstr] [10];

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

int nstr, nstb;

cout << "Введите количество строк и столбцов";

cin >> nstr >> nstb;

int **a = new int *[nstr];

for (int i = 0; i < nstr; i + +)

a[i] = new int [nstb];

...

Задание

Написать программу, вычисляющую среднее арифметическое для каждого столбца и каждой строки двумерного числового массива A[4, 5].

Строки

Строка представляет собой массив символов, заканчивающийся нуль-символом. Нуль-символ − это символ с кодом, равным нулю, что записывается в виде управляющей последовательности '\0'. По положению нуль-символа определяется фактическая длина строки. Строку можно инициализировать строковым литералом.

Пример

char str[10] = "Привет";

// выделено 10 элементов с номерами от 0 до 9

Если строка при определении инициализируется, ее размерность можно опускать:

char str[ ] = "Привет";

Оператор

char *str = "Привет";

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

Объявление:

сhar *str [10];

Задает массив из десяти строк.

Операция присваивания значения одной строки другой не определена и может выполняться с помощью цикла или функций стандартной библиотеки (см. раздел "Функции работы со строками и символами" методических указаний “Программирование на языке С++. Часть 2” ).

В стандартной библиотеке С++ определен класс string, который обеспечивает индексацию, присваивание, сравнение и ряд других операций со строками (см. раздел "Строки" в методических указаниях “Программирование на языке С++. Часть 4”).

!!! При вводе с клавиатуры с помощью стандартного потока cin ввод производится до первого пробела, все остальные символы игнорируются. Например:

char str [100];

cin >> str;

При вводе с клавиатуры строки "один два три" в переменную str поместится текст "один". При необходимости ввода строки целиком (т.е. до символа '\n') следует использовать методы потоковых классов get и getline (см. тему "Потоковые классы").

Пример. Программа, запрашивающая пароль у пользователя:

# include <stdio.h>

# include <string.h> //подключаем библиотеку работы со строками

int main ( ) {

char s[5], passw[ ] = "bond";

printf ( "Введите пароль: \n");

gets(s); // функция ввода строки

if (strcmp(s, passw) = = 0) printf ("\n Пароль верен \n");

// strcmp( ) − функция сравнивает две строки

else printf ("\n Пароль не верен \n");

return 0;

}

Задание

Написать программу, выполняющую частотный анализ строки, введенной пользователем (т. е. вычисляющей, сколько раз в строку входит данный символ). Рапорт по каждому символу выдавать только один раз (например, символ “a” встречается в строке несколько раз, рапорт должен быть выдан один раз).

Типы данных, определенные пользователем

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

Переименование типов

Типу можно задать новое имя с помощью ключевого слова typedef:

typedef тип новое_имя [размерность];

Примеры

typedef unsigned int UINT;

typedef char Msg [100];

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

UINT i, j;

Msg str [10];

Перечисления

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

enum [имя_типа] {список констант};

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

Пример

enum digit {one = 1, two = 2, three = 3};

enum Err {ERR_READ, ERR_WRITE, ERR_CONVERT};

Err error;

digit dig;

Константам ERR_READ, ERR_WRITE, ERR_CONVERT присваиваются (автоматически) значения 0, 1, 2 соответственно.

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

Структуры

Структура может содержать элементы различных типов:

struct [имя_типа] {

тип_1 элемент_1;

тип_2 элемент_2;

тип_n элемент_n;

} [список описателей];

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

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

Пример

struct {

char fio [30];

int date, code;

double salary;

} stuff [100], *ps; // определение массива структур и указателя на структуру

Пример

struct Worker {

char fio [30];

int date, code;

double salary;

}; // описание типа Worker

Worker staff [100], *ps; // описание массива и указателя типа Worker

Для инициализации структуры значения ее элементов перечисляют в фигурных скобках в порядке их описания:

struct {

char fio [30];

int date, code;

double salary;

} worker = {"Иванов", 31, 215, 3400.50};

При инициализации массивов структур следует заключать в фигурные скобки каждый элемент массива.

Для переменных одного и того же структурного типа определена операция присваивания. Структуру можно передавать в функцию и возвращать в качестве значения функции.

Доступ к полям структуры выполняется с помощью операций выбора . (точка) при обращении к полю через имя структуры и -> при обращении через указатель:

Worker staff [100], worker;

strcpy(worker.fio,”Ivanov”); // используем функцию копирования строки

// из библиотеки C (<string.h>) для передачи значения в строковое поле

stuff[1].code = 215;

Worker *ps = new Worker; // Задание указателя типа Worker

ps ->salary = 1200.12; // Вариант (*ps).salary = 1200.12;

Задание

Написать программу, позволяющую ввести в память следующую информацию о сотрудниках организации (объем списка задает пользователь) :

(ФИО, должность, возраст) и выполняющую поиск сотрудников по заданной должности или возрасту.

БИБЛИОГРАфичеСКИЙ СПИСОК

1. Павловская Т.А. С / C++. Программирование на языке высокого уровня / Т.А. Павловская. СПб, 2002.

2. Павловская Т.А. С / C++. Структурное программирование: практикум/ Т.А.Павловская, Ю.А. Щупак. СПб, 2002.

3. Павловская Т.А. С / C++. Объектно-ориентированное программирование: практикум/ Т.А.Павловская, Ю.А.Щупак. СПб, 2002.

4. Культин Н.Б. С++ Builder./ Н.Б.Культин. СПб, 2004.

ОглавлеНИЕ

ВВЕДЕНИЕ. 3

Состав языка.. 4

Алфавит языка. 4

Идентификаторы.. 4

Зарезервированные (ключевые) слова. 4

Знаки операций. 5

Константы.. 6

Комментарии. 7

Типы данных.. 7

Основные типы данных. 8

Тип void. 9

Структура программы.. 9

Переменные и выражения.. 11

Переменные. 11

Операции. 13

Примеры операций. 15

Выражения. 17

Функции ввода-вывода.. 18

Основные функции ввода-вывода в стиле С.. 18

Спецификации формата. 19

Модификаторы формата. 19

Вывод в стиле С++. 20

Операторы.. 21

Оператор "выражение". 21

Условный оператор if 21

Оператор switch. 22

Цикл с предусловием. 24

Цикл с постусловием. 24

Цикл с параметром for 24

Операторы передачи управления. 25

Оператор безусловного перехода. 25

Оператор break. 26

Оператор continue. 26

Оператор return. 26

Указатели и ссылки.. 26

Инициализация указателей. 28

Операции с указателями. 29

Ссылки. 30

Массивы.. 31

Одномерные массивы.. 31

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

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

Переименование типов. 37

Перечисления. 37

Структуры.. 38

БИБЛИОГРАфичеСКИЙ СПИСОК.. 41

ПРОГРАММИРОВАНИЕ НА ЯЗЫКЕ С++

Наши рекомендации