Вы можете определить, является ли выражение целочисленным выражением или выражением char*
, по крайней мере, в архитектурах, где приведение от указателя к uintptr_t
хорошо определено:
#define INT_OR_CHARP(X) (((uintptr_t)((X)+1) - (uintptr_t)(X)) == 1)
Это обнаружит, если X
- указатель на тип T
с sizeof(T) > 1
.Это не будет работать для void*
и других угловых случаев.И поскольку X
оценивается два раза, вам придется следить за побочными эффектами.
Чтобы избежать проблем с целочисленным переполнением, если X
имеет тип signed int
, вы можете заменить (X)
на
(1 ? (X) : (uintmax_t)0)
Это гарантирует, что если X
является целочисленным выражением, оно будет иметь тип uintmax_t
.Возможно, значение +1
обернется, но результат всегда будет четко определен, а разница между двумя частями всегда будет 1
.Если X
является выражением указателя, то это так, потому что любое константное целочисленное выражение значения 0
также является константой нулевого указателя .
Всего это дает
#define INT_OR_CHARP(X) (((uintptr_t)((1 ? (X) : (uintmax_t)0)+1) - (uintptr_t)(1 ? (X) : (uintmax_t)0)) == 1)