warning: left shift count >= width of type
警告:左シフト数 >= 型幅
[-Wshift-count-overflow]
■シフトし過ぎ(定数で)
#include <stdio.h>
#include <stdint.h>
//32bit定数シフト
int main(void){
uint32_t x = 0x12345678 << 32;//シフトし過ぎ
printf("x = %x\n",x);
uint32_t y = 0x12345678 ;
y <<= 32 ; //シフトし過ぎ
printf("y = %x\n",y);
}
オペランドのビット数以上のシフトはC言語の規格で未定義です。32bit変数を32bit以上左シフトして0になる事を期待して
期待した結果を得れるかも知れないし、得れないかもしれません。
未定義動作となるのでどのように動くか誰もわかりません。
●修正例
#include <stdio.h>
#include <stdint.h>
//32bit定数シフト
int main(void){
//(1) 64bit変数で宣言する
//(2) シフト対象の定数にUL接尾子を付与
//(3) 書式を%xから%lxへ変更
uint64_t x = 0x12345678UL << 32;
printf("x = %lx\n",x);
uint64_t y = 0x12345678 ;
y <<= 32 ;
printf("y = %lx\n",y);
}
シフト演算子の左辺を32bit以上シフトしたい場合は
int型ではなくてlong型等にする必要があります。
■シフトし過ぎ(変数で)
warning: shift by count (‘32’) >= precision of type (‘32’)
警告:カウント数 (’32’) >= 型幅 (’32’)
[-Wanalyzer-shift-count-overflow]
#include <stdio.h>
#include <stdint.h>
int main(void)
{
uint32_t x = 0x12345678 ;
int shift = 32;
//変数でシフト
printf("%x\n",x<<shift);
}
gccの新しいオプション gcc -fanalyzer を使用すると、シフト演算子の右辺が変数でも問題を検出してくれます。
オペランドのビット数以上シフトしないようにチェック処理を入れてください。
■シフトし過ぎ(関数引数で)
#include <stdio.h>
#include <stdint.h>
//関数跨ぎの変数引数シフト
void f(uint32_t x,int shift){
printf("%x\n",x<<shift);
}
int main(void){
uint32_t x = 0x12345678 ;
f(x,32);
}
gcc -fanalyzer を使用すると、
シフト演算子の右辺が引数でも問題を検出してくれます。
■8bit変数を24bitシフトすると?
//gcc -Wall -Wextra -fsanitize=undefined
#include <stdint.h>
#include <stdio.h>
int main(void){
uint8_t x80_8bit = 0x80 ;
uint32_t x80_32bit = 0x80 ;
// 符号無しのxを24bitシフトすると?
if((x80_8bit << 24) < 0)//未定義動作
puts("-");
else
puts("+");
if((x80_32bit << 24) < 0)
puts("-");
else
puts("+");
}
筆者の環境下では
gcc/clang/Visual Studioの全部が以下のように表示されました。
同じ0x80をシフトしているのに
uint8_t型とuint32_t型では結果が異なります。
gcc -Wall -Wextra -fsanitize=undefined で
コンパイル&実行してみると、
以下の走行時エラーが発生します。
runtime error: left shift of 128 by 24 places cannot be represented in type 'int'
8bit幅の変数を24bitシフトして
INT_MAXを超えると
未定義動作となるので注意しましょう。
参考:
INT34-C. 負のビット数のシフトやオペランドのビット数以上のシフトを行わない
https://www.jpcert.or.jp/sc-rules/c-int34-c.html