Ответ 1
Является ли мое использование assert в качестве левого операнда выражения запятой действительным стандартом C? То есть стандарт позволяет мне использовать assert как выражение?
Да, он действителен, так как левый операнд оператора запятой может быть выражением типа void
. А функция assert
имеет void
как возвращаемый тип.
Мой компилятор C считает, что int b [NUM_ELEMS (a)]; является VLA. Любой способ убедить его в противном случае?
Он верит, потому что результат выражения для запятой никогда не является постоянным выражением (e..g, 1, 2 не является постоянным выражением).
EDIT1: добавьте обновление ниже.
У меня есть другая версия вашего макроса, которая работает во время компиляции:
#define NUM_ELEMS(arr) \
(sizeof (struct {int not_an_array:((void*)&(arr) == &(arr)[0]);}) * 0 \
+ sizeof (arr) / sizeof (*(arr)))
и, похоже, работает даже с инициализатором для объекта со статической продолжительностью хранения.
И он также корректно работает с вашим примером int b[NUM_ELEMS(a)]
EDIT2:
чтобы отправить комментарий @DanielFischer. Макрос выше работает с gcc
без -pedantic
только потому, что gcc
принимает:
(void *) &arr == arr
как целочисленное постоянное выражение, в то время как оно считает
(void *) &ptr == ptr
не является целочисленным постоянным выражением. Согласно C они оба не являются целыми константными выражениями и -pedantic
, gcc
корректно выдает диагностику в обоих случаях.
Насколько я знаю, нет 100% -ного портативного способа написать этот макрос NUM_ELEM
. C имеет более гибкие правила с константными выражениями инициализатора (см. 6.6p7 в C99), которые можно использовать для записи этого макроса (например, с помощью sizeof
и сложных литералов), но в блочной области C не требует, чтобы инициализаторы были постоянными выражениями, поэтому невозможно будет иметь один макрос, который работает во всех случаях.
EDIT3:
Я думаю, что стоит упомянуть, что в ядре Linux есть макрос ARRAY_SIZE
(в include/linux/kernel.h
), который реализует такую проверку, когда выполняется разреженный (проверка статического анализа ядра).
Их решение не переносимо и использует два расширения GNU:
-
typeof
оператор -
__builtin_types_compatible_p
встроенная функция
В основном это выглядит примерно так:
#define NUM_ELEMS(arr) \
(sizeof(struct {int :-!!(__builtin_types_compatible_p(typeof(arr), typeof(&(arr)[0])));}) \
+ sizeof (arr) / sizeof (*(arr)))