
- •4. Инкремент и декремент
- •5. Операции сравнения (операции отношения)
- •7. Ввод- вывод
- •10. Оператор цикла с постусловием do-while
- •13. Работа с символами
- •15. Указатели и Массивы.
- •16. Некоторые функции из библиотеки string.H
- •17. Перечисления (enum)
- •19. Функции
- •20. Передача данных в функцию по значению, по указателю, по ссылке.
- •21. Передача массивов в функцию. Указатель на функцию
- •22. Встраиваемые функции. Перегрузка функций. Передача массивов в функцию. Указатель на функцию
- •23. Параметры функций по умолчанию, функции с переменным числом параметров.
- •25. Работа с файлами
- •26. Директивы препроцессора
- •27. Базовые принципы объектно-ориентированного программирования
- •29. Базовые блоки ооп. Объект. Класс
- •30. Реализация механизма сокрытия информации
13. Работа с символами
Для представления символов в памяти компьютера используется тип char. Можно в качестве примера привести определение констант и переменных этого типа:
const char c1 = 'A';// константная переменная
char c = c1, c2; // переменные
Под данные типа char отводится 1 байт. Тип char является целым типом, поэтому внутреннее представление для данных типа char (их коды) — это просто целые числа.
Для кодировки символов используется ASCII-код (American Standard Code for Information Interchange — американский стандартный код для обмена информацией). Это семибитный код, позволяющий записать 128 различных комбинаций (знаков). Так как под данные типа char отводится 8 двоичных цифр (бит), то имеется возможность закодировать ещё 128 знаков. Таким образом, кодовая таблица состоит из двух частей: младшая — соответствует ASCII-код, старшая — используется для представления национальных шрифтов, символов псевдографики и прочее.
Для различных систем кодировок младшая часть кодовой таблицы всегда одинакова, а старшая может быть различной. В нашей стране сейчас наиболее популярной является Windows-кодировка (Кодовая таблица 1251), для совместимости со старыми программами применяется DOS-кодировка (Кодовая таблица 866), а при работе в операционной системе Linux довольно распространена кодировка KOI8-R.
Для работы с данными типа char можно использовать все арифметические операции, операции сравнения и побитовые операции, хотя, если учесть, что этот тип используется для представления не чисел, а именно символов, то, естественно, чаще всего применяют операции сравнения.
В языках С/С++ имеется большое количество стандартных функций. Приведём некоторые из них. Пусть в программе дано описание следующих переменных:
char c;
int k;
Тогда можно в качестве примеров вызова стандартных функций для работы с символьной информацией написать следующее:
1)чтение одного символа из стандартного устройство ввода (обычно это клавиатура):
c = getchar();
2)чтение одного символа из файла (здесь f — указатель на структуру FILE, предопределённую в языках С/С++):
c=fgetc(f);
Работу с файлами будем рассматривать немного позже, а пока можно применить такой вариант этой же функции:
c=fgetc(stdin);
где stdin — идентификатор стандартного устройства ввода (это имя предопределёно для С/С++).
3)вывод символа на стандартное устройство вывода (обычно это дисплей монитора):
putchar(c);
4)вывод одного символа в файл (здесь f — указатель на структуру FILE):
fputc(c,f);
Для вывода на стандартное устройство вывода (идентификатор stdout также предопределён для С/С++) можно записать так:
fputc(c,stdout);
5)проверка того, что символ — латинская буква:
k=isalpha(c);
Здесь k будет не равно 0, если в переменой c хранится символ — латинская буква, и равно 0 — в противном случае.
6)проверка того, что символ — это цифра:
k=isdigit(c);
Результат — не 0, если в c находится символ цифры, и 0 — если это не так.
14.
15. Указатели и Массивы.
В С++ существует тесная связь между указателями и массивами. Любой доступ к элементу массива, осуществляемый операцией индексирования, может быть выполнен при помощи указателя.
Объявление int a[10]; определяет массив а размера 10, т.е. блок из 10 последовательных объектов с именами а[0], a[1], ..., a[9].
Запись a[i] отсылает нас к i-му элементу массива. Если pa есть указатель на int, т.е. определен как int *pa; то в результате присваивания pa=&a[0]; pa будет указывать на нулевой элемент а; иначе говоря, ра будет содержать адрес элемента а[0].
Теперь присваивание
x=*pa;
будет копировать содержимое a[0] в х. Если ра указывает на некоторый элемент массива, то ра+1 по определению указывает на следующий элемент, ра+i - на i-ый элемент после ра, а ра-i - на i-ый элемент перед ра. Таким образом, если ра указывает на а[0], то *(ра+1) есть содержимое а[1], pa+i - адрес а[i], a *(pa+i) - содержимое a[i].
Сделанные замечания верны к типу и размеру элементов массива а. Смысл слов "добавить 1 к указателю", как и смысл любой арифметики с указателями, в том, чтобы ра+1 указывал на следующий объект, а ра+i - на i-й после ра.
Между индексированием и арифметикой с указателями существует очень тесная связь. По определению имя масива - это адрес его нулевого элемента. После присваивания
pa=&a[0];
pa и а имеют одно и то же значение. Поскольку имя массива есть не что иное, как адрес его начального элемента, присваивание ра=&a[0]; можно также записать в следующем виде:
ра=a;
Еще более удивительно (по крайней мере на первый взгляд) то, что а[i] можно записать как *(a+i). Встречая запись a[i], компилятор сразу преобразует ее в * (а+i); указанные две формы записи эквивалентны. Из этого следует, что полученные в результате применения, оператора & записи &a[i] и a+i также будут эквивалентны, т.е. и в том и в другом случае это адрес i-го элемента после а. С другой стороны, если ра - указатель, то в выражениях его можно использовать с индексом, т.е. запись ра[i] эквивалентна записи *(ра+i). Элемент массива одинаково разрешается изображать и в виде указателя со смещением, и в виде имени массива с индексом.
Между именем массива и указателем, выступающим в роли имени массива, существует одно различие. Указатель - это переменная, поэтому можно написать ра=а или ра++. Но имя массива является константой, и записи типа а=ра или а++ не допускаются.
Рассмотрим пример, который иллюстрирует взаимосвязь массивов и указателей. Программа находит минимальное значение в массиве (пример известен нам еще со времен одномерных массивов, но теперь вернемся к нему, учитывая новые знания об указателях и массивах).
#include <iostream.h>
void main()
{
const int ArraySize=10; //объявили константу для задания размерности массива
int A[ArraySize], *p, i;
//объявили: массив A из ArraySize элементов типа int; указатель p на int;
//переменную i типа int
p=A; /*указателю p присвоили адрес нулевого элемента массива
(вспомним, имя массива содержит адрес его нулевого элемента)*/
for (i=0; i<ArraySize; i++)
{ cout<<"Do enter A["<<i<<"] element -->";
cin>>p[i]; //p[i] - можно заменить на: *(p+i), *(A+i) или на привычное нам A[i]
}
int min=*p, index=0;
for (i=1;i<ArraySize;i++)
if (min>*(p+i))
{
min=*(p+i); //*(p+i) - можно заменить на: p[i], *(A+i) или на привычное нам A[i]
index=i;
}
cout<<"\n\nMinimum was found!\nValue = "<<min<<"\nIndex ="<<index<<endl;
//Вывели на экран результаты работы нашей программы
}
Динамические массивы
В С++ операции new и delete предназначены для динамического распределения памяти компьютера. Операция new выделяет память из области свободной памяти, а операция delete высвобождает выделенную память. Выделяемая память, после её использования должна высвобождаться, поэтому операции new и delete используются парами. Даже если не высвобождать память явно, то она освободится ресурсами ОС по завершению работы программы. Рекомендую все-таки не забывать про операцию delete.
// пример использования операции new
int *ptrvalue = new int;
//где ptrvalue – указатель на выделенный участок памяти типа int
//new – операция выделения свободной памяти под создаваемый объект.
Операция new создает объект заданного типа, выделяет ему память и возвращает указатель правильного типа на данный участок памяти. Если память невозможно выделить, например, в случае отсутствия свободных участков, то возвращается нулевой указатель, то есть указатель вернет значение 0. Выделение памяти возможно под любой тип данных: int, float,double, char и т. д.
// пример использования операции delete:
delete ptrvalue;
// где ptrvalue – указатель на выделенный участок памяти типа int
// delete – операция высвобождения памяти
Как было сказано раньше, массивы также могут быть динамическими. Чаще всего операции new и delete применяются, для создания динамических массивов, а не для создания динамических переменных. Рассмотрим фрагмент кода, создания одномерного динамического массива.
// объявление одномерного динамического массива на 10 элементов:
float *ptrarray = new float [10];
// где ptrarray – указатель на выделенный участок памяти под массив вещественных чисел типа float
// в квадратных скобочках указываем размер массива
После того как динамический массив стал ненужным, нужно освободить участок памяти, который под него выделялся.
// высвобождение памяти отводимой под одномерный динамический массив:
delete [] ptrarray;
Созданный одномерный динамический массив заполняется случайными вещественными числами, полученными c помощью функций генерации случайных чисел, причём числа генерируются в интервале от 1 до 10, интервал задается так - rand() % 10 + 1. Чтобы получить случайные вещественные числа, выполняется операция деления, с использованием явного приведения к вещественному типу знаменателя - float((rand() % 10 + 1)). Чтобы показать только два знака после запятой используем функцию setprecision(2), прототип данной функции находится в заголовочном файле <iomanip>.
По завершению работы с массивом, он удаляется, таким образом, высвобождается память, отводимая под его хранение.
Как создавать и работать с одномерными динамическими массивами мы научились. Теперь рассмотрим фрагмент кода, в котором показано, как объявляется двумерный динамический массив.
// объявление двумерного динамического массива на 10 элементов:
float **ptrarray = new float* [2]; // две строки в массиве
for (int count = 0; count < 2; count++)
ptrarray[count] = new float [5]; // и пять столбцов
// где ptrarray – массив указателей на выделенный участок памяти под массив вещественных чисел типа float
Сначала объявляется указатель второго порядка float **ptrarray, который ссылается на массив указателей float* [2], где размер массива равен двум. После чего в цикле for каждой строке массива объявленного в строке 2 выделяется память под пять элементов. В результате получается двумерный динамический массив ptrarray[2][5]. Рассмотрим пример высвобождения памяти отводимой под двумерный динамический массив.
// высвобождение памяти отводимой под двумерный динамический массив:
for (int count = 0; count < 2; count++)
delete [] ptrarray[count];
// где 2 – количество строк в массиве
Объявление и удаление двумерного динамического массива выполняется с помощью цикла, так как показано выше, необходимо понять и запомнить то, как это делается.