Ответ 1
Это мой второй ответ. И это дает два решения.
Первое решение требует расширения gcc; ОП сказал, что он предпочел бы ответы, которые работают в MSVC, но что "любой конкретный ответ будет".
Второе решение ворует идеи из превосходного ответа oaah fooobar.com/questions/23776/... и, вероятно, более портативен.
Начнем с классического определения:
#define NUMBER_naive(x) ((int)(sizeof(x) / sizeof(x)[0])) // signed is optional
Для первого решения в gcc вы можете выполнить тест, чтобы определить, оценивает ли какое-либо выражение массив (или он дает ошибку компиляции в (x)[0]
); Я протестировал это решение с 6-летним gcc 4.1.2:
#define NUMBER(x) __builtin_choose_expr( \
__builtin_types_compatible_p(typeof(x), typeof((x)[0])[]), \
NUMBER_naive(x), garbage_never_defined)
extern void *garbage_never_defined;
Второе решение:
#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);})) // BUILD_BUG_ON_ZERO()
#define NUMBER(x) (NUMBER_naive(x) * !ASSERT_zero((void *)&(x) == (x)))
Ниже приведена короткая тестовая программа на некоторых массивах образцов и указателях:
#include <stdio.h>
#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);})) // BUILD_BUG_ON_ZERO()
#define NUMBER_naive(x) ((int)(sizeof(x) / sizeof(x)[0]))
#define NUMBER(x) (NUMBER_naive(x) * !ASSERT_zero((void*)&(x) == (x)))
int a1[10];
extern int a2[];
extern int a3[10];
int *p;
int square[10][10];
static void foo(int param[10]) {
// printf("foo param %d\n", NUMBER(param));
}
static void bar(int param[][10]) {
// printf("bar param %d\n", NUMBER(param));
printf("bar param[0] %d\n", NUMBER(param[0]));
printf("bar *param %d\n", NUMBER(*param));
}
int main(void) {
printf("a1 %d\n", NUMBER(a1));
// printf("a2 %d\n", NUMBER(a2));
printf("a3 %d\n", NUMBER(a3));
// printf("p %d\n", NUMBER(p));
printf("square %d\n", NUMBER(square));
printf("*square %d\n", NUMBER(*square));
foo(a1);
bar(square);
return 0;
}
Это дает:
a1 10
a3 10
square 10
*square 10
bar param[0] 10
bar *param 10
Как вы можете видеть, я прокомментировал четыре строки, которые не должны или не должны компилироваться, три для трех указателей и один для неполного типа массива.
У меня была небольшая проблема с выбором третьего arg __builtin_types_compatible_p()
. gcc manual (правильно) указывает "Furthermore, the unused expression (exp1 or exp2 depending on the value of const_exp) may still generate syntax errors."
Так что на данный момент я установил его для никогда не созданной переменной garbage_never_defined
, поэтому для некоторых четырех пронумерованных строк, а не ошибки компиляции, мы получаем предупреждение компилятора и ошибку компоновщика.