【问题标题】:A warning - comparison between signed and unsigned integer expressions警告 - 有符号和无符号整数表达式之间的比较
【发布时间】:2011-04-09 07:43:23
【问题描述】:

我目前正在研究Accelerated C++,并在练习 2-3 中遇到了一个问题。

程序概览 - 程序基本上采用名称,然后在星号框架内显示问候语 - 即 Hello !被 * 包围。

练习 - 在示例程序中,作者使用const int 来确定问候语和星号之间的填充(空格)。然后,作为练习的一部分,他们要求读者询问用户他们希望填充有多大。

这一切似乎很容易,我继续向用户询问两个整数 (int) 并存储它们并更改程序以使用这些整数,删除作者使用的整数,尽管我得到以下内容警告;

Exercise2-3.cpp:46:警告:有符号和无符号整数表达式之间的比较

经过一些研究,这似乎是因为代码尝试将上述整数之一 (int) 与string::size_type 进行比较,这很好。但我想知道 - 这是否意味着我应该将其中一个整数更改为 unsigned int?明确说明我的整数是有符号还是无符号重要吗?

 cout << "Please enter the size of the frame between top and bottom you would like ";
 int padtopbottom;
 cin >> padtopbottom;

 cout << "Please enter size of the frame from each side you would like: ";
 unsigned int padsides; 
 cin >> padsides;

 string::size_type c = 0; // definition of c in the program
 if (r == padtopbottom + 1 && c == padsides + 1) { // where the error occurs

上面是相关的代码,c 的类型是string::size_type,因为我们不知道问候可能会持续多久 - 但是为什么我现在遇到这个问题,而作者的代码没有得到使用const int时的问题?另外——对于可能已经完成Accelerated C++的任何人——这本书后面会解释吗?

我在通过 Geany 使用 g++ 的 Linux Mint 上,如果这有助于或有所作为(正如我读到的那样,在确定 string::size_type 是什么时它可以)。

【问题讨论】:

  • 不会有人认为您无论如何都想要无符号整数吗?我想不出为什么顶部和底部应该是负数的合乎逻辑的原因
  • 这是真的,我在上面的帖子中也提到了这一点,但我仍然不明白为什么作者的示例程序中使用 const int 时没有出现这个问题?我相信我会在书中谈到这一点,但我不禁好奇。
  • 报废 - 显然在那种情况下它没有发出警告,因为 int 总是 1...哎呀。
  • 一般来说,增加范围不值得为计数使用unsigned 整数类型的麻烦。无符号数也有保证的环绕行为,使它们的效率略低。
  • 作者可能看到了同样的警告,只是忽略了它。不要以为书籍的作者比普通程序员知识渊博或细心。

标签: c++ unsigned-integer


【解决方案1】:

如果将变量与大小进行比较,通常最好将变量声明为unsignedsize_t,以避免此问题。尽可能使用您要比较的确切类型(例如,在与 std::string 的长度进行比较时使用 std::string::size_type)。

编译器在比较有符号和无符号类型时会发出警告,因为有符号和无符号整数的范围不同,当它们相互比较时,结果可能会令人惊讶。如果您必须进行这样的比较,您应该将其中一个值显式转换为与另一个兼容的类型,也许在检查以确保转换有效之后。例如:

unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();

if (i >= 0)
{
    // i is nonnegative, so it is safe to cast to unsigned value
    if ((unsigned)i >= u)
        iIsGreaterThanOrEqualToU();
    else
        iIsLessThanU();
}
else
{
    iIsNegative();
}

【讨论】:

  • 我知道当前的 C 标准有时要求负有符号值的比较大于无符号值,但是否应将发生这种情况的任何情况视为已弃用?我希望看到标准演变为至少 允许 编译器产生算术正确的行为(这意味着如果有符号值是负数,它比较小,如果无符号值超过最大值有符号的类型,它比较大)。在没有显式类型转换的情况下,编译器需要产生愚蠢的行为,这似乎很奇怪。
  • @supercat:由于整数比较编译为单个机器指令,并且任何测试或边缘情况处理都需要多个机器指令,因此您的建议不太可能作为 C 功能添加.. . 它当然不可能是默认行为,因为即使程序员知道没有必要,它也会不必要地降低性能。
  • @BlakeMiller:想要比较有符号和无符号值的代码,就好像两者都是无符号的一样,可以强制转换并“全速”运行。否则,在许多情况下,区别在于采用两条指令与采用三条指令的比较和跳转之间的差异,这比手动处理各种情况的代码便宜。
  • @BlakeMiller:(我说二对三的原因是大多数比较两个数字的代码将使用一条指令来执行比较并根据它们设置标志;在许多情况下,编译器可以安排以便在比较之前,“符号”标志将保存其中一个操作数的高位,因此比较之前的单个条件跳转就足以确保正确的语义)。请注意,由于有多种方法可以实现正确的语义,因此编译器可以选择最便宜的方法。为正确的语义编写 C 代码会更难。
  • 只是为了证明“结果可能令人惊讶”,下面的程序(在顶部插入 #include &lt;cstdio&gt; 之后......我使用的是 g++ 4.4.7),将打印“true ",说明 (signed) -1 大于 (unsigned) 12 是真的:int main(int, char**) { int x = -1; unsigned int y = 12; printf("x &gt; y: %s\n", x &gt; y ? "true":"false"); return 0; }
【解决方案2】:

昨天我在解决加速 C++ 中的问题 2-3 时遇到了完全相同的问题。关键是将您将要比较的所有变量(使用布尔运算符)更改为兼容的类型。在这种情况下,这意味着string::size_type(或unsigned int,但由于此示例使用的是前者,即使两者在技术上兼容,我也会坚持使用)。

请注意,正如您正确指出的那样,在他们的原始代码中,他们对 c 计数器(本书第 2.5 节中的第 30 页)正是这样做的。

使这个示例更加复杂的是,不同的填充变量(padsides 和 padtopbottom)以及所有计数器都必须更改为string::size_type

以您的示例为例,您发布的代码最终将如下所示:

cout << "Please enter the size of the frame between top and bottom";
string::size_type padtopbottom;
cin >> padtopbottom;

cout << "Please enter size of the frame from each side you would like: ";
string::size_type padsides; 
cin >> padsides;

string::size_type c = 0; // definition of c in the program

if (r == padtopbottom + 1 && c == padsides + 1) { // where the error no longer occurs

请注意,在前面的条件中,如果您没有在 for 循环中将变量 r 初始化为 string::size_type,则会收到错误消息。所以你需要使用类似的东西来初始化for循环:

    for (string::size_type r=0; r!=rows; ++r)   //If r and rows are string::size_type, no error!

因此,基本上,一旦您将string::size_type 变量引入到组合中,任何时候您想对该项目执行布尔运算,所有操作数都必须具有兼容的类型才能编译而不会出现警告。

【讨论】:

    【解决方案3】:

    有符号整数和无符号整数的重要区别 是对最后一位的解释。最后一点 在有符号类型中表示数字的符号,含义: 例如:

    0001 是 1 有符号和无符号 1001 是 -1 有符号和 9 无符号

    (为了解释清楚,我避免了整个补码问题! 这不是整数在内存中的确切表示方式!)

    您可以想象,如果您进行比较,就会有所不同 -1 或 +9。在很多情况下,程序员只是太懒了 将计数整数声明为无符号(膨胀 for 循环头 f.i.) 这通常不是问题,因为使用整数你必须数到 2^31 直到你的标志位咬你。这就是为什么它只是一个警告。 因为我们懒得写 'unsigned' 而不是 'int'。

    【讨论】:

    • 啊,我明白了 - 我现在已将计数 int 更改为无符号数。这被认为是好的做法,甚至是坏做法吗? :)
    • 如果您投反对票,请简要说明原因。哪怕只有一个字。我看不出我的回答有什么问题。您可以帮助我解决哪个问题。
    • @Tim:“unsigned”是“unsigned int”的同义词。您应该使用 unsigned int 或 stl 标准计数/迭代变量类型 std::size_t (这也是同义词)。在“迭代元素 0 到 n”的所有情况下,最好使用 unsigned。它提高了清晰度并消除了警告,因此它是赢家;-)
    • 有符号整数的内部表示依赖于编译器(即机器)。由于某些问题(+/- 零是其中之一),您的符号位符号并未被广泛使用。大多数机器使用二进制补码概念来表示负数。优点是正常(无符号)算术也可以在没有任何更改的情况下使用。 2 的补码概念中的 -1 将是 1111 btw。
    • @AndreasT:虽然“为了清楚起见避免整个补码问题”是可以理解的,但您可以使用与 2 的补码兼容的示例,几乎所有平台都使用这种表示。 1001 for -1 是一个糟糕的选择,更好的选择是 "1111 equals -1 signed and 15 unsigned"
    【解决方案4】:

    在极端范围内,unsigned int 可以变得比 int 大。
    因此,编译器会生成警告。如果您确定这不是问题,请随意将类型强制转换为相同的类型,这样警告就会消失(使用 C++ 强制转换,以便它们易于发现)。

    或者,使变量具有相同的类型以阻止编译器抱怨。
    我的意思是,是否有可能有一个负填充?如果是这样,请将其保留为 int。否则,您可能应该使用 unsigned int 并让流捕获用户输入负数的情况。

    【讨论】:

      【解决方案5】:

      主要问题是底层硬件 CPU 仅具有比较两个有符号值或比较两个无符号值的指令。如果您向无符号比较指令传递一个有符号的负值,它将把它视为一个大的正数。因此,-1,即所有位都打开(二进制补码)的位模式,成为相同位数的最大无符号值。

      8 位:-1 有符号位与 255 位无符号位相同 16 位:-1 有符号位与 65535 无符号位相同 等等

      所以,如果你有以下代码:

      int fd;
      fd = open( .... );
      
      int cnt;
      SomeType buf;
      
      cnt = read( fd, &buf, sizeof(buf) );
      
      if( cnt < sizeof(buf) ) {
          perror("read error");
      }
      

      你会发现如果 read(2) 调用由于文件描述符变得无效(或其他错误)而失败,该 cnt 将被设置为 -1。与无符号值 sizeof(buf) 比较时,if() 语句将是 false,因为 0xffffffff 不小于 sizeof() 一些(合理的,不是炮制为最大大小的)数据结构。

      因此,您必须编写上述 if,以删除已签名/未签名的警告:

      if( cnt < 0 || (size_t)cnt < sizeof(buf) ) {
          perror("read error");
      }
      

      这只是大声说出问题。

      1.  Introduction of size_t and other datatypes was crafted to mostly work, 
          not engineered, with language changes, to be explicitly robust and 
          fool proof.
      2.  Overall, C/C++ data types should just be signed, as Java correctly
          implemented.
      

      如果您的值太大而无法找到有效的有符号值类型,则说明您使用的处理器太小或您选择的语言中的值太大。如果像金钱一样,每个数字都很重要,那么在大多数语言中都有一些系统可以为您提供无限的精度。 C/C++ 只是不能很好地做到这一点,并且您必须非常明确地了解与此处许多其他答案中提到的类型有关的所有内容。

      【讨论】:

        【解决方案6】:

        或使用this header library 并写:

        // |notEqaul|less|lessEqual|greater|greaterEqual
        if(sweet::equal(valueA,valueB))
        

        并且不关心有符号/无符号或不同的大小

        【讨论】:

          猜你喜欢
          • 2013-11-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-15
          • 2014-12-15
          • 1970-01-01
          • 2015-09-14
          • 1970-01-01
          相关资源
          最近更新 更多