_Static_assert(CHAR_BIT == 16, "Ошибочно предполагается 16-битовый тип char");
1 error generated.
$
statasrt.с;4;1; ошибка: отказ static_assert "Ошибочно предполагается 16битовый тип char"
_Static_assert (CHAR_BIT == 16, "Ошибочно предполагается 16-битовый тип char");
1 ошибка сгенерирована.
$
Синтаксически _Static_assert трактуется как оператор объявления. Следовательно, в отличие от большинства разновидностей операторов С, он может находиться либо в функции, либо (как в данном случае) быть внешним по отношению к функции.
Требование о том, что первым аргументом в _Static_assert должно быть целочисленное константное выражение, гарантирует возможность его оценки на этапе компиляции. (Вспомните, что выражения sizeof считаются целочисленными константами.) Поэтому в листинге 16.18 вы не можете подставить _Static_assert вместо assert(), т.к. для проверочного выражения в программе используется z > 0, которое является неконстантным и может быть вычислено только во время выполнения. В листинге 16.19 можно было бы применить assert (CHAR_BIT ==16) в теле main(), но это привело бы к выдаче предупреждения об ошибке лишь после компиляции и запуска программы, что менее эффективно.
Препроцессор и библиотека С 707
В заголовочном файле assert.h идентификатор static_assert определен как псевдоним для ключевого слова _Static_assert. Это делает С более совместимым с языком C++, в котором для рассмотренной возможности static assert используется в качестве ключевого слова.
Функции memcpy() И memmove()
ИЗ библиотеки string.h
Присваивать один массив другому нельзя, поэтому в таких случаях мы применяли циклы для поэлементного копирования одного массива в другой. Единственное исключение состоит в том, что для символьных массивов мы использовали функции strcpy() и strncpy(). Функции memcpy() и memmove() предлагают почти такие же услуги для других видов массивов. Рассмотрим прототипы этих функций:
void *memcpy(void * restrict si, const void * restrict s2, size_t n);
void *memmove (void *sl, const void *s2, size_t n);
Обе функции копируют n байтов из области, на которую указывает аргумент s2, в область, указанную аргументом si, и обе они возвращают значение si. Различие между этими двумя функциями, как указывает ключевое слово restrict, связано с тем, что memcpy() разрешено полагать, что две области памяти нигде не перекрываются друг с другом. Функция memmove() не делает такого предположения, поэтому копирование происходит так, как будто все байты сначала помещаются во временный буфер и только затем копируются в область назначения. Что произойдет, если применить memcpy() к перекрывающимся областям? В этом случае поведение функции не определено, т.е. она может как работать, так и не работать. Компилятор не запрещает использование функции memcpy(), когда этого делать не следует, поэтому именно вы несете ответственность за обеспечение того, что области памяти не перекрываются. Это еще одна часть тяжкой нощи программиста.
Поскольку эти функции предназначены для работы с любым типом данных, два их аргумента имеют тип указателя на 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.
Читать дальше