次のプログラムの場合:
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モードでテストしています。
私の質問は:
これはSplintのバグです。 Splintは、e1 << e2
のタイプがctype_wider(te1, te2)
であると誤って想定しています。正しいタイプはte1
だけです。
バグコードはここから始まります&
、|
、^
などのビット単位の演算子と、<<
演算子と>>
演算子には同じコードパスを使用します。
実際のバグはそのコードの最後にあります 。これは、これらすべてのビット単位の2項演算子の戻り値の型がctype_wider(te1, te2)
であることを前提としています。
私は バグをオープンしました をSplintのGitHub課題追跡に載せて、この質問を参照しています。
いいえ。標準では、ビットシフトのタイプは左オペランドのタイプであり、推奨されています: 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;
}
C99からC17までの規格は次のように述べています。
整数の昇格は、各オペランドで実行されます。結果のタイプは、プロモートされた左オペランドのタイプです。
value
はint
であるため、昇格は不要であり、「昇格された左オペランド」のタイプはint
であり、<<
の結果のタイプは同じ。
C89/C90は、「整数」が「整数」に置き換えられたことを除いて、同じことを言っています。