- •Введение
- •1. Структура программы на языке Си
- •2. Структура простейшей одномодульной программы
- •2.1. Комментарии в программе на языке Си
- •2.2. Начальные сведения о препроцессоре
- •2.3. Определение функции main().
- •2.4. Пример простейшей программы
- •3. Алфавит, синтаксис и семантика
- •4. Алфавит языка Си
- •5. Понятие о типе
- •6. Система типов языка Си
- •Система типов языка Си
- •7. Понятие об объекте
- •8. Лексемы
- •9. Зарезервированные слова
- •10. Идентификаторы
- •11. Литералы
- •11.1. Целочисленные литералы
- •11.2. Литерал вещественного типа
- •11.3. Символьные литералы
- •11.4. Строковый литерал
- •12. Переменные
- •13. Символические константы в языке Си
- •14. Операторы, выражения и инструкции. Общие сведения.
- •14.1. Классификация операторов
- •14.2. Приоритет и ассоциативность операторов.
- •14.3. Побочные эффекты и вычисления выражений
- •14.4. Порядок вычисления выражений
- •15. Арифметические операторы и выражения
- •15.1. Унарные операторы
- •15.2. Бинарные операторы
- •Автоматическое преобразование типов
- •Явное преобразование типа
- •15.4. Математические функции
- •16. Оператор присваивания и инструкция присваивания
- •16.1. Простой оператор присваивания
- •16.2. .Множественное присваивание
- •16.3. Составной оператор присваивания
- •16.4. Преобразование типа при присваивании
- •17. Начальные сведения об указателях. Выходные параметры функции
- •18. Принятие решений и логические величины. Операторы отношения и сравнения на равенство
- •18.1. Логические операторы
- •18.2. Поразрядные операторы
- •19. Условные выражения (оператор ?:)
- •20. Оператор запятая
- •21. Оператор sizeof
- •22. Инструкции перехода
- •22.1. Инструкция break
- •22.2. Инструкция continue
- •22.3. Инструкция goto
- •22.4. Инструкция return
- •23. Составная инструкция
- •24. Инструкция if else
- •24.1. Полная и сокращенная формы инструкции if
- •24.2. Вложенные инструкции if
- •25. Инструкция switch
- •25.1. Синтаксис инструкции switch
- •1.25.2. Использование инструкции switch
- •26. Функциональные компоненты цикла
- •27. Арифметические и итерационные циклы
- •27.1. Задачи, приводящие к арифметическим циклам
- •27.2. Задачи, приводящие к итерационным циклам
- •28. Циклические управляющие инструкции
- •29. Цикл, управляемый инструкцией for
- •30. Цикл, управляемый инструкцией while
- •31. Цикл, управляемый инструкцией do … while
- •32. Цикл с выходом
- •33. Вложенные циклы
- •34. Понятие о варианте и инварианте цикла
- •35. Объявления и определения (расширенное рассмотрение)
- •35.1. Спецификаторы объявления
- •Спецификаторы класса памяти.
- •Квалификаторы типа
- •Спецификаторы типа
- •Использование спецификаторов
- •35.2. Описатели
- •36. Функции
- •36.1. Понятие об абстракции и инкапсуляции
- •36.2. Функция языка Си и процедурная абстракция
- •36.3. Понятие о функции
- •36.4. Назначение функций
- •36.5. Определение функции
- •36.9. Понятие о прототипе. Компиляция
- •36.10. Старый стиль определения функции
- •37. Массивы в стиле языка С89
- •37.1. Определение и объявление массивов
- •Определение и объявление одномерных массивов. Иницициализация одномерных массивов при их определении. Доступ к элементам массива.
- •Определение и инициализация двумерных массивов
- •37.2. Операции с массивами
- •37.3. Размещение массивов в оперативной памяти
- •38. Указатели
- •38.1. Операторы разыменования и взятия адреса
- •38.2. Области применения указателей
- •38.3. Нулевой указатель
- •38.4. Определение указателя
- •38.5. Недействительный указатель
- •38.6. Операции с указателями
- •Дополнительные сведения о разыменовании указателей
- •Присваивание указателей
- •Операция взятия адреса для указателя. Указатели на указатели
- •Указатели и операция индексирования
- •Арифметические операции с указателями
- •Сравнение указателей
- •38.7. Указатели и динамическая память
- •Функция malloc
- •Функция calloc
- •Функция realloc
- •Функция free
- •38.9. Указатели на функцию
- •Постановка задачи
- •Решение
- •39. Указатели и массивы
- •40. Строки
- •40.1. Строковый литерал
- •40.2. Символические строковые константы
- •40.3. Строковые переменные
- •40.4. Инициализация строковых переменных
- •40.5. Операции со строковыми переменными
- •40.6. Ввод строк
- •Функция scanf()
- •Опасная функция gets()
- •Использование функции fgets()
- •40.7. Вывод строк
- •Функция printf()
- •Функция puts()
- •Функция fputs()
- •40.8. Библиотечные функции для обработки строк
- •Функция strlen()
- •Функции strcpy() и strncpy()
- •Функции strcat() и strncat()
- •Функция strcmp()
- •40.9. Массивы строк
- •41. Структуры
- •41.1. Объявление структур
- •41.2. Обращение к полям структуры
- •41.3. Инициализация структур
- •41.4. Операции над структурами
- •41.5. Массивы структур
- •Объявление массива структур
- •Идентификация элементов массива структур
- •42. Работа с внешними устройствами
- •42.1. Понятие потока
- •Текстовый поток
- •Двоичный поток
- •42.2. Файлы
- •Указатель файла
- •Функция fopen()
- •Функция fclose()
- •Функция feof()
- •Стандартные потоки
- •Классификация функций потокового ввода – вывода
- •Форматированный ввод – вывод
- •Построковый ввод – вывод
- •Блоковый ввод – вывод
- •Примеры решенных задач
- •Функция fseek()
/* |
// указателя |
.............................*/ |
38.6.Операции с указателями
Вязыке Си указатели наделены большими возможностями. Об этом свидетельствует размер перечня тех операций, которые можно выполнять с указателями:
∙Разыменование,
∙Взятие адреса,
∙Присваивание,
∙Индексирование указателей
∙Арифметические действия с указателями,
∙Сравнение указателей.
Дополнительные сведения о разыменовании указателей
Как отмечалось выше, разыменование – операция позволяющая получить доступ к объекту, на который установлен указатель. Однако эта операция допустима не ко всем указателям. Остановимся на тех категориях указателей,
ккоторым нельзя применять разыменование:
∙Недействительный указатель.
∙Нетипизированный указатель.
∙Нулевой указатель
Приведем примеры недопустимых разыменований.
/* Примеры недопустимых разыменований указателей */
int* p = NULL; |
/* Недопустимо разыменовывать нулевой указатель*/ |
|
*p = 10; |
||
int n = 5; |
|
|
void* |
p3 = &n; |
/* Недопустимо разыменовывать нетипизированный |
*p3 = 10; |
||
|
|
указатель p3*/ |
char* p4; |
/* Недопустимо разыменовывать |
|
*p4 = ’A’; |
||
/* |
|
неинициализированный указатель p4 */ |
. . . . . . . . . . . . . . . . . . . . . . . . . . . */ |
Заметим, что к категории операторов разыменования относятся еще два оператора:
∙Оператор [], который используется при работе с массивами.
∙Оператор ->, который используется при работе со структурами.
Присваивание указателей
Одному указателю можно присвоить значение другого указателя. Однако некоторые присваивания, которые допускает язык Си, могут привести к
96
некорректной работе программы. Остановимся на этом вопросе подробнее. Рассмотрим ряд частных случаев. Начнем с присваивания типизированных указателей. Корректность такой операции не вызывает сомнений только в том случае, когда совпадают базовые типы обоих указателей. В противном случае такую операцию следует считать недопустимой. Приведем пример программного кода, в котором будут встречаться оба вида присваиваний.
/* |
Корректные и некорректные присваивания типизированных |
||
int n = 10; |
указателей |
*/ |
|
|
|
||
double x = 20.3; |
|
||
int *p1, p2; |
|
|
|
double* p3; |
|
|
|
p1 |
= &n; |
/* допустимое присваивание |
*/ |
p2 |
= p1; |
/* допустимое присваивание |
*/ |
p3 |
= p2; |
/* недопустимое присваивание, т. к. не |
совпадают базовые типы указателей |
*/ |
|
/* конец программного кода примера |
*/ |
|
Отметим, что, несмотря на |
семантическую недопустимость |
присваивания p3 = p1 в приведенном выше примере компилятор языка Си ограничивается выводом предупреждения (Компилятор языка С++ в этой ситуации выводит сообщение об ошибке). Работа языка Си с указателями свидетельствует о его пониженной типизации. Это обстоятельство следует рассматривать как крайне опасным последствиям. Одна из рекомендаций по преодолению этих неприятностей заключается в предварительной компиляции программного кода компилятором языка С++.
Одному указателю можно присвоить значение другого указателя. Однако некоторые присваивания, которые допускает язык Си, могут привести к некорректной работе программы. Остановимся на этом вопросе подробнее. Рассмотрим ряд частных случаев. Начнем с присваивания типизированных указателей. Корректность такой операции не вызывает сомнений только в том случае, когда совпадают базовые типы обоих указателей. В противном случае такую операцию следует считать недопустимой. Приведем пример программного кода, в котором будут встречаться оба вида присваиваний.
/* |
Корректные и некорректные присваивания типизированных |
||
int n = 10; |
указателей |
*/ |
|
|
|
||
double x = 20.3; |
|
||
int *p1, p2; |
|
|
|
double* p3; |
|
|
|
p1 |
= &n; |
/* допустимое присваивание |
*/ |
p2 |
= p1; |
/* допустимое присваивание |
*/ |
p3 |
= p2; |
/* недопустимое присваивание, т. к. не совпадают |
|
|
|
базовые типы указателей |
*/ |
/* конец программного кода примера |
*/ |
97
Отметим, что, несмотря на недопустимость присваивания p3 = p1 в приведенном выше примере компилятор языка Си ограничивается выводом предупреждения (Компилятор языка С++ в этой ситуации выводит сообщение об ошибке).
Операция взятия адреса для указателя. Указатели на указатели
Пусть имеется следующий фрагмент программы
//..
int n = 5; int *p = &n; int **pp = &p;
Переменная pp является указателем на указатель или двухуровневым указателем. Эта переменная получена применением оператора взятия адреса & к указателю p. Указатели на указателиприменяются на практике. Например, в качестве параметров функций (см. прототип функции strtok).
Указатели и операция индексирования
Рассмотрим следующий фрагмент программного кода:
#define MSIZE 5 #include <stdio.h> int main(void)
{
int x[MSIZE] = {1, 3, 5, 7, 9}; int *p = &x[1];
printf(“p[1] = %d”, p[1]); // ..
}
В рассматриваемом фрагменте программы объявлен указатель p, который инициализирован адресом второго элемента массива x. Затем функции printf() к указателю p применен оператор индексные скобки []. В результате выполнения этого фрагмента программы на экране получим p[1] = 5.
Арифметические операции с указателями
Арифметические операции применяются к укзателям, которые работают с массивами. К числу допустимых арифметических операций к таким указателям относятся:
∙++ (инкремент),
∙--(декремент),
∙сложение с целым числом,
∙вычитание целого числа.
∙вычисление разности двух указателей. Рассмотрим слеющий программный код:
#define MSIZE 5 #include <stdio.h>
98