下面的宏使用_Generic来确定参数是否为数组(而不是指针):

#include <stdio.h>

#define IS_ARRAY(T)   \
  _Generic( &(T)[0],  \
    typeof(T) : 0,    \
    default   : 1     \
  )

int a[2];
int *p = a;

int main() {
  printf( "IS_ARRAY(a) = %d\n", IS_ARRAY(a) );
  printf( "IS_ARRAY(p) = %d\n", IS_ARRAY(p) );
}

以及:

$ clang --version
clang version 17.0.6
$ clang -std=c2x a.c

我得到了:

a.c:15:33: warning: due to lvalue conversion of the controlling expression, association of type 'typeof ((a))' (aka 'int[2]') will never be selected because it is of array type [-Wunreachable-code-generic-assoc]
   15 |   printf( "IS_ARRAY(a) = %d\n", IS_ARRAY(a) );
      |                                 ^
a.c:7:5: note: expanded from macro 'IS_ARRAY'
    7 |     typeof( (T) ) : 0,                                                      \
      |     ^
1 warning generated.

然而:

$ ./a.out
IS_ARRAY(a) = 1
IS_ARRAY(p) = 0

尽管有警告,但似乎起作用了.这里的警告是错的吗?

推荐答案

这意味着第一个表达式_Generic按照"左值转换"(C17中的新变化)进行转换,并根据转换后的值判断下面的通用关联列表.

这是因为在C11中,如何处理_Generic内的限定符是含糊的.一些编译器允许将类型+限定符const int: 1, int: 2等作为两个独立的用例,但有些则不允许.因此,作为修正,C17保证了传递的表达式的左值转换,因此在我的例子中它永远不会变成const int.请注意,如果我们try 执行此操作,clang会给出与您相同的警告:

#define IS_INT(x) _Generic((x), int: 1, const int: 1)

它也不能成为该问题的数组类型,因为数组不能赋值/值转换.您收到的警告是typeof(T),这是一个数组类型.

  • (&T)[0]不是一个正确的修复,因为这会将数组转换为指针,然后再次转换为数组,然后它将衰减……你会得到int*分.

  • &(T)[0]不是一个正确的修复,因为这将derefence数组,类型int,然后你得到一个地址,int*.

请注意,优先于一元&的是[].


然而,您可以做的是获取传递的表达式的地址,并判断它是否是指向数组的指针.

作为另一个小技巧,类型int (*)[](不完整类型的数组)与每个数组指针int (*)[n]兼容,无论n如何(参见"复合类型"C17 6.2.7).

固定代码,符合C23:

#define IS_ARRAY(T)            \
  _Generic( &(T),              \
    typeof(*T) (*)[]  : true,  \
    default           : false  \
  )
  • 如果我们将int a[2];传递给this,则数组不会"衰减"为指向第一个元素的指针,而是&(T)将其转换为int (*)[2].
  • 然而,typeof(*T)像往常一样衰减数组,我们最终得到int.
  • int (*)[2]int (*)[]兼容,所以它是一个数组,并且具有正确的元素类型.

C++相关问答推荐

如何在C中通过转换为char * 来访问float的字节表示?

在Windows上构建无聊的SSL x64

错误:在.h程序中重新定义 struct

丑陋的三重间接:可扩展的缓冲区管理 struct

为什么在函数内部分配内存空间时需要添加符号?

SDL 2.0-从数组渲染纹理

在我的代码中,我需要在哪里编写输出函数?

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

Flose()在Docker容器中抛出段错误

什么是.c.h文件?

在另一个函数中使用realloc和指针指向指针

For循环不会迭代所有字符串字符吗?(初学者问题)

为什么我的半数组测试和奇数组测试不起作用?(我使用Assert进行调试)

如何在C宏定义中包含双引号?

如何组合两个宏来初始化C语言中的字符串数组?

STM32 FATFS用户手册(Um1721)中的代码正确吗?

在Ubuntu上使用库部署C程序的最佳实践

I';我试着从.txt文件中读取文本,并用c计算其中的单词数量

Struct 内的数组赋值

使用fread()函数读取txt文件