Каковы наиболее полезные новые функции на C99?

C99 существует уже более 10 лет, но поддержка для него медленная, поэтому большинство разработчиков застряли в C89. Даже сегодня я иногда слегка удивляюсь, когда сталкиваюсь с функциями C99 в коде C.

Теперь, когда большинство основных компиляторов поддерживают C99 (MSVC является заметным исключением, а некоторые встроенные компиляторы также отстают), я считаю, что разработчики, которые работают с C, вероятно, должны знать о том, какие функции C99 доступны для них. Некоторые из функций - это обычные функции, которые раньше не были стандартизованы (например, snprintf) или знакомы с С++ (размещение объявления гибкой переменной или однострочные комментарии //), но некоторые из новых функций были впервые введенные в C99 и незнакомые многим программистам.

Что вы находите наиболее полезные новые функции на C99?

Для справки стандарт C99 (обозначенный как черновик, но идентичный обновленному стандарту, насколько мне известно), список новых функций, а статус выполнения GCC C99.

Одна функция для каждого ответа, пожалуйста; не стесняйтесь оставлять несколько ответов. Приводятся примеры коротких примеров, демонстрирующих новые функции.

Ответ 1

Я так привык печатать

for (int i = 0; i < n; ++i) { ... }

в С++, что боль в использовании компилятора, отличного от C99, где я вынужден сказать

int i;
for (i = 0; i < n; ++i ) { ... }

Ответ 2

stdint.h, который определяет int8_t, uint8_t и т.д. Больше не нужно делать не переносные предположения о том, как широко ваши целые числа.

uint32_t truth = 0xDECAFBAD;

Ответ 3

Я думаю, что новые механизмы инициализатора чрезвычайно важны.

struct { int x, y; } a[10] = { [3] = { .y = 12, .x = 1 } };

ОК - не убедительный пример, но нотация верна. Вы можете инициализировать определенные элементы массива и конкретные элементы структуры.

Может быть, лучшим примером может быть это, хотя я бы признал, что это не очень убедительно:

enum { Iron = 26, Aluminium = 13, Beryllium = 4, ... };

const char *element_names[] =
{
    [Iron]      = "Iron",
    [Aluminium] = "Aluminium",
    [Beryllium] = "Beryllium",
    ...
};

Ответ 4

Поддержка однострочных комментариев, начинающихся с //.

Ответ 5

Массивы переменной длины:

int x;
scanf("%d", &x);
int a[x];
for (int i = 0; i < x; ++i)
    a[i] = i * i;
for (int i = 0; i < x; ++i)
    printf("%d\n", a[i]);

Ответ 6

Возможность объявлять переменные в местах, отличных от начала блока.

Ответ 7

Вариадические макросы. Легче создавать шаблонный код с неограниченным количеством аргументов.

Ответ 8

snprintf() - серьезно, стоит много сделать безопасные форматированные строки.

Ответ 9

Элементы гибкого массива.

6.7.2.1 Характеристики структуры и объединения

В качестве особого случая последний элемент структуры с более чем одним именованным элементом может имеют неполный тип массива; это называется элементом гибкого массива. За двумя исключениями элемент гибкого массива игнорируется. Во-первых, размер структуры должен быть равный смещению последнего элемента в противном случае идентичной структуры, которая заменяет элемент гибкого массива массивом неопределенной длины). Во-вторых, когда . (или ->) имеет левый операнд, который является (указателем на) структуру с элементом гибкого массива, а правый операнд - этим членом, он ведет себя так, как если бы этот элемент был заменен самым длинным массивом (с тем же типом элемента), который не сделает структуру больше, чем доступ к объекту; смещение массива должно оставаться равным элементу гибкого элемента массива, даже если это будет отличаться от размера заменяющего массива. Если этот массив не будет содержать никаких элементов, он будет вести себя так, как если бы у него был один элемент, но поведение не было определено, если была предпринята попытка доступа к этому элементу или для генерации указателя в прошлом он.

Пример:

typedef struct {
  int len;
  char buf[];
} buffer;

int bufsize = 100;
buffer *b = malloc(sizeof(buffer) + sizeof(int[bufsize]));

Ответ 10

Составные литералы. Установка структурных элементов по-члену составляет '89;)

Вы также можете использовать их для получения указателей на объекты с автоматической продолжительностью хранения без объявления ненужных переменных, например

foo(&(int){ 4 });

insteand

int tmp = 4;
foo(&tmp);

Ответ 11

Тип bool.

Теперь вы можете сделать что-то подобное:

bool v = 5;

printf("v=%u\n", v);

напечатает

1

Ответ 12

Поддержка функций inline.

Ответ 13

Смешанные литералы, уже упомянутые, но здесь мой убедительный пример:

struct A *a = malloc(sizeof(*a));
*a = (struct A){0};  /* full zero-initialization   */
/* or */
*a = (struct A){.bufsiz=1024, .fd=2};   /* rest are zero-initialized.  */

Это четкий способ инициализации данных, даже если он находится в куче. Невозможно забыть нулевое инициализацию.

Ответ 14

Ключевое слово restrict. Особенно, когда вы хрустаете цифры...

Ответ 15

Поддержка escape-последовательности Unicode:

printf("It all \u03B5\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC to me.\n");

Или даже, буквальные символы Юникода:

printf("日本語\n");

(примечание: может не работать в зависимости от вашего языка, переносная поддержка для разных кодировок займет больше работы)

Ответ 16

Шестнадцатеричные константы с плавающей запятой (0x1.8p0f) и спецификаторы преобразования (%a, %a). Если вы часто занимаетесь низкоуровневыми численными деталями, это огромное улучшение по сравнению с десятичными буквами и преобразованиями.

Они избавляют вас от забот о округлении при указании констант для алгоритма и очень полезны для отладки низкоуровневого кода с плавающей запятой.

Ответ 17

Лично мне нравится подтверждение IEC 60559: 1989 (двоичная арифметика с плавающей запятой для микропроцессорных систем) и гораздо лучшая поддержка с плавающей запятой.

В аналогичном ключе, установка и запрос режима округления с плавающей запятой, проверка номеров Nan/Infinity/subnormal и т.д., отлично подходит.