環境
PICマイクロコントローラー用の8ビットCコンパイラーを使用して最初にコンパイルされたCコードを移植しています。符号なしグローバル変数(たとえば、エラーカウンター)がゼロにロールオーバーしないようにするために使用された一般的なイディオムは次のとおりです。
if(~counter) counter++;
ここでのビット演算子はすべてのビットを反転し、ステートメントが真であるのcounter
は、最大値よりも小さい場合のみです。重要なことに、これは変数のサイズに関係なく機能します。
問題
現在、GCCを使用する32ビットARMプロセッサをターゲットにしています。同じコードが異なる結果を生成することに気づきました。私たちが知る限りでは、ビットごとの補数演算は予想とは異なるサイズの値を返すようです。これを再現するには、GCCでコンパイルします。
uint8_t i = 0;
int sz;
sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1
sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4
出力の最初の行で、期待どおりの結果が得られますi
。1バイトです。ただし、のビット単位の補数i
は実際には4バイトであり、これとの比較では期待される結果が得られないため、問題が発生します。たとえば、次の場合(i
適切に初期化されたはどこですかuint8_t
):
if(~i) i++;
i
0xFFから0x00までの「折り返し」が表示されます。この動作は、以前のコンパイラと8ビットPICマイクロコントローラーで意図したとおりに機能していた場合と比較して、GCCで異なります。
次のようにキャストすることでこれを解決できることを認識しています。
if((uint8_t)~i) i++;
または、
if(i < 0xFF) i++;
ただし、どちらの回避策でも、変数のサイズは既知である必要があり、ソフトウェア開発者にとってエラーが発生しやすくなります。これらの種類の上限チェックは、コードベース全体で行われます。そこの変数の複数のサイズがある(例えば、uint16_t
およびunsigned char
など)、そうでなければ作業コードベースでこれらを変更すると、私たちは楽しみにしているものではありません。
質問
問題についての私たちの理解は正しいですか、そしてこのイディオムを使用した各ケースを再訪する必要のない、これを解決するために利用可能なオプションはありますか?私たちの仮定は正しいですか?ビット単位の補数のような演算は、オペランドと同じサイズの結果を返す必要がありますか?プロセッサーのアーキテクチャーによっては、これが壊れるようです。クレイジーな薬を飲んでいるような気がします。Cはこれよりも移植性が少し高いはずです。繰り返しますが、これについての私たちの理解は間違っている可能性があります。
表面的にはこれは大きな問題のようには見えないかもしれませんが、以前は機能していたこのイディオムは何百もの場所で使用されており、費用のかかる変更を進める前にこれを理解したいと思っています。
注:ここには一見似ているが正確には重複していない質問があります。charのビット単位の演算では32ビットの結果が得られます
そこで議論された問題の実際の要点はわかりませんでした。つまり、ビット単位の補数の結果のサイズは、オペレーターに渡されたものとは異なります。