5
{
  char *a, *b;

  printf("%lx\n",(b-a));
}

通常有效,事实上,我无法想象它会在 32 位或 64 位机器上发出警告或失败。但是,对于 ANSI C 和尺寸意识来说,这是正确的做法吗?我希望这段代码可以在所有可能的平台上工作,包括非 Unix 和嵌入式系统。

4

5 回答 5

22

b - a是一个ptrdiff_t,您可以用%td您的printf格式打印。从规范部分 6.5.6加法运算符

当两个指针相减时,都应指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。结果的大小由实现定义,其类型(有符号整数类型)ptrdiff_t在标头中定义<stddef.h>

对于printf和相关函数,第 7.19.6 节格式化输入/输出函数

t指定后面的d, i, o, u, x, 或X转换说明符应用于一个ptrdiff_t或相应的无符号整数类型参数;或者后面的n转换说明符适用于指向ptrdiff_t参数的指针。

我在规范中又戳了一下,它似乎表明两个指针的差异甚至可能不适合 a ptrdiff_t,在这种情况下,行为是未定义的:

J.2 未定义的行为
- 减去两个指针的结果不能在类型ptrdiff_t(6.5.6) 的对象中表示。

虽然我无法想象任何可能出现的实现。我想你可以检查PTRDIFF_MIN并确定PTRDIFF_MAX<stdint.h>

于 2009-10-29T18:33:45.727 回答
12

的结果仅在和都指向同一个字符数组的元素b - a时才定义。此要求也可以解释为并指向属于同一对象的字节,因为每个对象都可以重新解释为 char 数组。abab

否则,结果是未定义的。即尝试减去此类指针会导致未定义的行为。

定义结果时,它具有ptrdiff_t类型。ptrdiff_t是一个 typedef 名称,隐藏在 typedef 名称后面的类型是实现定义的。已知该类型是已签名的。

另请注意,C 语言不保证ptrdiff_t足够大以保存任何减法的结果,即使指针指向同一数组的元素。如果指针相距太远以至于类型ptrdiff_t无法容纳结果,则行为未定义。

即使在 C99 中也没有特定的printf格式说明符ptrdiff_t,因此最好将其转换为足够大的有符号整数类型并为该类型使用格式说明符

printf("%ld\n", (long) (b - a));

更正: C99 确实有一个长度修饰符ptrdiff_t。在 C99 中打印结果的正确方法是

printf("%td\n", b - a);

请注意,这t是一个长度修饰符。它可以与do、或转换说明符结合使用u,具体取决于您想要获得的输出格式。在 C89/90 中,您仍然必须坚持使用足够大的有符号类型。xX

PS你说你无法想象它在 32 位或 64 位机器上会失败。事实上,很容易想象(或实际做到)失败。您ptrdiff_t在 32 位机器上看到的通常是 32 位类型。由于它是有符号类型,因此它只有 31 位可用于表示值的大小。如果取两个相距较远的指针(即需要32位来表示“距离”),结果b - a会溢出,没有意义。为了防止这种故障,您需要在 32 位机器上至少 33 位签名,在 64 位机器上ptrdiff_t至少需要 65 位签名。ptrdiff_t实现通常不会这样做,它们只是使用标准中的“权限”来在溢出时产生未定义的行为。

于 2009-10-29T18:35:13.080 回答
5

ptrdiff_t。来自man stddef.h

ptrdiff_t
              两个指针相减结果的有符号整数类型。

用 打印%td

于 2009-10-29T18:33:23.623 回答
2

由于您尚未初始化变量ab,因此代码给出了未定义的行为。但除此之外, ba的类型是ptrdiff_t,它大到足以包含结果。如果你有足够现代的 C,你可以用%tx打印它。

如果您不想使用%tx,您应该转换您的结果,使其真正匹配(而不仅仅是偶然)您的格式说明符:

printf("%lx", (unsigned long)(a-b));

例如,一个系统可能具有 32 位地址空间和 32 位 ptrdiff_t,但长度为 64 位,这并非不可想象,然后您的 printf 将失败。

于 2009-10-29T18:32:22.867 回答
2

ba 的类型是ptrdiff_t

于 2009-10-29T18:36:07.393 回答