- •Происхождение языка с
- •Язык среднего уровня
- •Структурированный язык
- •Язык программирования
- •Компиляторы против интерпретаторов
- •Вид программ на с
- •Библиотеки и компоновка
- •Раздельная компиляция
- •Карта памяти с-программы
- •Переменные, константы, операторы и выражения
- •Идентификаторы
- •Типы данных
- •Модификаторы типов
- •Модификаторы доступа
- •Объявление переменных
- •Локальные переменные
- •Формальные параметры
- •Глобальные переменные
- •Спецификаторы хранения
- •Статические переменные
- •Статические локальные переменные
- •Статические глобальные переменные
- •Регистровые переменные
- •Оператор присваивания
- •Многочисленное присваивание
- •Преобразование типов при присваивании
- •Инициализация переменных
- •Константы
- •Символьные константы с обратным слэшем
- •Операторы
- •Арифметические операторы
- •Увеличение и уменьшение
- •Операторы отношения и логические операторы
- •Битовые операторы
- •Оператор ?
- •Операторы указания & и *
- •Оператор sizeof
- •Оператор «запятая»
- •Операторы [ ] u ()
- •Приоритеты в с
- •Выражения
- •Преобразование типов в выражениях
- •Принудительные преобразования
- •Пробелы и круглые скобки
- •Сокращенные операторы в с
- •Операторы управления программой
- •Истина и ложь в с
- •Операторы выбора
- •Вложенные if
- •Лесенка if-else-if
- •Оператор ?
- •Вложенные операторы switch
- •Вариации цикла for
- •Бесконечный цикл
- •Циклы for без тела
- •Метки и goto
- •Функции
- •Оператор return
- •Выход из функции
- •Возвращаемые значения
- •Значения, возвращаемые функцией main()
- •Правила видимости для функций
- •Аргументы функции
- •Передача по значению и передача по ссылке
- •Создание передачи по ссылке
- •Передача массивов в функции
- •Аргументы функции main()
- •Функции, возвращающие нецелые значения
- •Использование прототипов функции
- •Прототипы стандартных библиотечных функций
- •Создание прототипов функций, не имеющих параметров
- •Возврат указателей
- •Рекурсия
- •Сопоставление классического и современного объявления параметров
- •Указатели на функции
- •Особенности реализации
- •Параметризированные функции и функции общего назначения
- •Эффективность
- •Массивы
- •Одномерный массив
- •Создание указателя на массив
- •Передача одномерных массивов в функции
- •Двумерные массивы
- •Массивы строк
- •Многомерные массивы
- •Индексация с помощью указателей
- •Размещение массивов
- •Инициализация массива
- •Инициализация безразмерных массивов
- •Пример программы игры в крестики-нолики
- •Указатели
- •Указатели - это адреса
- •Переменные-указатели
- •Операторы для работы с указателями
- •Выражения с указателями
- •Присваивание указателей
- •Арифметические действия с указателями
- •Сравнение указателей
- •Динамическое выделение и указатели
- •Указатели на константы
- •Указатели на константы
- •Указатели на константы
- •Указатели и массивы
- •Указатели на символьные массивы
- •Массивы указателей
- •Указатели на указатели - многочисленное перенаправление
- •Инициализация указателей
- •Указатели на функции
- •Проблемы, связанные с указателями
- •Структуры, объединения и определяемые пользователем типы
- •Структуры
- •Доступ к членам структуры
- •Присваивание структур
- •Массивы структур
- •Программа инвентаризации
- •Передача структур в функции
- •Передача членов структур в функции
- •Передача всей структуры в функцию
- •Указатели на структуры
- •Объявление указателя на структуру
- •Использование указателей на структуру
- •Массивы и структуры в структурах
- •Битовые поля
- •Объединения
- •Перечисления
- •Использование sizeof для обеспечения переносимости
- •Ввод, вывод, потоки и файлы
- •Потоки и файлы
- •Текстовые потоки
- •Двоичные потоки
- •Консольный ввод/вывод
- •Чтение и запись символов
- •Чтение и запись строк: gets() и puts()
- •Форматированный консольный ввод/вывод
- •Печать символов
- •Вывод чисел
- •Вывод адресов
- •Спецификатор %n
- •Модификаторы формата
- •Спецификатор минимума ширины поля
- •Спецификатор точности
- •Выровненный вывод
- •Работа с другими типами данных
- •Модификаторы * u #
- •Спецификаторы формата
- •Ввод чисел
- •Ввод беззнаковых целых
- •Чтение отдельных символов с помощью scanf()
- •Чтение строк
- •Ввод адреса
- •Спецификатор %n
- •Использование множества сканирования
- •Пропуск нежелательных специальных символов
- •Обычные символы в управляющей строке
- •В scanf() следует передавать адреса
- •Модификаторы формата
- •Подавление ввода
- •Файловая система ansi с
- •Указатель на файл
- •Открытие файла
- •Запись символа
- •Чтение символа
- •Использование fopen(), getc(), putc() и fclose()
- •Использование feof()
- •Две расширенные функции: getw() и putw()
- •Работа со строками: fgets() и fputs()
- •Fseek() и произвольный доступ
- •Удаление файлов
- •Работа с консолью
- •Препроцессор и комментарии
- •Директивы условной компиляции
- •Использование defined
- •Операторы препроцессора # и ##
- •Предопределенные макросы
- •Комментарии
Массивы строк
В программировании типично использование массивов строк. Например, процессор ввода в базу данных может проверять команды пользователя в строковом массиве. Для создания массива строк используется двумерный массив символов. Левый индекс определяет число строк, а правый индекс - максимальное число символов в каждой строке. Данный фрагмент кода объявляет массив из 30-ти строк, причем каждая может содержать до 79 символов включительно: char str_array [30] [80]; Доступ к отдельным строкам очень прост - необходимо просто определить левый индекс. Следующий оператор вызывает функцию gets(), передавая ей в качестве параметра третью строку массива str_array: gets(str_array [2]) ; Данная функция эквивалентна gets(&str_array [2] [0]); но предыдущий вариант более типичен при написании профессиональных программ.
Чтобы лучше понять принцип работы символьных массивов, рассмотрим следующую программу, использующую массив как основу простейшего текстового редактора. #include <stdio.h> #define MAX 100 #define LEN 255 char text[MAX][LEN]; /* простейший текстовый редактор */ int main(void) { register int t, i, j; for (t=0; t<MAX; t++) { printf ("%d: ", t); gets(text [t]); if(!*text [t]) break; /* выход по пустой строке */ } /* посимвольный вывод текста */ for (i=0; i<t; i++) { for(j=0; text[i][j]; j++) printf("%с", text[i][j]); printf ("%с", '\n'); } return 0; } Данная программа осуществляет ввод текста, пока не встретится пустая строка. Затем она отображает каждую строку. В целях иллюстрации она выводит текст посимвольно, с использованием первого индекса. Поскольку каждая строка массива завершается нулевым символом, подпрограмма, отображающая текст, может быть упрощена: for (i=0; i<t; i++) printf("%s\n", text[i]);
Многомерные массивы
С позволяет создавать массивы с размерностями больше двух. Многомерный массив объявляется следующим образом: тип имя [разнерN] ... [размер2] [размер1];
Массивы, имеющие более трех размерностей, используются чрезвычайно редко, поскольку требуется большой объем памяти для их хранения.
Важно знать, что в многомерных массивах требуется некоторое время на вычисление каждого индекса. Это означает, что доступ к элементу в многомерных массивах происходит медленнее, чем доступ в одномерных массивах. По этой и другим причинам, если возникает необходимость в многомерных массивах, для них чаще всего память выделяется динамически с использованием функции динамического выделения памяти.
При передаче многомерных массивов функции следует определить все размерности, кроме самой левой. Например, если массив m определен как int m [4] [3] [6] [5]; то функция func1(), получающая m, может выглядеть следующим образом: int func1 (int d[] [3] [6] [5]) { ... }
Конечно, можно свободно включить и левую размерность, если так нравится.
Индексация с помощью указателей
Указатели и массивы очень тесно связаны между собой. Как объяснялось ранее, имя массива без индекса - это указатель на первый элемент массива. Пусть имеется массив char р [10]; тогда следующие операторы идентичны: р &р [0] Выражение р == &р [0] выдает истину, поскольку адрес первого элемента и адрес массива совпадают.
Справедливо и обратное. Любой указатель может быть проиндексирован, как будто это массив. Например: int *р, i [10]; p = i; р[5] = 100; /* присвоение с помощью индекса */ *(р+5) = 100; /* присвоение с помощью арифметики с указателями */ Оба оператора присваивания помещают значение 100 в шестой элемент i. Первый оператор использует индексацию с р, а второй - арифметику указателей. Так или иначе, результат одинаков.
Данные способы индексации совершенно справедливы для массивов размерности 2 и более. Предположим, что а - это целочисленный массив 10 на 10. Тогда нижеприведенные операторы эквивалентны: а &а [0] [0] Более того, к элементу 0, 4 массива а можно обратиться или с помощью индексации массива -а[0][4], или с помощью указателя — *((int *) а + 4). Аналогично к элементу 1, 2 можно обратиться или с помощью индексации массива - а [1] [2], или с помощью указателя - *((int *) а + 12). В целом, для любого двумерного массива справедливо а[j][k] эквивалентно *((тип *) a + (j * длина строки) + k) где тип - это базовый тип массива.
Указатели иногда используются для обращения к массивам, поскольку арифметика указателей чаще всего выполняется быстрее, чем индексация массивов. Преимущество - скорость использования указателей - наиболее заметно, когда осуществляется последовательный доступ к массиву. В данной ситуации указатель может увеличиваться или уменьшаться с помощью эффективных операторов увеличения или уменьшения. С другой стороны, если доступ к массиву происходит случайным образом, то лучше использовать индексацию массива, а не указатели.
Двумерные массивы подобны массивам указателей на строки. Поэтому использование отдельных указателей является одним из легких способов доступа к элементам двумерного массива.
Следующая функция демонстрирует данный прием. Она выводит содержимое указанной строки глобального целочисленного массива num: int num [10][10]; void pr_row (int j ) { int *p, t; p = num [ j]; /* получение адреса первого элемента строки j */ for(t=0; t<10; ++t) printf("%d ",(p+t) ); } Данный код может быть обобщен, если передавать в качестве аргументов строку, длину строки и указатель на первый элемент массива: /* общий */ void pr_row(int j, int row_dimension, int *p) { int t; p = p + (j * row_dimension); for(t=0; t<row_dimension; ++t) printf("%d ", *(p+t)); }
С массивами, имеющими размерность более чем 2, можно поступать аналогичным образом. Например, трехмерный массив может быть упрощен до указателя на двумерный массив, который в свою очередь может быть упрощен до указателя на одномерный массив. В общем случае N-мерный массив может быть упрощен до указателя на (N-1)-мерный массив. Данный новый массив может быть аналогичным образом упрощен. Этот процесс продолжается, пока не получится одномерный массив.
