Алфавит языка С (используемые символы)

Используемые в языке С символы условно можно разделить на 5 групп:

— прописные и строчные буквы латинского алфавита, а также символ подчёркивания;

— группа прописных и строчных букв русского алфавита;

— арабские цифры от 0 до 9 и шестнадцатеричные цифры от A до F;

— пробельные символы: пробел, символы табуляции, перевода строки, возврата каретки;

— специальные знаки, которые представлены в таблице 3.1.

Таблица 3.1 — Специальные символы

Символ Наименование Символ Наименование
, запятая ) круглая скобка правая
. точка ( круглая скобка левая
; точка с запятой } фигурная скобка правая
: двоеточие { фигурная скобка левая
? вопросительный знак < меньше
апостроф > больше
! восклицательный знак [ квадратная скобка левая
| вертикальная черта ] квадратная скобка правая
/ дробная черта # номер
\ обратная черта % процент
~ тильда & амперсант
* звёздочка ^ логическое НЕ
+ плюс = равно
минус '' кавычки

Символы используются для формирования лексем, выражений и операторов (см. рис.3.1).

Рис. 3.1 — Схема образования элементов языка

Лексема — это элементарная конструкция, т.е. минимальная единица языка, имеющая самостоятельный смысл.

Лексемы можно разделить на 5 групп:

— идентификаторы;

— константы;

— ключевые слова;

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

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

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

Идентификатор — это имя программного объекта: имя переменной, имя функции, имя метки, имя константы.

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

Замечания:

— прописные и строчные буквы различаются: Abc≠abc≠aBc≠ABC;

— первым символом не может быть цифра;

— длина идентификатора не ограничена, однако некоторые компиляторы различают ограниченное количество первых символов, например 32;

— идентификатор создаётся на этапе объявления переменной, функции, типа и т.п., после этого его можно использовать;

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

1) идентификатор не должен совпадать с ключевыми словами и именами стандартных объектов языка;

2) не рекомендуется начинать идентификатор с символа подчёркивания, т.к. они могут совпасть с системными функциями и переменными.

Константы

Константы — неизменяемые величины.

Как показано в табл. 3.2, константы бывают:

— целые;

— вещественные (с плавающей запятой);

— символьные;

— строковые.

Таблица 3.2 — Форматы констант

Константа Формат Примеры
Целая Десятичный: Последовательность десятичных цифр (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 8, 0, 1265, 19912
Восьмеричный: Нуль, за которым следуют восьмерич-ные цифры (0, 1, 2, 3, 4, 5, 6, 7) 01, 020, 075346
Шестнадцатеричный: 0х или 0Х, за которыми следуют шестнадцатеричные цифры (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F) 0хА, 0х1B8, 0X40FA
Вещественная Десятичный: [цифры].[цифры] 5.7,.001, 34
Экспоненциальный: 0.25E6, 0.11e–4, 5e3
Символьная Один или два символа, заключенные в апострофы 'A', 'ю', 'дБ', '\'
Строковая Последовательность символов, заключенная в кавычки ''Здесь был Федя'', ''Значения перемен-ной''

Замечания.

Для формирования отрицательной целой или вещественной константы используется знак «–» (минус).

В экспоненциальном виде число имеет следующий вид

мантисса Е порядок

.

Символьные константы, состоящие из одного символа занимают в памяти один байт (и имеют тип char).

Двухсимвольные константы занимают два байта (и имеют тип int). Первый символ размещен в байте с меньшим адресом.

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

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

Таблица 3.3 — Управляющие последовательности в языке С/С++

Последова-тельность Шестнадцате-ричный код Назначение
\a   Звуковой сигнал
\b   Возврат на шаг
\f 00С Перевод страницы
\n 00A Перевод строки
\r 00D Возврат каретки
\t   Горизонтальная табуляция
\v 00B Вертикальная табуляция
\\ 05C Обратная косая черта
\’   Апостроф
\’’   Кавычки
\? 03F Вопросительный знак
\0ddd Восьмеричный код символа
\0xddd ddd Шестнадцатеричный код символа

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

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

''Произведение\''Война и мир\'' ''

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

''Севастопольский национальный технический университет \

(СевНТУ)''

3.2.4. Ключевые слова

Ключевые слова — это зарезервированные идентификаторы, которые имеют специальное значение для компилятора.

Таблица 3.4 — Список ключевых слов

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

Эти слова можно использовать только в том значении, в котором они определены.

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

Знаки операций — это один или более символов, определяющих действие над операндами.

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

По количеству участвующих в них операндов операции делятся на унарные, бинарные и тринарные. Один и тот же знак в различных ситуациях может истолковываться по-разному. В таблице 3.5 представлены основные операции языка С++.

Таблица 3.5 — Основные операции языка С++

Операции Краткое описание
Унарные операции
++ Инкремент (увеличение на 1)
– – Декремент (уменьшение на 1)
sizeof Размер
~ Поразрядное отрицание
! Логическое отрицание
Унарный минус (арифметическое отрицание)
+ Унарный плюс
& Взятие адреса
* Реадресация
new Выделение памяти
delete Освобождение памяти
type Преобразование типа
Бинарные и тернарная операции
* Умножение
/ Деление
% Остаток от деления
+ Сложение
Вычитание
<< Сдвиг влево
>> Сдвиг вправо
< Меньше
<= Меньше или равно
> Больше
>= Больше или равно
== Равно
!= Не равно
& Поразрядная конъюнкция (И)
| Поразрядная дизъюнкция (ИЛИ)
^ Поразрядное исключающее ИЛИ
&& Логическое И
|| Логическое ИЛИ
?: Условная операция (тернарная)
Операции присваивания
= Присваивание
*= Умножение с присваиванием
/= Деление с присваиванием
%= Остаток от деления с присваиванием
+= Сложением с присваиванием
–= Вычитание с присваиванием
<<= Сдвиг влево с присваиванием
>>= Сдвиг вправо с присваиванием

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

Операции Краткое описание
&= Поразрядное И с присваиванием
|= Поразрядное ИЛИ с присваиванием
^= Поразрядное исключающее ИЛИ с присваиванием
, Последовательное вычисление

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

Комментарии — последовательность символов, игнорируемая компилятором.

Начинается комментарий либо с двух символов «косая черта» (//) и заканчивается символом перехода на новую строку, либо заключается между символами /* */.

Замечание. Вложенные комментарии не допускаются.

Пример,

3.3. Лекция 6. Типы данных и их объявление

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

В языке С/С++ все переменные, используемые в программе, необходимо объявлять. Тоже самое касается констант. Объявление связано с необходимостью выделить под переменную или константу область памяти, размер которой определяется задаваемым типом данных.

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

Целочисленные типы:

Типы с плавающей запятой:

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

Таблица 3.6 — Диапазоны значений простых типов данных

Тип Диапазон значений Размер, байт
bool true и false  
signed char –128 … 127  
unsigned char 0 … 255  
signed short int –32 768 … 32 767  
unsigned short int 0 … 65 535  
signed long int –2 147 483 648 … 2 147 483 647  
unsigned long int 0 … 4 294 967 295  
float 3.4e–38 … 3.4e+38  
double 1.7e–308 … 1.7e+308  
long double 3.4e–4932 … 3.4e+4932  

Примечания:

1. Размер типа int определяется при объявлении типом процессора (ЦПУ). Для 16-разрядного ЦПУ — 2 байта (16 бит) и для 32-разрядного — 4 байта (32 бита).

2. Выбор типа short int указывает компилятору, что под переменную требуется 2 байта независимо от ЦПУ.

Выбор типа long int — указывает, что требуется 4 байта независимо от ЦПУ.

3. Типы констант могут быть обозначены суффиксами:

L, l — long int; long double;

U, u — unsigned int;

F, f — float.

Например: 32L — тип long int;

32ul — тип unsigned long int;

2E+6L — тип long double;

1.82f — тип float.

4. Для различных ПК и ОС нельзя делать предположение о размерах памяти, занимаемых различными типами переменных. Необходимо определять эту величину операцией sizeof.

Например:

a=sizeof(int);

Результатом операции будет число 2 (в байтах) для MS-DOS, и

4 для Windows или OS/2.

5. Целые типы short int, long int,

signed int и unsigned int

можно сокращать до

short, long,

signed и unsigned.

6. Минимальные и максимальные допустимые значения приводятся в заголовочных файлах float.h (для вещественных) и limits.h (для целых переменных).

3.3.2. Объявление типов данных

Объявления С/С++ в имеют следующий формат записи (квадратные скобки показывают, что элемент может отсутствовать):

[спецификатор_класса_памяти] [const] [спецификатор_типа] [тип]
описатель [инициатор] [, описатель [инициатор] ] …;

Здесь:

— [спецификатор_класса_памяти] — определяется одним из 4 классов и определяет, каким образом будет распределяться память под переменную и область видимости переменной:

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

extern — внешние переменные; существуют и сохраняют свои значения в течение выполнения всей программы и могут использоваться для связи между функциями, в том числе и между независимо скомпилированными функциями;

static — статические переменные; являются локальными, но могут быть и глобальными; сохраняют свои значения после того, как управление передаётся за пределы блока;

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

— [const] — модификатор, указывающий на то, что значение переменной изменять нельзя, т.е. переменная является именованной константой;

— [спецификатор_типа] — уточняет внутреннее представление и диапазон значений типов:

short — короткий;

long — длинный;

signed — знаковый;

unsigned — беззнаковый;

— [тип] — указывает на тип переменной;

bool — целый тип;

char — символьный тип;

int — целый тип;

float — вещественный тип;

double — вещественный тип с двойной точностью;

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

— [инициатор] — присвоение переменной начального значения при инициализации; существуют две формы записи:

=значение — присвоение значения с использованием знака равенства;

(значение) — присвоение значения с использованием круглых скобкок.

В одном операторе можно описать несколько переменных одного типа, разделяя их запятыми.

Примеры:

1)

short int a=1; // короткая целая a=1

int b (7); // целая b=7

float c=0.22, //переменная с плавающей запятой c=0,22

x (3), // переменная с плавающей запятой x=3

s; // переменная с плавающей запятой

2)

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

main ()

{

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

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

static int y; // статическая переменная y

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

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

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

return 0;

}

int x=4; // присвоение и инициализация переменной х

3.4. Лекция 7. Указатели и массивы

3.4.1. Объявление указателей

При обработке оператора определения переменной, например, int i = 10; компилятор выделяет память в соответствие с типом (int) и инициализирует её с указанным значением (10). Все обращения в программе к переменной по её имени (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной.

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

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

Различают три вида указателей:

— указатели на функцию;

— указатели на объект;

— указатели на void.

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

Используется для:

— косвенного вызова функции (не через имя, а через обращение к переменной, хранящей ее адрес);

— передачи имени функции в другую функцию в качестве параметра.

Формат записи указателя на функцию имеет вид:

тип (*имя) (список_типов_аргументов);

Например, объявление вида:

int (*cooler) (double, double);

задаёт указатель cooler на функцию, возвращающую значение типа int и имеющую два аргумента типа double.

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

Формат указателя на объект в простейшем варианте имеет вид:

тип *имя;

где: тип — может быть любым типом, кроме ссылки и битового поля;

* — относится к имени.

Например:

int *a, b, *c;

В данном примере описываются два указателя на целое с именами a и c, а также целая переменная b. Размер указателя зависит от модели памяти. Можно определить указатель на указатель и т.д.

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

Форматы указателя на void имеют вид:

тип (*имя) (список_типов_аргументов);

тип *имя;

Примечание (!):

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

Примеры объявления указателей:

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

2) const int ci=1; // целая константа

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

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

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

// переменную

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

// целую константу

В представленных примерах:

1) символ & — команда взятия адреса переменной;

2) модификатор const — запрещает изменение значения указателя;

const — указывает на то, что в ячейке памяти хранится

константа.

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

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

Динамическая память — свободная память, в которой можно во время выполнения программы выделять место под данные в соответствии с потребностями.

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

Время «жизни» динамических переменных — от момента объявления, до конца выполнения программы или до явного освобождения динамической памяти.

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

— используя семейство функций malloc;

— используя операции new и delete.

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

Инициализатор записывается после имени указателя либо в круглых скобках, либо после знака равенства.

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

— присваивание указателю адреса уже существующего объекта;

— присваивание указателю адреса области памяти в явном виде;

— присваивание пустого значения;

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

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

— с помощью операции получения адреса:

int a=5; // целая переменная

int *p=&a; // в указатель записывается адрес

// переменной “а”

int *p (&a); // в указатель записывается адрес

// переменной “а” другим способом

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

int *r=p; //p — указатель, унициализированный ранее

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

int b[10]; // объявление массив

int *t=b; // присваивание адреса начала массива

void f(int a) { … } // определение функции

void (*pf)(int); // объявление указателя на функцию

pf=f // присваивание указателю адреса

// функции

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

char *vp=(char*) 0xB8000000;

Здесь 0xB8000000 — шестнадцатеричная константа (адрес области памяти);

(char*) — операция приведения типа: константа преобразуется к типу «указатель на char».

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

int *a=NULL;

int *b=0; // рекомендуется использовать этот вариант

Здесь NULL — нулевая константа.

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

Рекомендуется использовать второй вариант инициализации.

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

Пример:

— с помощью new:

int *n=new int; // 1

int *m=new int (10); // 2

int *q=new int [10]; // 3

— с помощью функции malloc (библиотека <malloc.h>):

int *u=(int*) malloc(sizeof(int)); // 4.

В представленных примерах инструкции могут быть описаны следующим образом:

//1 — операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n.

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

//2 — выполняются действия, описанные выше, а также производится инициализация выделенной динамической памяти значением 10.

//3 — операция new выполняет выделение памяти под 10 величин типа int (т.е. под массив из 10-ти элементов) и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива, через которое можно обращаться к любому элементу массива.

//4 — выполняется тоже самое, что в //1, но с помощью функции malloc. В функцию передаётся один параметр — количество выделяемой памяти в байтах.

(int*) — приведение типа указателя, возвращаемого функцией, к требуемому типу. Функция возвращает 0, если выделить память не удалось.

Примечание (!):

Предпочтительнее использовать операцию new, чем функцию malloc, особенно при работе с объектами.

5. Освобождение памяти:

— выделение new — освобождение delete;

— выделение malloc — освобождение free.

Пример освобождения памяти (соответствует примеру выделения памяти): delete n; // 1

delete m; // 2

delete []q; // 3

free(u); // 4

Примечания:

Если выделение осуществлялось для массива, то необходимо указывать, что освобождается массив, поставив перед именем указателя пустые квадратные скобки (для освобождения требуется []delete). Размерность массива при этом не указывается.

Если нет скобок [], то освобождается только первый элемент массива, а остальные окажутся недоступными для дальнейших операций. Т.е. появляется «мусор» в памяти.

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

3.4.3. Составные указатели

Пример:

Здесь объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int.

По умолчанию скобки [] и () имеют одинаковый приоритет, больший, чем * и рассматриваются слева направо. Для изменения порядка используют круглые скобки. Таким образом, порядок приоритета в приведенном примере составного указателя показан цифрами снизу.

Для сложных описаний используют правило «изнутри наружу»:

1) если справа от имени '[]' — это массив,

если '()' — функция;

2) если слева '*', то это указатель на проинтерпретированную ранее конструкцию;

3) если справа встречается ')', то необходимо применить приведённые выше правила внутри скобок, а затем переходить наружу;

4) в последнюю очередь интерпретируется спецификатор типа.

3.4.4. Операции над указателями

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

— реадресация или косвенное обращение к объекту (*);

— присваивание;

— сложение с константой;

— вычитание;

— инкремент (++);

— декремент (– –);

— сравнивание;

— приведение типов.

Кроме выше перечисленных операций, часто используется операция взятия адреса (&).

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

Пример:

char a; // объявление переменной типа char

char *p=new char; // выделение памяти под указатель и

// под динамическую переменную типа char

*p=''Ю''; a=*p; // присваивание значения обеим переменным

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

Пример:

unsigned long int A=0Xcc77ffaa;

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

unsigned char *p_char=(unsigned char *) &A;

printf(''| %x | %x | %x |'', A, *p_int, *p_char);

… }

Результат выполнения:

| cc77ffaa | ffaa | aa |

Примечание.

Присваивание без приведения типов допускается для указателей:

— типа void*;

— одного типа.

Арифметические операции с указателями — сложение с константой, вычитание, инкремент, декремент.

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

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

Операции умножения, деления или сложения указателей не существует!

Инкремент — ++ перемещает указатель к следующему элементу структуры данных (массива).

Декремент — – – перемещает указатель к предыдущему элементу.

Значение указателя изменяется на величину sizeof(тип) в байтах.

Пример:

short *p=new short[5]; //объявление указателя на массив

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

long *q=new long[5]; // объявление указателя на массив

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

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

Например, разность указателя на 3-й элемент массива и на 6-й элемент равна 3.

При сложении с константой или других действиях следует учитывать приоритеты выполнения операций.

Примечание! Суммирование указателей не допускается.

Пример:

1) *p++=10; /* Û */ *p=10; p++;

// Два одинаковых по смыслу выражения,

// в которых вначале по адресу *p запишется 10,

// а потом указатель увеличивается на 1xsizeof(тип).

2) (*p)++; // Выражение увеличивает содержимое ячейки памяти.

Операция получения (взятия) адреса & — применима к величинам, имеющим имя и размещенным в ОЗУ.

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

3.4.5. Ссылки

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

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

Объявление:

тип *имя [инициатор];

Пример:

int kol;

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

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

Замечания (!):

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

— является параметром функции;

— описана как extern;

— ссылается на поле данных класса.

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

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

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

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

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

3.5. Лекция 8. Массивы

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

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

Массивы — конечная именованная последовательность однотипных величин.

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

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

тип имя_массива [размер_массива] [= {инициализаторы}];

или

тип имя_массива [ ] [= {инициализаторы}];

Пример:

float a[10]; // объявление массива из 10-ти

// вещественных чисел

int i[256]; // объявление массива из 256-ти целых чисел

unsigned short int d[]= // объявление и инициализация

={15,255,120,0,1}; // массива из 5-ти целых чисел

Примечание (!):

1. Элементы массива нумеруются с нуля.

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

3. Элементами массива не могут быть функции и элементы типа void.

4. Инициализирующие значения массива записываются в {}. Значения элементам присваиваются по порядку. Если элементов массива больше, чем инициализирующих значений, то элементы, для которых значения не указаны, обнуляются. Например:

int b[5]={3,2,1}; // b[0]=3; b[1]=2; b[2]=1;

// b[3]=0; b[4]=0.

int c[4]={1,2,3,4}; // c[0]=1; c[1]=2; c[2]=3; c[3]=4.

Для доступа к элементу массива указывается его имя и номер элемента в квадратных скобках, который называют индексом, например b[4] (b — имя, 4 — индекс).

Пример:

int main()

{

const int N=10; //количество эл-тов массива

int i, sum;

int a[N]={3,4,5,6,4,3,1}; //объявление и инициализация

//массива

for (i=0, sum=0; i<N; i++)

sum+=a[i];

/* запись sum+=a[i] эквивалентна sum=sum+a[i] */

cout << ''Сумма элементов: '' << sum;

return 0;

}

Замечания:

— память под массив выделяется на этапе компиляции;

— размерность массива может быть задана только целой положительной константой или константным выражением;

— размерность массивов рекомендуется задавать с помощью именованных констант (см. пример выше);

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

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

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

тип имя [индекс_1][индекс_2]…[индекс_N] [={инициализаторы}];

Двумерный массив имеет следующий формат объявления:

тип имя [индекс_строк][индекс_столбцов] [={инициализаторы}];

Пример:

Здесь объявляется массив из 5-ти строк и 4-х столбцов.

При инициализации многомерного массива он представляется либо как массив из массивов, либо задаётся общий список элементов в том порядке, в котором они располагаются в памяти.

Пример:

int matr[][] = { {1, 2}, {3, 4}, {5, 6} };

или

int matr[3][2] = {1, 2, 3, 4, 5, 6};

В памяти такой массив располагается построчно последовательно.

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

Например, в выше приведенном примере инициализируется двумерный массив

,

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

Рисунок 3.2 — Схема размещения двумерного массива

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

Пример:

matr[i][j];

*(matr[i]+j) //или:

*(*(matr+i)+j)

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

3.5.3.1. Одномерные динамические массивы

Динамические массивы создают с помощью операции new (С++) или malloc (С).

Формат создания динамического массива с помощью операции new:

тип *имя = new тип [размер массива];

Пример:

int n=100; // количество элементов массива

float *p=new float[n]; // объявление динамического массива

… // из n элементов с плавающей запятой

delete []p; // освобождение памяти

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

Пример:

float a, b;

a = p[5]; // обращение к 5-му элементу массива

// с помощью индекса

b = *(p+5); // обращение к 5-му элементу массива

// через указатель

Формат создания динамического массива с помощью операции malloc:

тип *имя =

(приведение_типа *) malloc (размер_массива * размер_типа);

Пример:

int n = 100; //количество элементов массива

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

//объявление динамического массива

… //из n элементов с плавающей запятой

free(p); //освобождение памяти

Примечание:

1. Преобразование типа обязательно, поскольку функция malloc возвращает значение указателя типа void*.

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

3.5.3.2. Динамический многомерный массив

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

Пример:

int N_str=5; //количество строк

int **m=(int**) new int [N_str][10]; //объявление двумерного

//динамического массива

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

int N_str, N_stb; //1

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

cin>>N_str>>N_stb; //2

int **a=new int *[N_str]; //3

for (int i=0; i<N_str; i++) //4

{

a[i]=new int[N_stb]; //5

}

… /* Инициализация массива и обработка данных*/

delete []a; //6

В примере:

//1 — объявляется двух переменных — количества строк и столбцов;

//2 — ввод с клавиатуры количества строк и столбцов;

//3 — объявление указателя на массив указателей на int;

//4 — цикл для выделения памяти под каждую строку;

//5 — каждому элементу массива указателей на строки присваивается адрес начала участка памяти, выделенного под строку, как показано на рис. 3.3;

//6 — освобождение памяти из-под массива.

Рисунок 3.3 — Задание динамического многомерного массива

На рис. 3.3 отображены два этапа объявления и выделения памяти под двумерный массив:

— этап 1: объявляется указатель на массив указателей на int и выделяется память под массив указателей;

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

3.5.4. Символьные массивы (строки)

Строка представляет собой массив символов, заканчивающийся нуль-символом ‘\0’.

Замечание.

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

Форматы объявления символьного массива:

char имя_массива [размер_массива] [= “ инициализатор ”];

char имя_массива [ ] = “ инициализатор ”;

Примеры:

1. Объявление символьного массива заданной длины и инициализация строковым литералом:

char str[20] = ''University''; // объявление массива из 20 элементов

// с номерами от 0 до 19

Здесь под массив выделено 20 байт; символы массива записались в первые 11 байт, остальные ячейки памяти содержат нулевые значения:

2. Объявление символьного массива и инициализация строковым литералом без указания количества элементов массива:

char str[]= ''University''; // под массив выделено 11 Байт.

3. Запись следующего вида

char *str= ''University'';

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

Примечание:

Указатель на константу удалить нельзя.

3.6. Лекция 9. Выражения и присваивания

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

Операнд — константа, литера, идентификатор, функция и т.д.

3.6.1. Операции инкремента и декремента (увеличения и уменьшения на 1)

Знак операции инкремента — ++,

знак операции декремента — – –.

Существует две формы записи этих операций:

— префиксная (++x, – –x), при которой вначале изменяется операнд, а затем его значение становится результатом выражения;

— постфиксная (x– –, x++), при которой значением выражения вначале является операнд (его исходное значение), после чего он изменяется.

Пример:

include <stdio.h>

int main()

{

int x=3, y=3;

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

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

printf(“Значение x после приращения: %d\n”, x);

printf(“Значение y после приращения: %d\n”, y);

return 0;

}

Результат выполнения программы:

Значение префиксного выражения ++x: 4

Значение постфиксного выражения y++: 3

Значение x после приращения: 4

Значение y после приращения: 4.

3.6.2. Операция определения размера типа

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

Существует две формы записи:

sizeof выражение;

sizeof (тип);

Пример:

#include <iomanip.h>

int main()

{

float x =1;

cout<<“Размер типа: ”<< sizeof(float) << endl; //1

cout<<“Размер выражения 1: ”<< sizeof x << endl; //2

cout<<“Размер выражения 2: ”<< sizeof (x+0.1) << endl; //3

return 0;

}

Результат выполнения примера:

Размер типа: 4

Размер выражения 1: 4

Размер выражения2: 8

Результат выполнения строки //3 отличается от результатов выполнения строк //1 и //2 так как по умолчанию ‘0.1’ имеет тип double и результат суммирования также по умолчанию имеет тип double, поэтому при сложении этой величины с переменной x происходит автоматическое преобразование типа переменной x и результата суммирования к типу double.

3.6.3. Операции отрицания (–,!, ~)

Операция – — операция арифметического отрицания. Операция изменяет знак числа на противоположный.

Пример:

float x = 7, y;

y = –x; //в результате получаем y = –7.

Операция! — операция логического отрицания. Если операнд имеет значение ''Истинна'' (1), то применение операции! даст результат — ''Ложь'' (0), и наоборот, если операнд ''Ложь'' (0), то операция дает результат ''Истинна'' (1).

Пример:

bool a = true, b=false, x, y;

x =!a; // Получаем: x = 0, т.е false.

y =!b; // Получаем: y = 1, т.е true.

Операция ~ — операция поразрядного (побитового) отрицания. Операция инвертирует каждый разряд (бит) числа.

Пример:

int x=110, y;

y= ~x; // Получаем: y=145

Результат выполнения: y=~11010=14510=100100012

Рисунок 3.4 — К объяснению операции поразрядного отрицания

3.6.4. Операции деления (/) и нахождения остатка от деления (%)

Операция / — операция деления, позволяет разделить один операнд на другой.

Операция % — операция получения остатка от деления позволяет определить отстаток от деления одного операнда на другой.

Пример:

int x=11, y=4;

float z=4;

cout<< x/y << x/z << x%y;

Результат: 2 2.750000 3

Примечания:

При выполнении операции деления

y= a / b;

если ''a'' и ''b'' — целые, то ''y'' округляется до целого, в противном случае тип результата определяется самым длинным типом из ''a'' и ''b''.

Операция «остаток от деления» применяется только к целочисленным типам.

3.6.5. Операции сдвига (<< >>)

Операция << — операция сдвига влево.

Операция >> — операция сдвига вправо.

Операции применяют к целочисленным операндам. Сдвигают влево или вправо двоичное представление операнда на количество двоичных разрядов (бит), заданное вторым операндом:

y=a<<b;

z=c>>d;.

Пример:

cout<< (2<<1) << (2<<2) << (2<<3) <<endl;

cout<< (64<<1) << (64<<2) << (64<<3) <<endl;

Исходное значение — 2 (000000102),

Результат: 4 (000001002) 8 (000010002) 16 (000100002)

Исходное значение — 64 (010000002).

32 (001000002) 16 (000100002) 8 (000010002)

Примечание:

— операция сдвига влево на N бит эквивалентна умножению операнда на ;

— операция сдвига вправо на N бит эквивалентна делению операнда на ;

— при сдвиге двоичного представления операнда влево старшие биты выбрасываются, а младшие биты заполняются нулями;

— при сдвиге двоичного представления операнда вправо старшие биты заполняются нулями, а младшие биты выбрасываются;

— операция сдвига применяют к целочисленным типам.

3.6.6. Операции отношения (<, <=, >, >=, ==,!=)

Операция < — операция меньше.

Операция <= — операция меньше или равно.

Операция > — операция больше.

Операция >= — операция больше или равно.

Операция == — операция равно.

Операция!= — операция не равно.

Приоритет операций:

Операнды могут быть любых типов или указателями. Результатом выполнения операций всегда является либо ''Истина'' (1) либо ''Ложь'' (0) (тип bool).

Примечание! Операции == и = различаются, и их не следует путать.

При необходимости можно использовать скобки.

3.6.7. Поразрядные операции

Операция & — операция поразрядного И (конъюнкция), которая сравнивает операнды побитно.

Результат равен 1, только в том случае, если оба бита равны ‘1’, в остальных случаях результат равен '0'.

Пример:

cout<< (6&5); // Результат — 4

Рис. 3.5 — К объяснению операции поразрядного И

Операция | — операция поразрядного ИЛИ (дизъюнкция) сравнивает операнды побитно: результат равен '1' если хоть один из двух сравниваемых битов равен ‘1’, результат равен ‘0’ только если оба сравниваемых бита равны '0'.

Пример:

cout<< (6|5); // Результат — 7

Рис. 3.6 — К объяснению операции поразрядного ИЛИ

Операция ^ — операция поразрядного исключающее ИЛИ сравнивает операнды побитно: результат равен '1' только если один из двух сравниваемых битов равен '1', в противном случае результат равен '0'.

Пример:

cout<< (6^5); // Результат — 3

Рис. 3.7 — К объяснению операции поразрядного «Исключающее ИЛИ»

Таблица 3.7 — Таблица истинности поразрядных операций

a b & | ^
         
         
         
         

3.6.8. Логические операции

Операция && — операция логического И.

Операция || — операция логического ИЛИ.

Пример:

a&&b;

a||b;

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

Таблица 3.8 — Таблица истинности логических операций

a b && ||
       
       
       
       

Примечание:

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

Операнды в каждой операции могут быть различных типов. Результат всегда имеет тип bool.

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

При выполнении логических операций операнд, равный нулю, воспринимается как “false”, а операнд, отличающийся от нуля, воспринимается как “true”.

Пример:

if ((a<=b) && x)

y=a / x;

else

{

if ((b>0) || (x!=a))

y = b * x;

else

y = a / b;

}

3.6.9. Операции присваивания

Операция = — простое присваивание.

Операции +=

–=

*=

/=

%= — сложное присваивание.

Примечание:

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

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

Пример:

a = b; // переменной a присваивается значение b

a += b; // a = a + b;

a –=b; // a = a – b;

a *=b; // a = a * b;

a /=b; // a = a / b;

a %=b; // a = a % b;

a = b = c = d = e; // d = e; c = d; b = c; a = b;

a= (b=c) * d; // b = c; a = b * d;

3.6.10. Условная операция

Формат записи:

Выражение_1? Выражение_2: Выражение_3

Условная операция вначале обрабатывает Выражение_1, если результат выражения равен ‘true’ (т.е. отличается от «0»), то затем обрабатывается Выражение_2, в противном случае обрабатывается Выражение_3.

Примеры:

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

i = (i<n)? i+1: 1;

3.6.11. Приоритеты выполнения операций

В языке С/С++ первыми выполняются операции с высшими приоритетами. Самый высший приоритет — 1. В табл. 3.9 перечислены существующие приоритеты операций.

Таблица 3.9 — Приоритеты выполнения операций

Приоритет Операторы Тип операции Порядок выполнения
  () []. –> Выражение Слева направо
  ! = ++ – – + – * & sizeof(тип) Унарные Слева направо
  * / % Мультипликативные Слева направо
  + – Аддитивные
  << >> Сдвиг
  < > <= >= Отношение
  ==!= Отношение
  & Поразрядное И
  ^ Поразрядное исключающее ИЛИ
  | Поразрядное ИЛИ
  && Логическое И Слева направо
  || Логическое ИЛИ
  ?: Условная
  = += –= *= /= %= &= ^= |= <<= >>= Присваивание Справа налево
  , Последовательное вычисление Слева направо

3.6.12. Преобразование типов

Преобразование типов либо задаётся в явном виде командами и операторами, либо производится в неявном виде при выполнении операций.

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

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

Правила преобразования арифметических типов представляют собой следующую последовательность:

Любые операнды типа char, unsigned char или short преобразуются к типу int по правилам:

— char расширяется нулём или знаком в зависимости от умолчания для char;

— unsigned char расширяется нулём;

— signed char расширяется знаком;

— short, unsigned short и enum при преобразовании не изменяются;

— затем любые два оператора становятся либо int, либо float, double или long double;

— если один из операндов имеет тип float, то другой преобразуется к float.

Примечание:

1. Если один из операндов имеет тип long double, то другой преобразуется к long double.

2. Если один из операторов имеет тип double, то другой преобразуется к double.

3. Если один из операторов имеет тип float, то другой преобразуется к float.

4. Если один из операндов имеет тип unsigned long, то другой преобразуется к unsigned long.

5. Если один из операндов имеет тип long, то другой преобразуется к типу long.

6. Если один из операндов имеет тип unsigned, то другой преобразуется к unsigned.

7. Иначе оба операнда должны иметь тип int.

3.7. Лекция 10. Операторы

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

Примеры:

/* Оператор выражение: */

i++;

a = sin(b + c * 5);

func(x, y);

/* Пустой оператор: */

;

/* Составной оператор: */

if (x < y) { x = a + b; y = (b – a) / 2; }

Пустой оператор состоит только из точек с запятой (;).

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

3.7.1. Операторы ветвления

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

Формат записи (квадратные скобки показывают, что этих ветвей может и не быть):

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

Вначале вычисляется выражение, а затем, если выражение равно ‘true’ — выполняется оператор_1, если же выражение равно ‘false’ — оператор_2.

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

Обозначение оператора в алгоритмах. Блок-схема алгоритма части программы, соответствующей оператору if, может быть представлена как в полном виде, так и в сокращенном виде, когда второй оператор отсутствует, как показано на рис. 3.8.

Рисунок 3.8 — Блок-схемы алгоритма оператора if:
а) полная запись; б) сокращенная запись (отсутствует ветвь 'false’),
в) сокращенная запись (отсутствует ветвь ‘true’)

Примеры:

if (a<0) b=1; //1

if (a<b && (a>d || a==0)) //2

b++;

else

{

b*=a;

a=0;

}

if (a++) b++; //3

if (a– –); b– –; //4

В примере:

— //1 показан сокращенный оператор if с пропущенной ветвью 'false’,

— //2 — полный оператор if, в котором в ветви 'false’ записан составной оператор в фигурных скобках;

— //3 — сокращенный оператор if с пропущенной ветвью 'false’, в котором выражение в круглых скобках включает в себя также и постфиксный оператор ++; порядок действий здесь следующий: вначале вычисляется выражение (a), а затем проводится операция инкремента (++); если выражение (а) истинно, то выполняется оператор b++ (в итоге, после выполнения оператора if, производится увеличение значения переменной а на 1, в не зависимости от того, истинно ли выражение в круглых скобках);

— //4 — сокращенный оператор if с пропущенной ветвью 'true’, в выражении которого (в круглых скобках) также записана постфиксная операция (– –).

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

if (int t=fun(z)) a –=t; else a+=t;

3.7.1.2. Оператор switch

Оператор switch предназначен для разветвления процесса на несколько направлений.

Формат записи:

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

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

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

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

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

}

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

Выход из оператора switch может быть организован с помощью операторов break или return.

Обозначение о


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: