- •Основы программирования на языке С
- •Введение
- •1. Базовые понятия программирования
- •1.1. Алгоритмизация задачи
- •1.2. Схема алгоритма программы
- •1.3. Пример алгоритмизации
- •1.4. Этапы трансляции программы
- •2. Особенности языка С
- •2.1. Характеристики языка
- •2.2. Элементы языка
- •2.3. Стандартные типы данных
- •2.4. Компоненты простой программы
- •Вопросы для самопроверки
- •3. Выражения и операции
- •3.1. Операция и выражение присваивания
- •3.2. Бинарные арифметические операции
- •3.3. Операции увеличения (++) и уменьшения (--)
- •3.4. Преобразования типов при вычислении арифметических выражений
- •3.5. Тернарная или условная операция
- •3.6. Логические операции и операции отношения
- •3.7. Поразрядные (побитовые) операции
- •3.8. Операции сдвига
- •3.9. Операция sizeof
- •3.10. Операция следования
- •3.11. Сводная таблица операций языка С
- •Вопросы для самопроверки
- •4. Операторы
- •4.1. Оператор выражение
- •4.2. Пустой оператор
- •4.3. Объявления и составной оператор
- •4.4. Условный оператор
- •4.5. Оператор выбора switch
- •4.6. Циклы
- •4.7. Оператор break
- •4.8. Оператор безусловного перехода goto
- •4.9. Оператор continue
- •4.10. Оператор return
- •Вопросы для самопроверки
- •5. Массивы
- •5.1. Одномерные статические массивы
- •5.2. Объявление массива. Обращение к элементу массива
- •5.3. Инициализация массива
- •5.4. Многомерные массивы
- •5.5. Выход индекса за границы массива
- •5.6. Приемы работы с массивами в вычислительных задачах
- •5.7. Строка как массив символов
- •Вопросы для самопроверки
- •6. Указатели и ссылки
- •6.1. Понятие указателя
- •6.2. Операция получения адреса &
- •6.3. Операция разыменования (*)
- •6.4. Арифметика указателей
- •6.5. Применение указателей в выражениях
- •6.6. Указатели и массивы
- •6.7. Ссылочный тип данных
- •Вопросы для самопроверки
- •7. Время жизни и область видимости переменной
- •7.1. Общие понятия
- •7.2. Классы памяти
- •7.3. Вложенные блоки в программе
- •8. Функции
- •8.1. Общие понятия
- •8.2. Определение функции
- •8.3. Прототип функции
- •8.4. Переменные в функции
- •8.5. Передача параметров в функцию
- •Вопросы для самопроверки
- •9. Пользовательские типы данных
- •9.1. Структурный тип данных
- •9.2. Битовые поля
- •9.3. Объединения или смеси
- •9.4. Перечисления
- •Вопросы для самопроверки
- •10. Динамическая работа с памятью
- •10.1. Универсальный указатель void
- •10.2. Принципы работы с динамическими массивами
- •Вопросы для самопроверки
- •11. Ввод-вывод данных
- •11.2. Функции ввода-вывода библиотеки iostream
Вопросы для самопроверки
1.Дайте общее определение массива данных.
2.Может ли массив содержать переменные нескольких типов?
3.Дайте определение статического массива данных.
4.Перечислите свойства статических массивов.
5.Приведите пример объявления статического массива.
6.Что такое индекс массива?
7.Приведите пример обращения к элементу массива.
8.Какова взаимосвязь между значением индекса и значением элемента массива?
9.Для чего нужна инициализация массивов?
10.Можно ли инициализировать массив при его объявлении?
11.Расскажите об одномерных и многомерных массивах – их инициализация, обращение к элементам. Приведите примеры.
12.Что произойдет, если при обращении к элементу массива индекс превысит максимально возможное значение?
13.Что такое строка с языке С++?
14.Как присвоить значение строковой переменной?
PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com
6. Указатели и ссылки
Без преувеличения можно сказать, что невозможно написать хорошую программу на Си без использования указателей. Это одно из самых мощных и, в то же время, самых опасных средств языка. Неправильное использование указателей может быть причиной трудно обнаруживаемых ошибок, сбоев в работе программы или же операционной системы.
6.1. Понятие указателя
Определяя переменную, программист задает её имя, которое позволяет оперировать со значением переменной – это программный уровень работы. На машинном уровне компьютер имеет дело с понятиями "адрес участка памяти", выделенный под переменную и "содержимое участка памяти".
Например, при определении переменной, выделяется необходимая память (допустим по адресу 100), а оператор присваивания заносит в неё некое значение.
int var =1;
Программный
Уровень
|
|
ИМЯ |
ЗНАЧЕНИЕ |
|
|
|
|
VAR |
1 |
|
|
Машинный |
|
|
|||
Уровень |
|
|
|||
|
|
АДРЕС ПАМЯТИ |
|
СОДЕРЖИМОЕ ПАМЯТИ |
|
|
100 |
|
1 |
|
Можно сказать иначе, имя переменной var адресует участок памяти, а константа 1 определяет значение, которое запишется по этому адресу. Имея адрес переменной или другого объекта программы необходимо с ним работать: сохранять, передавать и преобразовывать, для этой цели и служат указатели.
Указатели – это переменные, значениями которых являются адреса других объектов – переменных, массивов, функций.
Указатели служат для обращения к области памяти, отведенной под другую переменную. Подобно любой другой переменной, указатель нужно создать (объявить) и задать начальное значение. Термин "задать значение" применительно к указателю используется редко, вместо этого говорят, что нужно "установить" указатель, после чего он будет "ссылаться", то есть указывать на переменную.
Чтобы создать указатель следует определить тип данных, на которые он будет ссылаться. Символ "*" сообщает компилятору, что вы создаете указатель, в конце указывается имя переменной.
Например, инструкция int *ptr; создает указатель, который будет ссылаться на данные целого типа.
Другие примеры объявления указателей:
// переменная ptr_i – указатель на данные типа int int *ptr_i ;
//переменная ptr_с – указатель на данные типа char char *ptr_c ;
//переменая ptr_d – указатель на данные типа double double *ptr_d ;
6.2. Операция получения адреса &
После того, как указатель определен, его нужно установить на какой-либо объект, размещенный в памяти компьютера, то есть записать в него адрес этого объекта. Выражение &var позволяет получить адрес памяти, выделенный под переменную var. Следует отметить, что операция & применима только к объектам, имеющим имя и размещенным в памяти.
Операцию & нельзя применять к:
∙выражениям;
∙константам;
∙битовым полям;
∙регистровым переменным;
∙внешним объектам (файлам).
PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com
Неправильными являются конструкции: &(х+7) и &28.
Первая пытается получить адрес выражения, а вторая – константы.
Установка указателя на объект – это обязательный этап работы. Будьте внимательны неустановленный указатель – главный источник неприятностей. Если переменная p объявлена как указатель, то оператор присваивания p = &x означает: "взять адресс переменной x и присвоить его значение переменной – указателю p".
Пример: |
|
int |
var, *ptr_i, var; |
double |
ss, *ptr_d; |
ptr_i = &var ; ptr_d = &ss ;
Определены два указателя:
*ptr_i – указатель на переменную типа int *ptr_d указатель на переменную типа double.
Указателю ptr_i присваивается адрес переменной var (говорят: ptr_i указывает на переменную var), а указателю ptr_d – адрес переменной ss.
После того, как указатель установлен, можно обращаться к объекту, на который он указывает, для этой цели служит специальная операция "*".
6.3. Операция разыменования (*)
Унарная операция *, примененная к указателю, обеспечивает доступ к содержимому ячейки памяти, на которую ссылается указатель. Иными словами операция * рассматривает свой операнд как адрес и позволяет обратиться к содержимому этого адреса. Легко догадаться, что операндом операции * может выступать только указатель. Приведем различные названия операции *, используемые в литературе по программированию:
∙операцией разыменования (или разадресации);
∙операцией раскрытия ссылки;
∙косвенная адресация (или обращение по адресу).
Итак, для того, чтобы работать с указателем, необходимы, по крайней мере, две переменные – сам указатель и переменная на которую он будет ссылаться.
На рис. 6.1. схематично изображен участок памяти, в которой располагаются две переменные – указатель ptr_i и переменная var. Если указателю присвоен адрес переменной var, то операция разыменования, примененная к указателю, вернет значение переменной var.
Пусть указатель ptr_i имеет тип int, тогда комбинация *ptr_i эквивалентна произвольной переменной типа int. В этой связи важно заме-
Ук а зат ел ь |
|
Адрес var |
p tr _ i |
|
|
|
|
|
|
|
|
|
|
|
|
|
. |
|
|
. |
|
|
. |
|
|
|
Переменная |
|
Содержимое |
var |
|
var |
|
|
|
|
|
|
Рис. 6.1. Косвенный доступ к переменной var через указатель ptr_i
тить, что привязка указателя к объекту (переменной) происходит лишь на начальной стадии работы (установка указателя), затем идет безымянная работа с объектом через указатель, который на определенной стадии работы может быть перенаправлен на другую переменную типа int.
Указатели могут использоваться в выражениях, подобно любым другим переменным.
Пример: |
|
int x, *ptr; |
|
ptr = &x; |
// ptr ссылается на x |
*ptr= *ptr +1; // аналог: x = x+1 |
|
*ptr = *ptr*5; |
// x=x*5 |
В первой строке определён указатель ptr на переменную int, а также и сама переменная – x. Затем указатель устанавливается на переменную x, в дальнейшем он может использоваться в арифметических выражениях вместо переменной x.
PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com
6.4.Арифметика указателей
Вданном разделе рассмотрим арифметические операции, применимые к указателям. Существует ряд ограничений и особенностей при использовании арифметических операций над указателями, накладываемых компилятором языка С. Часто арифметические операции над указателями называют адресной арифметикой, основные элементы которой мы и рассмотрим ниже.
6.4.1. Операция присваивания
Операцию присваивания можно применять при занесении адреса или нулевого значения в указатель. Два указателя можно приравнять друг другу, в этом случае значение указателя правой части равенства заносится в левый указатель.
Пример:
int *p,*k,*z,date=2007; // установка указателя на переменную date p=&date; // значение указателя p заносится в указатель k k=p;
z=NULL;
6.4.2. Унарные операции изменения указателя, инкремент и декремент
(++ --)
При выполнении операций инкременат и декремента значение указателя увеличивается или уменьшается на длину типа, на который ссылается используемый указатель.
Если ptr – некоторый указатель, то ptr++ увеличивает его значение на такую величину, которая обеспечивает адресацию следующего (соседнего) однотипного объекта. Следовательно, операции ++ и -- изменяют числовые значения указателей по-разному, в зависимости от типа данных с которыми связан указатель.
Изменение числовых значений указателя:
для типа char |
+ (или – ) 1 |
для типа int |
+ (или – ) 2 |
для типа float |
+ (или – ) 4 |
для типа double |
+ (или – ) 8 |
Пример: float *k; char *s;
\\+4(перемещение на следующее число) k++;
\\-1 (перемещение на предыдущий символ) s--;
6.4.3. Аддитивные операции (сложение и вычитание)
Аддитивные операции имеют ограничения при работе с указателями: два указателя нельзя складывать, однако к указателю можно прибавить целую величину. При этом результат зависит не только от значения слагаемого, но и от типа указателя.
Пример: float *k,*i; int j;
PDF created with FinePrint pdfFactory Pro trial version http://www.fineprint.com