用“U”指定无符号整数有什么意义?

时间:2017-05-02 14:48:16

标签: c unsigned convention

我一直都是,只要我记得并无处不在,就这样做了:

for (unsigned int i = 0U; i < 10U; ++i)
{
    // ...
}

换句话说,我在无符号整数上使用U说明符。现在看了很长时间,我想知道为什么我这样做。除了表示意图之外,我想不出为什么它在像这样的琐碎代码中有用的原因?

有没有一个有效的编程原因我为什么要继续这个约定,还是多余的?

5 个答案:

答案 0 :(得分:9)

首先,我会说明你可能会看到什么,但是你的问题留有余地,所以我确保我们都在同一页上。

无符号整数与常规整数之间存在明显差异:它们的范围差异(对于int32为-2,147,483,648到2,147,483,647,对于uint32为0到4,294,967,295)。当您使用正确的bitshift >>运算符时,在最高位上放置的位有所不同。

当您需要告诉编译器将常量值视为uint而不是常规int时,后缀很重要。如果常量超出常规int的范围但在uint的范围内,这可能很重要。如果您不使用U后缀,编译器可能会发出警告或错误。

除此之外,Daniel Daranas在评论中提到了唯一发生的事情:如果你不使用U后缀,你将隐式地将常量从常规int转换为UINT。这对编译器来说是一点点额外的努力,但是没有运行时差异。

你应该关心吗?这是我的答案,(粗体,对于那些只想快速回答的人):确实没有理由将常量声明为10U0U 大多数情况下,您处于uint和int的公共范围内,因此无论是uint还是int,该常量的值看起来都完全相同。编译器将立即获取const int表达式并将其转换为const uint。

那就是说,这是我能给你的唯一一个论点:语义。使代码在语义上连贯起来很不错。在这种情况下,如果你的变量是一个uint,那么将该值设置为常量int是没有意义的。如果你有一个uint变量,它显然是有原因的,它应该只适用于uint值。

但是,这是一个非常弱的论点,特别是因为作为读者,我们接受uint常量通常看起来像int常量。我喜欢一致性,但使用&#39; U&#39;没有任何好处。

答案 1 :(得分:4)

我在使用定义时经常会看到这一点,以避免签名/未签名的不匹配警告。我为使用不同工具链的几个处理器构建了代码库,其中一些处理器非常严格。

例如,删除MAX_PRINT_WIDTH定义中的'u':

#define MAX_PRINT_WIDTH          (384u)
#define IMAGE_HEIGHT             (480u)   // 240 * 2
#define IMAGE_WIDTH              (320u)   // 160 * 2 double density

发出以下警告: &#34; .. \ Application \ Devices \ MartelPrinter \ mtl_print_screen.c&#34;,第106行:cc1123:{D}警告:     无符号类型与有符号类型的比较

for ( x = 1; (x < IMAGE_WIDTH) && (index <= MAX_PRINT_WIDTH); x++ )

你可能也会看到'f'代表浮动与双倍。

答案 2 :(得分:4)

我从评论中提取了这句话,因为它是一个广为人知的不正确的陈述,也因为它提供了一些明确标记无符号常数的原因,因为这是一个好习惯。

  

...当我认为溢出可能是一个问题时,似乎只保留它才有用吗?但话说回来,我没有通过某种方式指明无符号来减轻这种情况......

现在,让我们考虑一些代码:

int something = get_the_value();
// Compute how many 8s are necessary to reach something
unsigned count = (something + 7) / 8;

那么,unsigned是否会减少潜在的溢出?完全没有。

假设something原来是INT_MAX(或接近该值)。假设一台32位机器,我们可能期望count为2 29 或268,435,456。 But it's not.

告诉编译器计算的结果应该unsigned对计算的输入没有任何影响。由于somethingint,而7intsomething + 7将被计算为int,并且会溢出。然后,溢出的值将除以8(也使用带符号的算术),无论有什么用,都将转换为unsigned并分配给count

使用GCC,算术实际上以2s补码执行,因此溢出将是一个非常大的负数;在划分之后,它将是一个不那么大的负数,并且最终是一个较大的无符号数,比我们期望的数字大得多。

假设我们已经指定了7U(也许8U也是如此,以保持一致)。 Now it works.。它的工作原理是因为现在something + 7U是用无符号算术计算的,它不会溢出(甚至包裹。)

当然,这个错误(和成千上万的人一样)可能会被忽视很长一段时间,在最糟糕的时刻爆炸(也许是字面意思)......

(显然,使something无符号缓解问题。这里非常明显。但定义可能距离使用还有很长的路要走。)

答案 3 :(得分:1)

你应该为简单的代码 1 执行此操作的一个原因是后缀强制文字上的类型,并且类型对于产生正确的结果可能非常重要。

考虑一下(有些愚蠢)代码:

and loginname!='abc123'

根据他们的论证类型,不难想象这些功能实际上是在做一些有意义的事情。如果我省略了后缀,那么泛型选择会产生一个非常简单的调用的错误结果。

1 但也许不是你帖子中的特定代码。

答案 4 :(得分:-1)

在这种情况下,它完全没用。

在其他情况下,后缀可能很有用。例如:

#include <stdio.h>

int
main()
{
  printf("%zu\n", sizeof(123));
  printf("%zu\n", sizeof(123LL));
  return 0;
}

在我的系统上,它将打印4然后8。

但回到你的代码,是的,它使你的代码更加明确,仅此而已。