warning: ‘return’ will never be executed
[-Wunreachable-code-return]
■デッドコードは2種類ある
2大コーディングルールで微妙に違います。
●CERT-Cルールでは
到達不能コード(unreachable code)の事。
実行される事が無い。
●MISRA-Cルールでは
冗長コード(redundant code)の事。
実行する意味が無い。
この記事は、
C言語におけるデッドコードの種類について説明しています。
■到達不能コード(unreachable code)の例
➡break直後のreturn
#include <stdio.h>
typedef enum {R,G,B} color;
char *RGB(color x){
switch(x){
case R:
return "赤";
break;
case G:
return "緑";
break;
case B:
break;
return "青";//バグ
}
return "予期しない値";
}
int main(void){
printf("%s\n",RGB(B));
}
breakとreturnの順番を間違えているので
returnには制御が来ません。
➡if文が矛盾しているので制御が来ない
(warning): Opposite inner ‘if’ condition leads to a dead code block.
警告:矛盾する逆条件はデッドコードになる[(warning)oppositeInnerCondition]
#include <stdio.h>
int f(int x)
{
if(x != 0) {
if(x == 0){//NG
return 1;
}
}
return 0;
}
4行目と5行目が矛盾するので
6行目がデッドコードブロックになります。
➡無条件breakでfor文更新に制御が来ない
warning: loop will run at most once
警告:ループは最大でも一度しか実行されない
[-Wunreachable-code-loop-increment]
#include <stdio.h>
int main(void)
{
for(int i = 0 ; i < 10 ; i++ ) {
printf("%d\n",i);
//回りたい?、止まりたい? デバッグの残骸?
break ;
}
}
7行目にbreakがあるので、for文第三式 i++ が実行されません。
デバッグ文を書いたり消したりして
残骸として残った可能性がありますが、
保守者には break の意味がわかりません。
➡最初のcaseの上に色々書いても無駄
warning: statement will never be executed
警告:宣言時の代入文が実行されることはない
[-Wswitch-unreachable]
#include <stdio.h>
int main(int argc,char *argv[]){
switch(argc){
int i = 1234 ; //変数宣言は有効だが実行はされない
int j; //変数宣言は有効
j = 5678 ; //実行はされない
printf("デバッグ文も実行されない%d:%d\n",i,j);
break;
case 1:
printf("変数は未初期化 %d:%d\n",i,j);
break;
case 2:
printf("変数は未初期化 %d:%d\n",i,j);
break ;
case 3:
printf("変数は未初期化 %d:%d\n",i,j);
break ;
}
}
case 前の変数宣言は可能ですが実行はされません。
このため変数の初期化やデバッグ文は実行はされないので
注意して下さい。
参考:
DCL41-C. switch 文のなかでは最初の case 句より前で変数宣言しない
https://www.jpcert.or.jp/sc-rules/c-dcl41-c.html
■冗長コード(redundant code)の例
➡同じ比較を2回
(warning) Identical condition ‘x==1’, second condition is always false
警告:同じ比較をしているので2番目の比較は常に偽になる[(warning)identicalConditionAfterEarlyExit]
#include <stdio.h>
typedef enum {R,G,B} color;
char *RGB(color x){
if(x == R){
return "赤";
}
if(x == R){
return "緑";
}
if(x == B){
return "青";
}
return "予期しない値";
}
int main(void){
printf("%s\n",RGB(G));
}
if(x == R)の比較を2回行っています。
1回目のif文が成立するとreturn するので、
2回目のif文が成立する事はありません。
➡両辺同じif(p->x!=0 && p->x!=0)
warning: self-comparison always evaluates to true
[-Wtautological-compare]
警告:両辺同じ物の比較は常に真になる
#include <stdio.h>
#include <stdbool.h>
typedef struct {
int Low;
int High;
} tag ;
bool same(tag data1, tag data2){
return (data1.Low == data2.Low) &&
(data2.High == data2.High);
}
int main(void){
tag data1 = {0,123};
tag data2 = {0,456};
if(same(data1,data2)){
puts("同じ");
}
}
論理演算子の両辺が同じなので
条件式に意味がありません。
➡then節とelse節が同じ
warning: this condition has identical branches
警告:then節とelse節が同じ
[-Wduplicated-branches]
#include <stdio.h>
char *samediff(int x,int y){
static char ans[128];
if(x == y){
sprintf(ans,"%dと%dは同じ",x,y);
return ans;
} else {
sprintf(ans,"%dと%dは同じ",x,y);//バグ
return ans;
}
}
int main(void){
printf("%s\n",samediff(1,2));
}
then節とelse節が同じなのでif文の意味がありません。
この問題はthen節をコピーしてelse 節を作り、
else節の変更を忘れた時等に発生します。
➡同一変数に上書き代入
(style): Variable ‘ret’ is reassigned a value before the old one has been used.
(スタイル):変数’ret’は古い値が使用される前に再設定された。
[(style)redundantAssignment]
extern int Sin(void),Cos(void);
int f1(void){
int ret = 0;
ret = Sin() ;//警告されない
return ret ;
}
int f2(void) {
int ret;
ret = 0 ;
ret = Sin() ;//警告されない
return ret ;
}
int f3(void) {
int ret ;
ret = Sin() ;
ret = Cos() ;//★警告される
return ret ;
}
この cppcheck の警告は3行目、9行目のような
とりあえず0で初期化した変数に再設定しても警告は出ません。
16行目のようにretを上書きして何のためにSin()関数を呼んだのか不明なものだけ警告してくれます。
➡*p++の丸括弧が足りない
warning: value computed is not used
warning: expression result unused
警告:計算された値は使用されません
[-Wunused-value]
#include <stdio.h>
void increment(int *p)
{
*p++ ;//ダメ:デッドコード
}
int main(void)
{
int x = 1234 ;
printf("前:%d\n",x);
increment(&x);
printf("後:%d\n",x);
}
*p++ の * がデッドコードです。
このincrement関数を呼んでも
ポインタpの指す先は加算されません。
➡(*p)++に修正
➡*p++の*が多い
#include <stdio.h>
int my_strlen(char *p){
int len = 0 ;
while(*p != '\0'){
*p++ ;//ダメ:デッドコード
len++ ;
}
return len;
}
int main(void){
printf("%d\n",my_strlen("abcd"));
}
4行目の*p != ‘\0’につられて
5行目も*p++ と書いてしまう場合が多いですが
5行目の*p++ の*がデッドコードです。
➡p++に修正
参考:
C-FAQ 4.3: *p++はpを増分するか。それともpが指すものを増分するのか。
http://www.kouno.jp/home/c_faq/c4.html#3
➡return x++ の後置演算子が冗長
warning: Value stored to ‘x’ is never read
警告:’x’に格納された値が読み出されない
[clang-analyzer-deadcode.DeadStores]
#include <stdio.h>
int increment(int x)
{
return x++; //NG:意味なし++
}
int main(void)
{
printf("%d\n",increment(1234)) ;//1234と表示される
}
自動変数 x を return 時に後置演算で ++ しても、
もはや誰も参照できないので 後置演算++ に意味がありません。
++ した値を return したいのであれば
2行に分けるか、
前置演算子を使いましょう
➡修正案1:2行に分ける
➡修正案2:前置演算子を使う
参考:
MSC07-C. デッドコードを検出して削除する