18

对于以下程序:

int main(void)
{
    int value = 2;
    int result = value >> 1U;
    return result;
}

...夹板 3.1.2 给出警告:

splint_test.c: (in function main)
splint_test.c:4:18: Variable result initialized to type unsigned int, expects
                       int: value >> 1U
  To ignore signs in type comparisons use +ignoresigns

Splint 似乎声称有符号整数右移的表达式具有无符号整数的类型。但是,我在 ANSI C90 标准中只能找到:

结果E1 >> E2E1右移的E2位位置。如果E1有无符号类型或E1有符号类型和非负值,则结果的值是 的商E1除以数量的整数部分,2 的幂E2

此代码的主要目标是具有主要 C90 编译器的嵌入式系统。但是,我对编写符合标准的代码很感兴趣。我一直在 C99 模式下对 GCC 和 Clang 进行测试,这样就restrict可以了。

我的问题是:

  1. C 标准是否对位移结果的类型做出任何声明?
  2. 编译器吗?
  3. 如果不是,为什么 Splint 会发出此警告?
4

3 回答 3

18

这是 Splint 中的一个错误。Splint 错误地假设 的类型e1 << e2ctype_wider(te1, te2)。正确的类型是 just te1

错误代码从这里开始,对按位运算符(如&,|^, 以及<<and运算符)使用相同的代码路径>>

实际的错误在该代码的末尾,它假定所有这些按位二进制运算符的返回类型是ctype_wider(te1, te2).

我在 Splint 的 GitHub 问题跟踪器上打开了一个错误,引用了这个问题。


2021 年 2 月更新:

NetBSD 的 lint 说,在传统 C 中,按位移位运算符将通常的算术转换应用于其操作数:

    /* Make sure both operands are of the same type */
    if (mp->m_balance_operands || (tflag && (op == SHL || op == SHR)))
        balance(op, &ln, &rn);

代码说明:

这是 C90 的一个变化。因此,Splint 代码对于传统 C 来说可能是正确的,也许它只是没有针对 C90 或 C99 进行更新。

于 2019-04-14T07:56:45.053 回答
17

不,标准说移位的类型是左操作数的类型,提升: 6.5.7p3

...结果的类型是提升的左操作数的类型。...

您的工具一定会感到困惑,通过通常的算术转换来推断类型,这适用于大多数二元运算符,但不适用于<<and >>

您还可以通过插入_Generic基于- 的类型 assert并观察编译器接受它来验证类型是否为 int :

int main(void)
{
    int value = 2;
    int result = _Generic(value >> 1U, int: value>>1U); //compiles, the type is int
    return result;
}
于 2019-04-14T07:43:47.633 回答
2

C99 到 C17 标准说:

对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。

由于valueis anint它不需要提升,并且“提升的左操作数”的类型是int,并且结果的类型<<是相同的。

C89/C90 的说法相同,只是将“整数”一词替换为“整数”。

于 2019-04-14T07:47:17.037 回答