- •Введение
- •Структура программы на языке Си
- •Директивы препроцессора
- •Константы
- •Переменные
- •Арифметические выражения
- •Операция присваивания
- •Ввод информации с клавиатуры и вывод на экран
- •Логические выражения
- •Операторы управления
- •Оператор условного перехода if
- •Оператор выбора варианта
- •Перечисляемый тип данных
- •Циклы
- •Оператор цикла while
- •Оператор цикла do-while
- •Оператор цикла for
- •Локальные и глобальные переменные
- •Переменные с индексами (массивы)
- •Примеры обработки одномерных массивов
- •Многомерные массивы
- •Массивы и указатели
- •Арифметические и логические операции с указателями
- •Обработка массивов с помощью указателей
- •Обработка массивов переменной размерности
- •Обработка матриц переменной размерности
- •Обработка текстовой информации
- •Стандартные строки языка С++
- •Пример 1. Определение длины строки.
- •Пример 2. Копирование одной строки в другую
- •Пример 3. Исключение из строки символа.
- •Пример 4. Вставка в строку символа
- •Пример 5. Проверка правильности расстановки скобок.
- •Строки типа string
- •Массивы указателей
- •Указатели на функции
- •Сводная таблица форм объявления указателей
- •Преобразование типов
- •Ссылки
- •Структуры
- •Объединения
- •Файлы
- •Чтение файла в матрицу
- •Чтение файла в структуру
- •Функции для обработки произвольных полей структур
- •Связные списки
- •Контейнерные классы
- •Стеки
- •Рекурсия
- •Вопросы для самопроверки
- •Литература
120
Сводная таблица форм объявления указателей
Ниже приведена сводная таблица различных форм объявления указателей с объяснением объявления
Форма объ- |
Объяснение |
явления |
|
|
|
int p |
p - целое число |
|
|
int *p |
p - указатель на целое число |
|
|
int *p[3] |
p - массив указателей на целые числа (квадратные |
|
скобки имеют больший приоритет, чем звездочка) |
|
|
int (*p)[3] |
p - массив целых чисел (звездочка имеет больший |
|
приоритет внутри скобок) |
|
|
int p() |
p - функция, возвращающая целое число |
|
|
int *p() |
p - функция, которая возвращает указатель на целое |
|
число (скобки имеют более высокий приоритет) |
|
|
int (*p)() |
p - указатель на функцию, которая возвращает це- |
|
лое число (звездочка имеет более высокий при- |
|
оритет) |
|
|
int *(*p)() |
p - указатель на функцию, которая возвращает ука- |
|
затель на целые числа |
|
|
int (*p[3])() |
p - массив из трех указателей на функции, которые |
|
возвращают целые числа |
|
|
Преобразование типов
Когда в арифметическом выражении в любой из пар операндов типы данных не совпадают, автоматически выполняется преобразование к такому типу данных, который обеспечивает наибольший диапазон значений.
Неявное преобразование можно применять и к указателям. Указатель void* может быть неявно преобразован к любому другому типу указателя.
Например,
int *pi; char *pc;
121
void *pv1, *pv2;
Тогда возможны присваивания с неявным преобразованием типа pv1 = pi; pv2 = pc;
Но записи вида pc = pv2; не допускаются, поскольку в этом случае тип данного для указателя с правой стороны знака равенства не определен. Не допускается также смешивание при неявном преобразовании указателей на константные и не константные типы данных.
Явное преобразование типов выполняется в случаях, когда компилятор не может определить тип данного, но этот тип известен программисту или изменяется в процессе выполнения программы. Ответственность за правильность таких преобразований возлагается на программиста. Явное преобразование реализуется с помощью операторов static_cast, const_cast, dynamic_cast и reinterpret_cast.
Например, не допускаемая при неявном преобразовании запись pc = pv2; теперь может быть реализована с помощью оператора
static_cast <char*> (pv2);
Обратите внимание на форму записи: в угловых скобках записан указатель, к которому выполняется преобразование, а в круглых скобках имя преобразуемого указателя. Эта форма едина для всех четырех операторов явного преобразования. Преобразование static_cast применяют для обычных переменных (не указателей), когда надо исключить предупреждения компилятора о возможной потере значащих цифр в старших разрядах или точности, которые могут возникнуть при использовании перегруженных знаков операций с неявным преобразованием типов.
С помощью преобразования static_cast можно изменить значение для константы перечисляемого типа данных. В приведенном ниже фрагменте программы значение для константы REG будет заменено значением, введенным с клавиатуры.
enum sam {REG = 135, TAR = 24}; int num;
122
cout << "Введите значение для REG" << endl; cin >> num;
sam REG = static_cast <sam> (num); cout << "Значение для REG = " << REG;
При получении адреса для переменной с атрибутом const получают указатель-константу, иначе эта переменная перестанет быть константой. Если в функцию передают параметр по ссылке с атрибутом const, такой параметр (указатель-константу) использовать в качестве рабочего нельзя, поэтому появляется ограничение на использование параметров функций. Преобразование const_cast применяется для преобразования указателя константы в обычный указатель, чтобы снять такое ограничение. Такое преобразование выполняется в приведенном ниже фрагменте программы:
const int cst = 5; int *pst;
pst = const_cast <int *>(&cst); cout << *pst;
Оператор dynamic_cast применяется для изменения типа объекта в объектно-ориентированном программировании во время выполнения программы и здесь не рассматривается.
Оператор reinterpret_cast позволяет изменить точку зрения компилятора на тип объекта без изменения объекта. В примере ниже введенное с клавиатуры число типа double и распечатывается в шестнадцатеричном коде. Для получения целого беззнакового восьми байтового числа используется тип unsigned long long.
double num; // Это число с плавающей точкой из 8 байт unsigned long long *pt; // Это целое число из 8 байт cout << "Введите число" << endl;
cin >> num;
pt = reinterpret_cast <unsigned long long*>(&num); cout<<"Шестнадцатеричный код для "<< num <<“ = “
<< hex<< *pt;
Результат выполнения программы:
123
Применение этого преобразования достаточно опасно, и оно в основном применяется в объектно-ориентированном программировании.
Операторы явного преобразования типов появились в стандарте нового языка С++. Для совместимости с ранее разработанными программами из языка не исключены устаревшие формы явного преобразования типов. Общий вид такого преобразования:
(<тип данного>)<имя переменной>
Или
<тип данного>(<имя переменной>)
Например, (double) num или double (num)
Ссылки
Когда функция должна возвратить один объект (одиночное значение или массив), обычно используют функцию-подпрограмму. Когда же требуется возвратить более одного объекта, приходится либо использовать глобальные переменные, либо применять для этого параметры списка аргументов.
При возврате объектов из функций можно применять как указатели, так и ссылки. В любом из этих случаев для размещения объекта используется память вызывающей программы. Другими словами, вызывающая функция указывает адрес размещения объекта для функции, поэтому все изменения объекта в функции выполняются в памяти вызывающей программы Различие между ссылками и указателями влияет на оформление функции. При использовании указателя при вызове функции в нее передается адрес объекта в списке параметров, а при ссылке в функцию передается объект, а адрес этого объекта в вызывающей программе вычисляет функция. В этом случае при оформлении функции перед именем формального параметра в списке аргументов записывают символ '&', что задает адрес объекта в вызывающей программе.
124
Ниже приведен пример программы, которая переставляет местами два введенных числа с помощью функций. В одной функции (swap1) используются указатели, а в другой (swap2) передача осуществляется по ссылке. При оформлении этих функций амперсанд (‘&’) записан в разных местах (в первом случае он записан перед фактическими параметрами, а во втором перед формальными), но во втором случае более естественна форма вызова функции.
// Передача параметров с помощью указателей и по ссылке
#include "stdafx.h" #include <conio.h> #include <iostream> using namespace std; void swap1 (int *a, int *b); void swap2 (int &a, int &b); void _tmain()
{
int k, n, p, q;
cout << "Введите два числа\n"; cin >> k >> n ;
cout << "\nВозврат с помощью указателей";
p = k; // Сохранение чисел для последующего вызова q = n;
cout << "\n"Исходные числа " << k << " " << n; swap1(&p, &q);
cout << "\nПосле перестановки \n" << p << " " << q; cout << "\nВозврат с помощью ссылки \n";
cout <<"\n Исходные числа \n" << k << " " << n; swap2(k,n);
cout << "\nПосле перестановки \n" << p << " " << q; getch();
}
125
//Передача (и возврат) с помощью указателей void swap1 (int *a, int *b)
{
int temp; temp = *a; *a = * b; *b = temp;
}
//Передача (и возврат) с помощью ссылки void swap2 (int &a, int &b)
{
int temp; temp = a; a = b;
b = temp;
}
Как видно из примера, передача параметров по ссылке более естественна. И хотя при этом неявно используются указатели, но в целом это похоже на передачу параметров по значению. Не следует забывать, что при передаче объекта по ссылке изменение его элементов в функции влечет к изменению этих же элементов в вызывающей программе.
Ссылка может использоваться в качестве возвращаемого значения функции. Любопытной особенностью функций, возвращающих ссылку, является возможность их записи с левой стороны от знака равенства в операции присваивания. Такая функция возвращает НЕ значение глобальной переменной, а адрес объекта, которому что-то присваивается.
// Ссылка в качестве возвращаемого параметра
#include "stdafx.h" #include <conio.h> #include <iostream> using namespace std;