为什么GCC的-Wconversion对char和unsigned char的行为不同?

时间:2017-06-28 06:51:17

标签: c gcc

考虑

U8 foo(U8 x, U8 y) {
    return x % y;
}

如果U8(x和y的类型是char或unsigned char),GCC' -Wconversion的行为会有所不同:

gcc -Wconversion -c test.c -DU8='unsigned char'

(没有警告)

gcc -Wconversion -c test.c -DU8=char
test.c: In function ‘foo’:
test.c:2:14: warning: conversion to ‘char’ from ‘int’ may alter its value [-Wconversion]
     return x % y;
            ~~^~~

from what I understand在两种情况下x,y都经历整数提升(到int或unsigned int),因此在两种情况下它都将从int转换为返回类型(char或unsigned char)。< / p>

为什么会有区别?

额外问题:如果启用了ubsan(-fsanitize = undefined),那么GCC会在两种情况下都发出-Wconversion。

编辑

没有论证x,y经历整数提升然后需要转换为结果类型,因此无需解释。

这里唯一的问题是为什么GCC对不同类型的行为方式不同。答案将涉及对GCC内部的一些见解。

2 个答案:

答案 0 :(得分:4)

TLDR

仅使用有关所涉及类型的信息,gcc应警告这两种情况,因为从int(较大类型)转换为char / unsigned char(较小类型)

使用有关可能值的信息(范围分析)gcc应警告无,因为x % y的结果,即使在提升为int之后,也总是适合与{{ 1}}和x

因此,似乎在第一种情况下y可以声明操作永远不会导致值更改,但由于某种原因不能对第二种情况执行此操作。

作为附注,clang并没有警告任何人。

输入系统

  • 在测试系统(x86-64)上,gcc类型已签名。请注意,它仍然与char不同。

  • signed char由于整数推广规则,在这两种情况下,x % yx都会提升为y。结果int的类型为x % y

  • 如果我们将所有隐式转换都显式化,那么我们得到这个:

    int
  • unsigned char foo1(unsigned char x, unsigned char y) { return (unsigned char)((int) x % (int) y); } char foo2(char x, char y) { return (char)((int) x % (int) y); } intcharunsigned char的隐式转换会使用signed char触发警告:

      

    -Wconversion

         

    警告可能会改变值的隐式转换。这包括   [..]和转换为较小的类型

    确实这两个函数都会导致生成警告:

    -Wconversion

因此,仅使用类型信息我们应该收到两者的警告,因为我们的2个函数具有从char bar1(int a) { return a; // warning: conversion from 'int' to 'char' may change value [-Wconversion] } unsigned char bar2(int a) { return a; // warning: conversion from 'int' to 'unsigned char' may change value [-Wconversion] } int / char的隐式转换,就像unsigned char和{ {1}}。

价值分析

如果我们使用符号bar1,则bar2r = x % yr具有相同的符号。

  • 如果x|r| ∈ [0, |y|)的类型为x,则为y

    unsigned char适合r ∈ [0, CHAR_MAX)。所以不需要警告。

  • 如果runsigned char类型为x

    • y
    • char
    • CHAR_MIN = -CHAR_MAX - 1
    • max(|y|) = CHAR_MAX + 1
    • |r| ∈ [0, max(|y|))

    |r| ∈ [0, CHAR_MAX + 1)符合r ∈ (-CHAR_MAX - 1, CHAR_MAX + 1)所以不需要警告。

所以我认为即使在所有整数提升和隐式转换之后, r的结果总是适合char

您可以查看此godbolt

答案 1 :(得分:1)

正如您所说,x % y涉及将两个操作数隐式类型转换为int(整数提升规则/通常的算术转换)。操作的结果是类型int

-Wconversion关注表达式中签名的隐式更改,因为这些可能是无意的。当您在有符号和无符号类型之间进行转换而没有显式强制转换时,它会发出警告。当从较大类型(有符号或无符号)隐式转换为较小类型时,它也显然会警告溢出的潜在问题。

char类型具有实现定义的签名,可以是无符号签名或签名.GCC喜欢在我见过的所有实现中签名。)

intchar的隐式转换可能会导致char溢出。

我们可以通过编写return (char)(x % y);来使编译器静音。这只会隐藏潜在的bug。您必须在代码中确保在添加此类显式转换之前永远不会发生溢出。