warning: array subscript 10 is outside array bounds of ‘char[10]’
警告:配列の添字 10 は ‘char[10]’ の配列境界外です。
[-Warray-bounds]
■配列の添え字が一つ多い
(Off-by-one Error)
//gcc -Wall -Wextra -O2
//warning: array subscript 10 is above array bounds of ‘int[10]’
//[-Warray-bounds]
int main(void){
int ary[10]={0};
//ダメ:参照できるのはary[0]~ary[9]まで
return ary[10];
}
配列は ary[10]で宣言したら、
参照できるのはary[0]~ary[9]までです。
ary[10]は参照できません。
ary[10]を参照すると未定義動作となり何が起きるかわかりません。
■<と<=を間違えて一回多く回り過ぎる
//gcc -Wall -Wextra -O
//warning: iteration 10 invokes undefined behavior
//[-Waggressive-loop-optimizations]
int main(void) {
int ary[10];
//ダメ:一回まわりすぎ
for(int i=0;i<=10;i++){
ary[i] = 0;
}
return ary[0];
}
一回多く回りすぎです。
i <= 10 ではなくて
i < 10 です。
バグの多くは境界で発生するので
以上・以下・未満・超えるを使いわけましょう。
■回り切ったカウンタで配列を参照してしまう
//gcc -Wall -Wextra -O2
//warning: array subscript 10 is above array bounds of ‘int[10]’
//[-Warray-bounds]
int main(void){
int ary[10]={0};
int i;
for(i=0;i<10;i++){
ary[i] = 0;
}
//ダメ:回り切ったiは10になってる
return ary[i];
}
forループを回り切ると添字の i は10になっているので
ary[10] の配列範囲外となります。
回り切った時のiを添字に使う時は注意してください。
■添字のチェック間違い
//gcc -Wall -Wextra -O2
//warning: array subscript 4 is above array bounds of ‘int[4]’
//[-Warray-bounds]
#include <stdio.h>
int f(int idx){
int buf[4] = {'A','B','C','D'} ;
if(idx > 4) {//ダメ:idx == 4 を許容する
return 'X' ;
}
return buf[idx] ;
}
int main(void){
printf("%c\n",f(4));
}
添字のチェック条件が誤っているので
idx が4の時も
制御が下へ行ってbuf[4]を参照してしまいます。
if(idx > 4) { ではなくて
if(idx >= 4) {でしょう。
■終端文字¥0の1byte考慮抜けてる
warning: ‘__builtin_memcpy’ writing 5 bytes into a region of size 4 overflows the destination
警告:サイズ4の領域へ5バイト書き込み
[-Wstringop-overflow=]
//warning: ‘__builtin_memcpy’ writing 5 bytes into a region of size 4 overflows the destination
//[-Wstringop-overflow=]
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[4];
strcpy(buf,"1234");
puts(buf);
}
終端文字¥0の考慮が抜けています。
コピー先が
4byteしかないのに
5byteコピーしました。
■if(配列[添字]&&範囲チェック)➡チェックを先に
(style) Array index ‘idx’ is used before limits check.
警告:配列添字を範囲チェック前に使った[(style)arrayIndexThenCheck]
//gcc f60.c -fsanitize=address
#include <stdio.h>
char *f(int idx){
char *buf[4] = {"赤","青","黄",NULL} ;
if(buf[idx] != NULL && (idx < 4)) {
return buf[idx] ;
}
return "無効" ;
}
int main(void){
printf("%s\n",f(0));
printf("%s\n",f(1));
printf("%s\n",f(2));
printf("%s\n",f(3));
printf("%s\n",f(4));
}
if((ary[idx]!=NULL) && (idx<MAX)){は
第一条件(ary[idx]!=NULL) と
第二条件 (idx<MAX)を
同時に評価するのではなく
左から順番に評価します。
このため(ary[idx]を参照してしまった後で
(idx<MAX)のチェックをしても手遅れです。
配列領域外参照が発生する可能性があり、
これは未定義動作なので
プログラムが異常終了するかもしれません。
筆者の環境下では以下のようになりました。
gcc -fsanitize=address⇒異常終了する
gcc -fsanitize=address -O2⇒正常終了する
➡修正後:if(範囲チェック&&配列[添字])
//gcc f60.c -fsanitize=address
#include <stdio.h>
char *f(int idx){
char *buf[4] = {"赤","青","黄",NULL} ;
if((idx < 4) && (buf[idx] != NULL)) {
return buf[idx] ;
}
return "無効" ;
}
int main(void){
printf("%s\n",f(0));
printf("%s\n",f(1));
printf("%s\n",f(2));
printf("%s\n",f(3));
printf("%s\n",f(4));
}
if((idx<MAX) && (ary[idx]!=NULL))と
記述すると
第一条件(idx<MAX) が成立しなければ
第二条件(ary[idx]!=NULL)は評価されないので
配列領域外参照は発生しません。
■&配列[0]+byte数は多分バグ
int main(void){
int buf[1024];
//配列末尾のアドレスを求める間違った計算
int *tail = &buf[0] + (sizeof(int)*1024);
for(int *p = buf; p < tail ;p++){
*p = 0;
}
}
➡&配列[0]+要素数←正しい
int main(void){
int buf[1024];
//配列末尾のアドレスを求める正しい計算
int *tail = &buf[0] + 1024;
for(int *p = buf; p < tail ;p++){
*p = 0;
}
}
参考: