为什么 unsigned typeof(var) 不起作用? const typeof(x) 工作正常,为什么不呢?

Why doesn't unsigned typeof(var) work? const typeof(x) works fine, so why not?

在宏中,我想让值具有相同的宽度,但是没有符号。于是,自然而然的想到了unsigned typeof(x)。但是,它没有编译:

/home/guest/mc.c: In function ‘main’:
/home/guest/mc.c:14:36: error: expected ‘)’ before ‘typeof’
   14 |                         *((unsigned typeof(minus)*)&minus));

不知为什么会这样?也许我做错了什么?我能以某种方式将 unsigned 添加到变量类型吗?

PS。我注意到,添加 const 的类似演员可以正常工作:

#define add_const(x) ((const typeof(x))(x))

此外,添加 * 以创建指针类型也可以。

看起来唯一的方法是在通用选择中枚举所有“有趣”的类型。

#define to_unsigned(x) _Generic((x),     \
                char: (unsigned char     )(x),     \
         signed char: (unsigned char     )(x),     \
                 int: (unsigned int      )(x),     \
               short: (unsigned short    )(x),     \
                long: (unsigned long     )(x),     \
           long long: (unsigned long long)(x),     \
             default: x)

short a;
typeof(to_unsigned(a)) ua;

这不可移植,因为实现可以提供更多的整数类型。然而 typeof 本身是特定于 gcc 的。由于 gcc 似乎提供所有其他标准整数类型(size_tuint8_t 等)作为 typedef 名称而不是单独的不同类型,因此这些情况应该涵盖所有内容。

unsigned typeof(x) 不起作用而 const typeof(x) 起作用的根本原因是 unsignedconst 是两个不同的语法组件。 unsigned 它是一个类型说明符,而 const 是一个类型限定符。

类型说明符列表(请注意,空 unsigned 在句法上等同于 unsigned int:

6.7.2 Type specifiers

1. Syntax

      type-specifier:
             void
             char
             short
             int
             long
             float
             double
             signed
             unsigned
             _Bool
             _Complex
             atomic-type-specifier
             struct-or-union-specifier
             enum-specifier
             typedef-name

2. Constraints

...

  • unsigned, or unsigned int

所以 unsigned typeof(x) 等价于 unsigned int typeof(x).

const 是类型限定符:

6.7.3 Type qualifiers

1. Syntax

      type-qualifier:
             const
             restrict
             volatile
             _Atomic

const char 是正确的 C 代码,假设 xcharunsigned int char 不是正确的 C 代码。

Why doesn't unsigned typeof(var) work? const typeof(x) works fine, so why not?

根据 clause 6.7 of the GCC 10.2 documentation:

A typeof construct can be used anywhere a typedef name can be used.

在 C 2018 6.7 中指定的声明的 C 语法中,声明说明符被划分为 classes:

  • Storage-class 说明符,例如 staticextern.
  • 类型说明符,例如 intfloat
  • 类型限定符,例如 constvolatile
  • 函数说明符,例如 inline.
  • 对齐说明符,例如 _Alignas(4).

条款 6.7.2 表明 typedef-name 是一个类型说明符。第 2 段指定了它们的使用方式。它给出了允许组合的列表,包括:

  • intsignedsigned int
  • unsigned,或unsigned int
  • typedef 名称

因此,unsigned 只能用于此列表中显示的特定组合,例如 unsigned long long。 typedef 名称单独显示为一个项目,没有 unsigned。所以它不能与 unsigned.

一起使用

相比之下,const是一个限定符,是一种不同的说明符,6.7的其他条款允许混合使用不同种类的说明符(有一些额外的限制在这里不相关,例如最多有一个存储-class 说明符)。