【问题标题】:What does POSIX mean by "two's complement representation" in stdint.h?POSIX 在 stdint.h 中的“二进制补码表示”是什么意思?
【发布时间】:2017-08-09 06:59:24
【问题描述】:

有符号整数溢出会引发未定义的行为,这是一个众所周知的事实,对有符号整数的按位操作充其量是不可靠的。因此,我在the POSIX standard 中发现了这一行相当好奇:

typedef 名称 int N _t 指定宽度为 N、无填充位、和二进制补码表示的有符号整数类型。 因此,int8_t 表示宽度正好为 8 的有符号整数类型位。

这是一个相当模糊的陈述,可能意味着这些事情的任何组合:

  1. intN_t 的范围是从INTN_MININTN_MAX
  2. sizeof(intN_t) == N/8
  3. intN_t 上的按位运算的行为与二进制补码表示的预期相同。 -1 ^ x == ~x 对每个x,在插入intN_t 转换之后。
  4. intN_t 不能有陷阱表示(并且优化编译器不得利用可能的陷阱)。
  5. intN_t 变量的溢出是已定义的行为(并从INTN_MAX 包装到INTN_MIN)。

(1) 和 (2) 对我来说似乎都很明显,基于文档的其余部分。 (1) 由INTN_MIN/MAX 的定义明确指定。 (2) 由“无填充位”暗示。

POSIX 需要 (3)、(4) 和 (5) 中的哪一个(如果有)?

【问题讨论】:

  • 关于 (2) - 为什么8?
  • @ecatmur 因为对于 POSIX CHAR_BIT 等于 8。

标签: c posix language-lawyer


【解决方案1】:

TL;DR

134 在存在 intN_t 的任何 C99、C11 编译器上都为真。 2 在存在 int8_t 的任何 C11 编译器上都为真 - 因为 int8_t 的存在意味着 CHAR_BIT 为 8。 5 特别不需要 C -有符号整数溢出的行为未定义。

POSIX 限制了允许的 C 实现,因此 CHAR_BIT 必须为 8 并且整数表示是二进制补码。因此,在 POSIX 平台上兼容的 C99/C11 编译器必须具有 int8_t,这使得语句 123 4 在 POSIX 上为真。由于 POSIX 没有说明有符号整数溢出,它仍然是未定义的,因此 5 为假。


引用的句子逐字取自 C11 (C99) 标准。 C11 7.20.1.1p1:

typedef 名称intN_t 指定一个有符号整数类型,宽度为N,没有填充位,并且是二进制补码表示。因此,int8_t 表示这种有符号整数类型,宽度正好为 8 位。

int8_t 在 C 中是可选,因此仅在标准中存在此片段甚至不需要 2 的补码表示。 C11 7.20.1.1p3:

这些类型是可选的。但是,如果实现提供了宽度为 8、16、32 或 64 位的整数类型,没有填充位,并且(对于有符号类型)具有二进制补码表示,则它应定义相应的 typedef 名称。

在您的原始陈述中,

  1. 当然是正确的,但这种情况并不能从 二的补码表示中得出。 int 范围从 INT_MININT_MAX 在任何人的补码架构上也是如此。但是,由此得出的结论是 INTN_MIN 的值是 。

  2. 遵循补码表示sizeof(intN_t)N / CHAR_BIT。但是,POSIX 要求 CHAR_BIT 是 8 所以 sizeof(intN_t) 确实是 N / 8

  3. 这是二进制补码表示

  4. 的唯一内容
  5. 遵循二进制补码表示,而是 2 的补码的组合并且没有填充位。

  6. 遵循二进制补码表示C 和 POSIX 都不需要


int8_t 类型不是 POSIX 指定的,而是 POSIX 采用和扩充的 C99、C11。 POSIX 增加了两个限制: * CHAR_BIT 必须正好为 8 且不能更大,即使 C 编程语言允许 * 不允许整数的补码或符号和大小表示,即使 C 编程语言允许这样做

C99、C11 指定如果存在,类型intN_t 必须有这么多位且没有填充位和2 的补码。如果int8_t存在,那么它的sizeof必须是1,因为它是signed char的同义词,而CHAR_BIT等于8。

intN_t 不会有陷阱表示,但这并不意味着那些具有明确 indeterminate 值的对象必须具有相同的值,或者将这些值传递给库函数将具有定义的行为。考虑以下片段:

int32_t *foo = malloc(sizeof(int32_t));
printf(PRId32 "\n", *foo);
printf(PRId32 "\n", *foo);
free(foo);

编译器甚至不需要调用malloc;它可以将其编译为等效于puts("42\n666"); - 即使没有int32_t 类型的陷阱值。这是因为malloc:

[...] 为大小由 size 指定且 其值不确定 的对象分配空间。

不确定真的意味着unstable; indeterminable


有符号整数溢出的行为始终未定义。

【讨论】:

  • 当我写INTN_MIN/MAX时,我实际上指的是标准中其他地方给出的数学值(即-2^(N-1)到2^(N-1) - 1),这是典型的二进制补码实现。
  • @Kevin C 标准仅提供 3 种可能的数字格式:符号和大小、1 的补码和 2 的补码。 POSIX 中的 2 的补码是指 C 中定义的 2 的补码,“典型”是唯一允许的。但是,是的,我已经注意到上面的数字了。
  • 我不确定是否可以从二进制补码和没有填充位中推断出没有陷阱表示。只有高位集可能是陷阱,但高位集和低位集可能是INT_MIN(或-INT_MAX)。
猜你喜欢
  • 2020-01-24
  • 2011-10-14
  • 2011-08-22
  • 2017-02-27
  • 2013-04-05
  • 2015-08-26
  • 2021-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多