【问题标题】:How can I fix warnings like: "comparison between signed and unsigned"?如何修复诸如“签名和未签名之间的比较”之类的警告?
【发布时间】:2009-05-13 19:31:05
【问题描述】:

有人建议我对 GCC 使用以下选项,因为它有助于避免许多常见错误。它会打开一堆警告,-Werror 会将它们变成错误。

gcc -pedantic -W -Wall -Wextra -Wshadow -Wstrict-overflow=5 -Wwrite-strings -std=c99 -Werror

给定以下测试代码:

#include <stdio.h>

int main(void)
{
    int arr[8]={0,10,20,30,40,50,60,70};
    int x;

    printf("sizeof(arr): %d\n", sizeof(arr));
    printf("sizeof(int): %d\n", sizeof(int));

    for(x = 0; x < sizeof(arr)/sizeof(int); x++)
    {
        printf("%d\n",arr[x]);
    }

    return 0;
}

我明白了:

test.c:11: error: comparison between signed and unsigned

我知道解决此问题的一种方法是关闭警告,但它们并没有让我最终使用这些设置将其关闭。

另一种方法是强制转换,但有人告诉我不推荐使用强制转换。

另外,我可以将 x 变成 unsigned int

unsigned x;

但是当我必须使用这些编译器选项将有符号值与无符号值进行比较时,它并不能解决一般问题。有没有比强制转换更清洁的方法?

【问题讨论】:

  • 注意sizeof返回一个size_t,必须使用%zu打印出来

标签: c


【解决方案1】:

替换

int x;
/* ... */
for(x=0;x<sizeof(arr) / sizeof(int);x++)

通过

for(size_t x=0;x<sizeof(arr) / sizeof(int);x++)

但是当我必须使用这些编译器选项将有符号值与无符号值进行比较时,它并不能解决一般问题。有没有更清洁的铸造方式?

在这种情况下,请尝试确定带符号的数字是否可能具有会导致溢出的值。如果没有,您可以忽略警告。否则,强制转换为无符号类型(如果它与有符号组件大小相同或更大)就足够了。

【讨论】:

  • 我同意这个答案。另请注意,强制转换并没有被弃用,但对于初学者来说可能不鼓励,因为它会导致你养成一些坏习惯。
  • 这是最好的答案,但我在一个关于 C 的问题中引入了一个 C++ 概念。我有一段时间没有使用严格的 C,但我认为你不能声明你的索引变量像在 C++ 中一样在循环内部。
  • @Adrian McCarthy: See C99, 6.8.5.3.1: [...] “如果第 1 条是一个声明,它声明的任何标识符的范围是声明的其余部分和整个循环,包括其他两个表达式;它在控制表达式的第一次评估之前按执行顺序到达。"
  • 声明该变量是 C99,旧编译器不支持。如果你关心的话,显然你可以同样很好地更改声明,同时仍将其保留在函数的顶部。
【解决方案2】:

这实际上取决于数据类型。可以通过将值隐式转换为包含有符号和无符号类型中所有可用值的超集的类型来避免这种情况。例如,您可以使用 64 位有符号值来比较无符号 32 位和有符号 32 位值。

但是,这是一个极端情况,您需要执行诸如验证类型大小之类的操作。您最好的解决方案是对两个操作数使用相同的类型。

如果您必须强制转换,请考虑您可能会导致溢出,并考虑这对您的应用程序是否重要。

【讨论】:

    【解决方案3】:

    问题的关键在于比较有符号和无符号值会出现一些奇怪的情况。例如,考虑在无符号数组长度中发生的情况大于可以由有符号整数表示的最大值。有符号的计数器溢出(剩余“小于”数组大小),并且您开始寻址您不打算的内存......

    编译器会生成警告以确保您正在考虑它们。使用 -Werror 会将该警告升级为错误并停止编译。

    要么严格选择类型的签名,要么在您确定它不适用时摆脱麻烦,或者摆脱-Werror 并使其成为解决所有警告的策略,或者一个解释...

    【讨论】:

      【解决方案4】:

      一种解决方法是在这种特殊情况下选择性地禁用该警告。 GCC has pragma diagnostic ignored "-Wsomething"

      // Disable a warning for a block of code:
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wmissing-prototypes"
      // ... Some code where the specified warning should be suppressed ...
      #pragma GCC diagnostic pop
      

      最新版本的 GCC(我实际上不确定从什么时候开始,但 4.8.x 应该支持它)显示相应的 -Wsomething 选项。这很重要,因为大多数警告选项都没有明确设置,而是与 -Wall 等选项一起设置。 错误消息如下所示:

      readers.c: In function ‘buffered_fullread’:
      readers.c:864:11: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
          if(got < sizeof(readbuf)) /* That naturally catches got == 0, too. */
      

      [-Werror=sign-compare] 部分告诉您您可以对“Wsomething”使用“Wsign-compare”来抑制警告。

      当然,你应该只在合适的地方这样做(它不太有助于可读性),例如何时需要编译器警告的行为(或者,如果您可能不会在代码库中引入更大的更改)。

      【讨论】:

        【解决方案5】:

        一个选项是附加标志“-Wno-sign-compare”:)

        【讨论】:

        • 隐藏此警告可以隐藏合法的错误。
        • 因此是笑脸!
        • 笑脸几乎是最糟糕的方式来表示如此重要的事情,尤其是因为正在学习如何编程的人会找到这些答案。与其害羞,不如尝试在您的答案中添加有用的内容,以解释为什么这是一个在技术上可行的解决方案,即使它不应该被使用。
        【解决方案6】:
        test.c:11: error: comparison between signed and unsigned
        

        您可以将 x 声明为无符号整数,因为 size_t 是无符号的

        编辑:

        如果你不想强制转换,也不想将其声明为未签名,我认为没有什么可做的。

        也许按位运算是解决它的一种方法,删除符号位。我不得不说,IMO 非常值得怀疑。

        【讨论】:

        • "但是,当我必须使用这些编译器选项将有符号值与无符号值进行比较时,它并不能解决一般问题。有没有更简洁的强制转换方法?"
        【解决方案7】:

        我们在 Visual Studio 编译中禁止此警告,因为它经常发生并且几乎从不意味着任何重要的事情。当然,并非所有编码标准都允许这样做。

        您可以使类型一致(例如,将变量声明为 size_t 或 unsigned int 而不是 int),或者您可以强制转换,或者您可以更改您的编译行。就是这样。

        【讨论】:

          【解决方案8】:

          不管铸造弃用困境如何,我仍然建议将逻辑从 for 循环中分离出来。

          int range = (int)(sizeof(arr) / sizeof(int));
          int x;
          
          for(x = 0; x < range; x++)
          {
              printf("%d\n", arr[x]);
          }
          

          虽然这使用了您所说的已弃用的强制转换,但它清除了强制转换发生的位置。一般来说,我建议不要将大量逻辑塞进你的 for 循环声明中。特别是在您使用 size_t 除法的情况下,它(因为它是整数除法)可能会截断答案。 for 循环声明应该是干净的,并且应该不会产生错误。您的转换发生在不同的位置,这意味着如果您想更改创建范围的方式,您不必为使 for 声明更长而烦恼。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-09-21
            • 1970-01-01
            • 2011-11-18
            • 1970-01-01
            • 2010-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多