- •7. Программирование алгоритма линейной структуры на языке Си 27
- •8. Понятие о препроцессоре языка Си 28
- •9. Операторы языка Си и приемы программирования 28
- •10. Массивы. Адресная арифметика языка Си 47
- •1.Правила записи программы на языке Си
- •2.Правила формального описания синтаксиса языка программирования
- •3.Идентификаторы языка Си
- •4.Понятие о типах данных.
- •4.1.Системы счисления. Представление данных в эвм.
- •4.2.Основные типы данных языка Си
- •4.3.Правила записи констант различных типов
- •4.4.Беззнаковый тип для целых данных
- •4.5.Символьные строки
- •5.Понятие функции
- •5.1.Стандартная функция printf
- •5.2.Стандартная функция scanf
- •6.Операции и выражения
- •6.1.Простейшие арифметические операции
- •6.2.Операция присваивания
- •6.3.Оператор-выражение
- •6.4.Использование в выражениях операндов разных типов
- •6.5.Операции преобразования типов
- •6.6.Стандартные математические функции
- •6.7.Простейшие функции, определяемые программистом
- •6.8.Дополнительные арифметические операции
- •6.9.Дополнительные операции присваивания
- •6.10.Битовые операции
- •6.11.Операции отношения
- •6.12.Логические операции
- •6.13.Операция определения размера данных
- •6.14.Приоритеты операций
- •7.Программирование алгоритма линейной структуры на языке Си
- •8.Понятие о препроцессоре языка Си
- •9.Операторы языка Си и приемы программирования
- •9.1.Оператор цикла while
- •9.2.Условный оператор и условная операция
- •1) Короткие операторы:
- •2) Группы коротких операторов:
- •3) Длинные группы операторов:
- •9.3.Запись алгоритмов с помощью диаграмм Несси - Шнейдермана (структограмм )
- •9.4.Н екоторые приемы программирования
- •9.5.Оператор прерывания цикла
- •9.6.Оператор продолжения цикла
- •9.7.Пример организации простейшего меню
- •9.8.Множественный выбор. Оператор переключения
- •9.9.Оператор цикла do-while.
- •9.10.Перечисления. Работа с клавиатурой ibm pc
- •9.11.Пример организации светового меню
- •10.Массивы. Адресная арифметика языка Си
- •10.1.Описание массива
- •10.2.Ввод-вывод массива
- •10.3.Инициализация массива
- •10.4.Программа вычисления длины строки символов
- •10.5.Двумерные массивы (массивы массивов)
- •10.6.Адресная арифметика языка Си
- •10.7.Указатели и одномерные массивы
- •10.8.Указатели и двумерные массивы
- •10.9.Указатели и функции
- •10.10.Оператор typedef
- •10.11.Дополнительные описания указателей для ibm pc
- •10.12.Непосредственная работа с экранной памятью
- •11.Дополнительные сведения о функциях
- •11.1.Области видимости и глобальные данные
- •11.2.Время жизни переменных и классы памяти языка Си
- •11.3.Передача аргументов в функцию
- •11.4.Возврат значений из функций
- •12.Работа с динамической памятью
- •12.1.Стандартные функции управления динамической памятью
- •12.2.Пример использования динамической памяти
- •12.3.Особенности работы с двумерными массивами
- •12.3.1.Пересчет индексов вручную
- •12.3.2.Массивы с постоянной длиной строки
- •12.3.3.Общий случай двумерного массива
- •12.4.Особенности работы с массивами большого размера
- •13.Модульное программирование в системе Turbo c
- •13.1.Обеспечение корректной стыковки модулей
- •13.2.Создание библиотек функций
- •14.Некоторые библиотечные функции языка Си
- •14.1.Функции консольного ввода/вывода (уникальны для tc)
- •14.2.Функции обработки строк.
- •14.3.Функции распознавания вида символа
- •14.4.Функции преобразования данных
- •15.Структуры языка c.
- •15.1.Описание структуры
- •1 Способ
- •2 Способ
- •15.2.Трактовка имени структуры.
- •15.2.1.Доступ к элементу структуры.
- •15.3.Инициализация структур.
- •15.4.Структуры и функции.
- •15.5.Поля бит в структурах.
- •16.Объединения.
- •17.Дополнительные сведения о препроцессоре языка c.
- •18.Условное выражение.
- •18.1.Приоритеты и направления операций.
- •19.Динамические данные.
- •19.1.Линейные списки.
- •19.2.Организация данных в виде стека.
- •19.3.Организация данных в виде очереди.
- •19.4.Организация данных в виде деревьев.
- •20.Библиотека ввода-вывода языка c.
- •20.1.Открытие потока.
- •20.2.Закрытие потока.
- •20.3.Предопределенные указатели потоков.
- •20.4.Функции ввода-вывода.
11.4.Возврат значений из функций
Возврат результата работы функции в вызывающую программу в виде единственного значения можно осуществить с помощью оператора return. При этом результат возвратится как значение самой функции и должен иметь соответствующий тип.
Типы возвращаемых значений могут быть любыми, кроме массивов. Тип void означает, что функция не возвращает никакого значения. Тип void* означает, что функция возвращает указатель на произвольный тип данных.
Если необходимо изменить содержимое массива, его адрес нужно передать в функцию и обычным способом, с помощью операции индексации изменить нужные элементы массива. В следующем примере функция FillArray() заполняет массив указанным значением:
void FillArray(double A[], int nA, double val)
{
int i;
for (i=0; i<nA; i++) A[i] = val;
}
void main (void)
{
double B[100];
FillArray(B, 40, 35.4);
/* ... */
FillArray(&B[60], 20, 15.4);
/* ... */
}
Первый вызов FillArray() заполняет 40 первых элементов массива B значением 35.4, второй вызов заполняет 20 элементов массива B, начиная с элемента B[60], значением 15.4. При возврате из функции массив будет изменен, т. к. занесение значения val происходит непосредственно по нужному адресу.
Эту же функцию можно использовать для заполнения строк двумерного массива:
void main (void)
{
double a[10][20];
int n = sizeof(a) / sizeof(a[0]);
int m = sizeof(a[0]) / sizeof(a[0][0]);
int i;
/* ... */
for(i=0; i<n; i++ )
FillArray(a[i], m, 14.6);
/* ... */
}
В примере следует обратить внимание на соответствие типов передаваемых параметров и на способ вычисления числа строк и числа столбцов двумерного массива.
Возврат из функции нескольких значений, которые не являются элементами массива, можно организовать, используя указатели. В следующем примере функция Decart() осуществляет перевод пары полярных координат в декартовые:
void Decart(double *px, double *py, double r, double f)
{
(*px) = r * cos(f);
(*py) = r * sin(f);
}
При обращении к данной функции для параметров px и py нужно передавать адреса:
void main(void)
{
double x, y, r=5, f=0.5;
/* ... */
Decart( &x, &y, r, f );
/* ... */
}
В данном примере при вызове функции создаются локальные копии адресов переменных x и y, а внутри функции происходит обращение к переменным x и y через их адреса (как и в случае массивов), поэтому значения x и y после вызова функции будут изменены.
12.Работа с динамической памятью
12.1.Стандартные функции управления динамической памятью
Данные, которые создаются, инициализируются и уничтожаются по требованию программиста называются динамическими. Для управления такими данными используются специальные стандартные функции, прототипы которых описаны в заголовочном файле <malloc.h> (для некоторых компиляторов <alloc.h>).
Для запроса динамической памяти служит функция malloc(), которая имеет следующий прототип:
void * malloc(size_t size);
Функция malloc() выделяет область динамической памяти, размером size байт, и возвращает адрес этой области памяти.
Параметр size, имеет тип size_t, который описан в файле <malloc.h> с помощью оператора typedef и используется для описания размеров, счетчиков и т.д. Обычно тип size_t соответствует типу unsigned int.
В том случае, когда функция malloc() не может удовлетворить запрос на память, она возвращает значение NULL, то есть значение не существующего указателя. Константа NULL описана в заголовочном файле <malloc.h>. Значение NULL возвращается и в том случае, когда значение параметра size нулевое.
Поскольку функция malloc() возвращает значение на произвольный тип данных, то возвращаемое значение должно быть явно преобразовано к нужному типу данных.
После того, как выполнена вся работа с выделенной областью памяти, ее следует освободить с помощью функции free(), имеющей следующий прототип:
void free(void *block);
где block - указатель на область памяти, значение которого ранее было возвращено какой-либо функцией выделения памяти.
Если при вызове функции free() значение указателя block не соответствует адресу, возвращенному функцией выделения памяти, то результат выполнения функции free() непредсказуем, а область динамической памяти может быть вообще разрушена.
Не допускается также освобождать уже освобожденный блок памяти.
Значение параметра block равное NULL не вызывает никаких действий со стороны функции free();
Рассмотрим типичную последовательность действий при работе с динамической памятью:
double *A; int n;
...
n = 200;
...
A = (double *) malloc( n * sizeof(double) );
...
/* Работа с массивом A */
...
free(A);
В рассмотренном фрагменте программы выделяется память для хранения n элементов типа double. В целях совместимости никогда не следует явно задавать размер элемента данных. Нужно пользоваться операцией sizeof(). Возвращаемое функцией malloc() значение преобразуется к типу указателя на double.
Как видно из примера, функции работы с динамической памятью позволяют использовать массивы с границами, задаваемыми переменными, а не константами.
В некоторых случаях бывает полезной функция calloc(), которая не только выделяет память, но и заполняет область выделенной памяти нулевыми значениями. Она имеет следующий прототип:
void * calloc(size_t nitems, size_t size);
Функция выделяет непрерывный блок памяти для nitems элементов данных размером size байт каждый и заполняет этот блок нулевыми значениями. В остальном работа ее аналогична работе функции malloc().
Функция realloc() служит для изменения размера ранее выделенного блока памяти:
void *realloc(void *block, size_t size);
Здесь block - адрес ранее выделенного блока памяти, size - новый размер блока в байтах. Функция возвращает значение нового указателя на блок памяти, которое может и не совпадать со старым.
Функция гарантирует сохранность данных в блоке, разумеется, сохранность не более size байт. В остальном работа функции совпадает с работой ранее рассмотренных функций выделения памяти.
Все рассмотренные функции могут выделять память размером не более одного сегмента, то есть не более 64K в 16-ти разрядных моделях и не более 4G в 32-х разрядных моделях памяти.
При работе с динамической памятью следует иметь в виду, что в каждом выделенном блоке несколько байт отводится на служебную информацию. Так в 16-ти разрядной Large модели память выделяется блоками по размеру кратными 16 байтам, и в каждом блоке 4 байта служебные.
К сожалению, стандартные средства работы с динамической памятью не предусматривают "сборку мусора", то есть автоматическое перемещение выделенных блоков в динамической памяти так, чтобы между ними не было неиспользуемых промежутков. Поэтому от программиста требуется повышенное внимание к стратегии выделения и освобождения динамической памяти в своих программах. Иначе может получиться так, что требуемый блок памяти невозможно выделить, хотя суммарный объем неиспользуемой памяти допускает это.
Функция coreleft() возвращает значение оставшейся в динамической области памяти в байтах. Функция может иметь следующие прототипы в зависимости от моделей памяти:
unsigned coreleft(void); /* Маленьких модели */
unsigned long coreleft(void); /* Большие модели */
При использовании этих функций следует иметь в виду, что они возвращают не общее количество свободной динамической памяти и не размер наибольшего свободного блока, а размер блока памяти, который остался между наивысшем по адресу выделенным блоком и концом динамической памяти.
