
- •Происхождение языка с
- •Язык среднего уровня
- •Структурированный язык
- •Язык программирования
- •Компиляторы против интерпретаторов
- •Вид программ на с
- •Библиотеки и компоновка
- •Раздельная компиляция
- •Карта памяти с-программы
- •Переменные, константы, операторы и выражения
- •Идентификаторы
- •Типы данных
- •Модификаторы типов
- •Модификаторы доступа
- •Объявление переменных
- •Локальные переменные
- •Формальные параметры
- •Глобальные переменные
- •Спецификаторы хранения
- •Статические переменные
- •Статические локальные переменные
- •Статические глобальные переменные
- •Регистровые переменные
- •Оператор присваивания
- •Многочисленное присваивание
- •Преобразование типов при присваивании
- •Инициализация переменных
- •Константы
- •Символьные константы с обратным слэшем
- •Операторы
- •Арифметические операторы
- •Увеличение и уменьшение
- •Операторы отношения и логические операторы
- •Битовые операторы
- •Оператор ?
- •Операторы указания & и *
- •Оператор 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
- •Операторы препроцессора # и ##
- •Предопределенные макросы
- •Комментарии
Размещение массивов
Во многих ситуациях невозможно знать размер массива. Кроме этого, существует много программ, которым необходимо использовать всю доступную память даже при запуске на компьютерах с небольшим объемом памяти. Примерами таких программ являются текстовый редактор или база данных. В таких ситуациях невозможно использовать предопределенный массив, поскольку его размерности определяются во время компиляции и они не могут изменяться во время выполнения программы. Решением является создание динамического массива. Динамический массив использует участок свободной памяти, называемый кучей. (Помните, что любой указатель может быть проиндексирован, как будто это массив.)
В С можно динамически выделять и освобождать память с помощью стандартных библиотечных процедур: malloc(), которая выделяет память и возвращает указатель типа void* на начало выделенной памяти, и free(), которая возвращает ранее выделенную память в кучу для повторного использования. Прототипы malloc() и free() показаны ниже: void *malloc(size_t число_байт) void free(void *p); Обе функции используют заголовочный файл stdlib.h. Здесь число_байт - это число требуемых байт. Как упоминалось ранее, тип size_t определен как беззнаковое целое. Если недостаточно свободной памяти для выполнения запроса, malloc() возвращает NULL. Важно понимать, что free() может вызываться только с корректным ранее выделенным указателем, иначе может произойти нарушение организации кучи, что, возможно, приведет к краху программы.
Нижеприведенный фрагмент кода выделяет 1000 байт памяти: char *р; р = malloc (1000); /* получение 1000 байт */ Здесь р указывает на начало 1000-байтной области памяти. Обратим внимание, что не используется принудительное преобразование типов для преобразования указателя типа void*, возвращаемого malloc() к требуемому указателю на char, Поскольку malloc() возвращает указатель типа void*, он может присваиваться любому другому типу указателя и будет осуществлено автоматическое преобразование к типу целевого указателя. Тем не менее важно понимать, что данное автоматическое преобразование не поддерживается в С++. В С++ требуется однозначное преобразование типа, когда указатель типа void* присваивается другому типу указателя. Следовательно, в С++ предыдущий оператор должен быть переписан следующим образом: р = (char *) malloc(1000) ; /* получение 1000 байт */
В С++ необходимо использовать при присваивании преобразования одного типа указателя к другому. Это одно из фундаментальных отличий между С и С++.
Нижеприведенный пример показывает корректный способ использования динамически выделенного массива для чтения с клавиатуры с помощью функции gets(): /* Печатает строку в обратном порядке, используя динамическое выделение. */ #include <stdlib.h> #include <stdio.h> #include <string.h> int main(void) { char *s; register int t; s = (char *) malloc(80); if (!s) { printf("Memory request failed.\n"); return 1; } gets (s); for(t=strlen(s)-1; t>=0; t--) printf("%с", s[t]); free(s); return 0; } s тестируется до первого использования, чтобы проверить правильность возвращенного функцией malloc() указателя. Это необходимо для предотвращения случайного использования нулевого указателя. (Использование нулевого указателя практически однозначно приводит к краху системы.) Обратим внимание, как указатель s индексирует массив для вывода строки.
Можно иметь многомерные динамические массивы, но для доступа к ним следует использовать функции, поскольку должен существовать способ определения размера всех размерностей помимо левой. С этой целью указатель передается в функцию, которая имеет параметры, содержащие правильный размер массива. Для того, чтобы понять принципы работы, рассмотрим небольшой пример, создающий таблицу чисел от 1 до 10, содержащую их первую, вторую, третью и четвертую степени: #include <stdlib.h> #include <stdio.h> int pwr(int a, int b); void table(int p[5][11]), show(int p[5] [11]); /* данная программа выводит различные числа, возведенные в целочисленные степени */ int main(void) { int *р; р = (int *) malloc(55*sizeof (int)); if(!p) { printf("Memory request failed.\n"); return 1; } /* p - это просто указатель */ table(p); show(p); return 0; } /* создание таблицы чисел */ void table(int p[5] [11]) /* теперь компилятор думает, что р - это массив */ { register int i, j; for(j=1; j <11; j++) for (i=1; i<5; i++) p [i] [ j ] = pwr(j, i); /* вывод таблицы */ void show(int p[5][11]) { register int i, j; printf ("%10s %10s %10s %10s\n", "N", "N^2", "N^3", "N^4"); for(j=1; j<11; j++) { for(i=1; i<5; i++) printf("%10d ", p[i][j]); printf ("\n"); } } /* возведение а в степень b */ int pwr(int a, int b) register int t=1; for(; b; b--) t = t*a; return t; } Программа в итоге должна выдать:
N |
N^2 |
N^3 |
N^4 |
1 2 3 4 5 6 7 8 9 10 |
1 4 9 16 25 36 49 64 81 100 |
1 8 27 64 125 216 343 512 727 1000 |
1 16 81 256 625 1296 2401 4096 6561 10000 |
Способ, показанный в данной программе, позволяет использовать в С многомерные динамические массивы. На самом деле в функциях show() и table() мы имеем целочисленный массив 5 на 11. Отличие состоит в том, что место для хранения массива выделяется с помощью оператора malloc(), а не с помощью обычного оператора объявления массива. Также обратим внимание на использование sizeof для вычисления размера памяти, необходимой для выделения целочисленного массива 5 на 11. sizeof обеспечивает работу программы как с 16-, так и с 32-битными целыми.