C 我如何修复诸如“警告”之类的警告;有符号和无符号之间的比较;?

C 我如何修复诸如“警告”之类的警告;有符号和无符号之间的比较;?,c,C,有人建议我在GCC中使用以下选项,因为它有助于避免许多常见错误。它会打开一系列警告,并将它们变成错误 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;

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

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;
}
我知道我可以解决这个问题的一个方法是关闭警告,但它们并没有让我最终使用这些设置来关闭它们

另一种方法是铸造的东西,但我已经被告知,铸造是不推荐的

另外,我可以将x转换成一个
无符号int

unsigned x;

但当我必须使用这些编译器选项比较有符号值和无符号值时,它并不能解决一般问题。有没有更干净的方法代替铸造

test.c:11: error: comparison between signed and unsigned
您可以将x声明为无符号int,因为size\u t是无符号的

编辑:

如果你不想投,也不想宣布它为未签名,我认为没什么可做的

也许逐位运算是解决这个问题的一种方法,去掉符号位。我不得不说,这很有问题。

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

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

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


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

< P>问题的关键在于,比较有符号和无符号值可承认一些奇怪的情况。例如,考虑在无符号数组长度中发生的情况大于可表示的最大值。签名计数器溢出(剩余“小于”数组大小),然后开始寻址您不想寻址的内存

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


要么严格选择您的类型的无符号性,要么在确定它不适用时排除麻烦,要么摆脱
-Werror
,并将其作为一项策略,通过修复或解释来解决所有警告…

我们在Visual Studio编译中禁止此警告,因为它经常发生,而且几乎没有任何意义当然,并非所有的编码标准都允许这样做


您可以使类型一致(例如,将变量声明为size\u t或unsigned int而不是int),或者您可以强制转换,或者您可以更改编译行。仅此而已。

不管强制转换的两难选择,我仍然建议将逻辑从for循环中分离出来

int range = (int)(sizeof(arr) / sizeof(int));
int x;

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

尽管这使用了您所说的不推荐使用的强制转换,但它清除了强制转换发生的位置。一般来说,我建议不要在for循环声明中塞入大量逻辑。特别是在您使用size\t division的情况下,它(因为它是整数除法)可能有截断答案的可能性。for循环声明应该是干净的,并且不应该生成错误。您的强制转换发生在不同的位置,这意味着如果您想更改创建范围的方式,您不必再费劲地进行for声明。

一个选项是addi国家标志“-Wno符号比较”:

一种解决方法是在这种特殊情况下有选择地禁用该警告。 GCC“-Wsomething”

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. */
readers.c:在函数“buffered_fullread”中:
readers.c:864:11:错误:有符号和无符号整数表达式之间的比较[-Werror=符号比较]
如果(get
[-Werror=sign compare]部分告诉您可以将“Wsign compare”用于“Wsomething”以抑制警告


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

“但是,当我必须使用这些编译器选项比较有符号值和无符号值时,这并不能解决一般问题。有没有比铸造更干净的方法?“我同意这个答案。还要注意,铸造不是被贬低的,但初学者可能会感到沮丧,因为它会导致你养成一些坏习惯。这是最好的答案,但我将C++概念引入到C的问题中。但是我认为你不能像C++那样在循环中声明你的索引变量。@ Adrian McCarthy:见C99,65.5.3.1:…“如果第1条是一个声明,那么它声明的任何标识符的范围就是声明的剩余部分和整个循环,包括其他两个表达式;它是在第一次计算控制表达式之前按照执行顺序到达的。在那里声明变量是C99,在旧的编译器上不受支持。如果您愿意,显然您也可以在将声明保持在函数顶部的同时更改声明。请注意,
sizeof
返回一个
size\t
,必须使用hidden打印出来
int range = (int)(sizeof(arr) / sizeof(int));
int x;

for(x = 0; x < range; x++)
{
    printf("%d\n", arr[x]);
}
// 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
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. */