Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Поскольку эти функции предназначены для работы с любым типом данных, два их аргумента имеют тип указателя на void. В С разрешено присваивать указателю типа void* указатель любого типа. Оборотная сторона такой гибкости состоит в том, что функции не способны распознавать, какого типа данные копируются. Поэтому в них присутствует третий аргумент, задающий количество копируемых байтов. Обратите внимание, что для массива количество байтов в общем случае не совпадает с количеством элементов. Таким образом, при копировании массива из 10 значений double в качестве третьего аргумента должно применяться выражение 10*sizeof (double), а не 10.
В листинге 16.20 показаны некоторые примеры использования этих двух функций. В нем предполагается, что тип double имеет в два раза больший размер, чем int, и для проверки этого предположения применяется средство _Static_assert из С11.
Листинг 16.20. Программа mems. с
708 глава 16
Вот как выглядит вывод:
Использование memcpy(): значения (исходные данные):
123456789 10
целевые данные (копия значений):
123456789 10
Использование memmove() с перекрывающимися областями: значения -- элементы 0-5 скопированы в элементы 2-7:
1 2 1 2 3 4 5 8 9 10
Использование memcpy() для копирования double в int: целевые данные -- 5 значений double в 10 позициях int:
0 1073741824 0 1091070464 536870912
1108516959 2025163840 1143320349 -2012696540 1179618799
Последний вызов memcpy() копирует данные из массива типа double в массив типа int. Это демонстрирует тот факт, что функция memcpy() ничего не знает, дай не заботится, о типах данных; она просто копирует байты из одной области в другую. (Вы могли бы, к примеру, копировать байты из структуры в массив символов.) Кроме того, никакого преобразования данных не происходит. Если организовать цикл, выполняющий поэлементное присваивание, то значения типа double будут преобразованы в тип int. В этом случае байты копируются в том виде, как есть, и программа затем интерпретирует комбинации битов, как если бы они имели тип int.
Препроцессор и библиотека С 709
Переменное число аргументов: файл stdarg.h
Ранее в этой главе обсуждались макросы с переменным числом аргументов. Заголовочный файл stdarg.h предоставляет похожую возможность для функций. Однако использовать ее немного сложнее. Вы должны выполнить следующие действия.
1. Подготовить прототип функции, в котором применяется троеточие.
2. Создать в определении функции переменную типа va_list.
3. Использовать макрос для инициализации этой переменной списком аргументов.
4. Применить макрос для доступа к списку аргументов.
5. Использовать макрос для очистки.
Давайте рассмотрим эти действия более подробно. Прототип для функции подобного рода должен иметь список, содержащий, по крайней мере, один параметр, за которым следует троеточие:
void f 1 (int n, ...); // допустимо
int f2(const char * s, int k, ...); // допустимо
char f3(char cl, char c2); // недопустимо, троеточие не в конце
double f3(...); /./ недопустимо, параметры отсутствуют
Крайний справа параметр (предшествующий троеточию) играет специальную роль; для его обозначения в стандарте используется термин parmN. В предшествующих примерах роль parmN играл параметр n в первом случае и к — во втором. Фактическим аргументом, передаваемым этому параметру, является количество аргументов, которые представлены разделом троеточия. Например, прототипироваиную ранее функцию fl() можно вызывать следующим образом:
f1 (2, 200, 400); //2 дополнительных аргумента
f1 (4, 13, 117, 18, 23); // 4 дополнительных аргумента
Тип va_list, объявленный в заголовочном файле stdargs.li, представляет объект данных, применяемый для хранения параметров, которые соответствуют разделу троеточия в списке параметров. Начало определения функции с переменным числом аргументов выглядит примерно так:
double sum(int lim,...)
{
va_list ар; // объявление объекта для хранения аргументов
В этом примере lim является параметром parmN и указывает количество аргументов в списке переменных-аргументов.
Затем функция будет использовать макрос va start(), также определенный в stdargs.li, для копирования списка аргументов в переменную va_list. Макрос принимает два аргумента: переменную va_list и параметр parmN. Продолжая предыдущий пример, переменная va list названа ар, а параметру parmN назначено имя lim, так что вызов будет иметь следующий вид:
va_start(ар, lim); // инициализация ар списком аргументов
На следующем этапе производится доступ к содержимому списка аргументов. Эго предусматривает применение еще одного макроса, va_arg(), который принимает два аргумента: переменную типа va list и имя типа. При первом вызове он возвращает первый элемент списка, при следующем вызове — следующий элемент списка и т.д.
710 Глава 16
Например, если первым аргументом в списке был double, а вторым — int, вы мог ли бы поступить так:
Но будьте внимательны. Тип аргумента на самом деле должен соответствовать спецификации. Если первым аргументом является 10.0, предыдущий код для tic работает нормально. Однако если аргументом оказывается 10, код может не заработать; автоматическое преобразование double в int, предпринимаемое для операции присваивания, здесь не происходит.
Наконец, вы должны провести очистку с помощью макроса va_end(). Например, может понадобиться освободить память, динамически выделенную для хранения аргументов. Этот макрос принимает в качестве аргумента переменную va_list:
va_end(ap); // очистка
После этого переменная ар может оказаться непригодной к употреблению до тех пор, пока вы не инициализируете ее повторно посредством макроса va_start.
Поскольку макрос va_arg() не обеспечивает копирование предыдущих аргументов для их возможного восстановления, может оказаться целесообразным сохранение копии переменной va list. Для этой цели в стандарте С99 предусмотрен макрос по имени va copy(). Он принимает два аргумента типа va list и копирует второй аргумент в первый:
На данном этапе по-прежнему можно извлечь первые два элемента из арсору, несмотря на то, что они были удалены из ар.
В листинге 16.21 приведен краткий пример использования этих возможностей для создания функции, которая суммирует переменное число аргументов; здесь первым аргументом sum() является количество суммируемых элементов.
Листинг 16.21. Программа varargs.c
Интервал:
Закладка: