web-dev-qa-db-ja.com

Cシフト式には符号なしの型がありますか? Splintが右シフトについて警告するのはなぜですか?

次のプログラムの場合:

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

... Splint 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 >> E2の結果はE1右シフトE2ビット位置です。 E1が符号なしの型である場合、またはE1が符号付きの型で非負の値である場合、結果の値はE1を数量で割った商の整数部であり、2累乗E2

このコードの主なターゲットは、主にC90コンパイラを備えた組み込みシステムです。しかし、私は標準に準拠したコードを書くことに興味があります。 restrictが機能するように、GCCとClangをC99モードでテストしています。

私の質問は:

  1. C規格は、ビットシフトの結果の型について何らかの主張をしていますか?
  2. コンパイラはありますか?
  3. そうでない場合、なぜSplintがこの警告を発行するのでしょうか?
17
detly

これはSplintのバグです。 Splintは、e1 << e2のタイプがctype_wider(te1, te2)であると誤って想定しています。正しいタイプはte1だけです。

バグコードはここから始まります&|^などのビット単位の演算子と、<<演算子と>>演算子には同じコードパスを使用します。

実際のバグはそのコードの最後にあります 。これは、これらすべてのビット単位の2項演算子の戻り値の型がctype_wider(te1, te2)であることを前提としています。

私は バグをオープンしました をSplintのGitHub課題追跡に載せて、この質問を参照しています。

17
Roland Illig

いいえ。標準では、ビットシフトのタイプは左オペランドのタイプであり、推奨されています: 6.5.7p3

...結果のタイプは、プロモートされた左オペランドのタイプです。 ...

ツールを混乱させて、通常の算術変換で型を推論する必要があります。これは、ほとんどのバイナリ演算子に適用されますが、<<および>>には適用されません。

_Genericベースのtype assertを挿入し、コンパイラが受け入れる を監視することによって、型がintであることを確認することもできます。それ

int main(void)
{
    int value = 2;
    int result = _Generic(value >> 1U, int: value>>1U); //compiles, the type is int
    return result;
}
17
PSkocik

C99からC17までの規格は次のように述べています。

整数の昇格は、各オペランドで実行されます。結果のタイプは、プロモートされた左オペランドのタイプです。

valueintであるため、昇格は不要であり、「昇格された左オペランド」のタイプはintであり、<<の結果のタイプは同じ。

C89/C90は、「整数」が「整数」に置き換えられたことを除いて、同じことを言っています。

2
hobbs