11.1 コーディング上の注意事項

(1)

関数原型について

関数を呼び出す際には、呼び出される関数の関数原型を行ってください。関数原型を行わない場合、パラメータの受け渡しが正しく行えない場合があります。

例 1.

float型パラメータをもつ関数(dbl_size=8を指定した場合)

void g()
{
            float a;
            ...
            f(a);                      // aはdouble型に変換されます。
}
void f(float x)
{...}

 

例 2.

スタック渡しとなるsigned char、(unsigned)char、(signed)short、およびunsigned short型のパラメータをもつ関数

void h();
void g()
{
            char a,b;
            ...
            h(1,2,3,4,a,b);            // a,bはint型に変換されます。
}
void h(int a1, int a2, int a3, int a4, char a5, char a6)
{...}

 

(2)

引数に型情報のない関数宣言

同じ関数に対して関数宣言(関数定義を含む)を複数行うとき、引数並びに型を記述しない形式と、型を記述する形式を両方使用しないでください。

この場合、呼び出す関数と呼び出される関数とで引数の解釈に違いが生じるため、生成コードが型を正しく処理できない場合があります。

コンパイル時にW0520147のメッセージが表示された場合、この問題に該当している可能性がありますので、引数並びに型を記述する形式に変更するか、生成コードを確認して引数の受け渡しに問題がないかを確認してください。

 

old_styleを異なる形式で記述しているために、引数dとeの型の意味が呼び出す関数と呼び出される関数で異なるため、引数の受け渡しが正しく行われません。

extern int old_style(int,int,int,short,short);  /* 関数宣言:引数並びに型を記述する形式 */
int old_style(a,b,c,d,e)    /* 関数定義: 引数並びに型を記述しない形式 */
 int a,b,c;
 short d,e;
{
    return a + b + c + d + e;
}
int result;
func()
{
    result = old_style(1,2,3,4,5);
}

 

(3)

C/C++言語で評価順序を規定していない式

C/C++言語規格で評価順序が規定されていない式を用いる際、評価順序で結果が変わるようなコーディングをした場合は動作保証しません。

 

a[i]=a[++i];  代入式の右辺を先に評価するか後に評価するかで左辺の値が変わります。
 
sub(++i, i);  関数の第1引数を先に評価するか後に評価するかで第2引数の値が変わります。

 

(4)

オーバーフロー演算、ゼロ除算

オーバーフロー演算や浮動小数点のゼロ除算があっても、エラーメッセージを出力しません。ただし、一つの定数または定数どうしの演算でのオーバーフロー演算があれば、コンパイル時にエラーメッセージを出力します。

 

void main()
{
  int ia; 
  int ib; 
  float fa;
  float fb;
 
  ib=32767;
  fb=3.4e+38f;
 
  /* 定数または定数どうしの演算時はオーバーフローに対する                       */
  /* コンパイルエラーメッセージを出力します                                   */
 
  ia=99999999999;          /* (W) 定数のオーバーフローを検出します           */
  fa=3.5e+40f;             /* (E) 浮動小数点演算のオーバーフローを検出します   */
 
  /* 実行時のオーバーフローに対するエラーメッセージは出力しません                 */
 
  ib=ib+32767;             /* 演算結果のオーバーフローを無視します            */
  fb=fb+3.4e+38f;          /* 浮動小数点演算結果のオーバーフローを無視します    */
}

 

(5)

const型変数への書き込み

const型の変数を宣言していても、型変換でconst型でない型に変換して代入した場合や、分割コンパイルしたプログラムの間で、型を統一して扱っていない場合は、const型変数への書き込みをコンパイラでチェックできませんので、注意が必要です。

 

const char *p;                    /* ライブラリ関数strcatの第1引数は char型への */
 :                                /* ポインタ型なので、引数の指す領域が書き換わる   */
strcat(p, "abc");                 /* ことがあります                            */
 
  ファイル1
const int i;
 
  ファイル2
extern int i;                     /* 変数iは、ファイル2ではconst型で宣言していま*/
 :                                /* せんのでファイル2の中で書き込んでもエラーに   */
i=10;                             /* なりません                                */

 

(6)

数学関数ライブラリの精度について

acos(x)、asin(x)関数ではx≒1で誤差が大きくなりますので注意が必要です。

誤差範囲は以下のとおりです。

acos(1.0 -ε)における絶対誤差 倍精度2-39 (ε= 2-33 )

単精度2-21 (ε= 2-19 )

asin(1.0 -ε)における絶対誤差 倍精度2-39 (ε= 2-28 )

単精度2-21 (ε= 2-16 )

 

(7)

最適化により削除される可能性のあるコーディング

連続した同一変数の参照や、結果を使用しない式を記述した場合、コンパイラの最適化により冗長コードとして削除される場合があります。常にアクセスを保証する場合は、宣言時にvolatile を指定してください。

 

[1] b=a;              /* 1行目の式は冗長コードとして削除されることがあります    */
    b=a;
[2] while(1)a;        /* 変数aの参照およびループ文は冗長コードとして削除される  */
                      /* ことがあります                                    */

 

(8)

C89とC99の動作の差異

C99では、選択文および反復文は、{}で囲まれます。そのため、C89とC99で動作が異なることがあります。

 

enum {a,b};
int g(void)
{
     if(!sizeof(enum{b,a}))
     return a;
     return b;
}

上記を-lang=c99を指定してコンパイルすると、以下の解釈となります。

enum {a,b};
int g(void)
{
     {
         if(!sizeof(enum{b,a}))
         return a;
     }
     return b;
}

-lang=cでは、g()=0となりますが、-lang=c99では、g()=1となります。

(9)

オーバーフローを伴う演算および型変換に関する注意事項

数値演算や型変換を行う場合、その型で取り扱える値の範囲外(オーバーフロー)とならないようにご注意ください。オーバーフローが発生すると、得られる結果がコンパイルオプションなどの条件によって変化する場合があります。

標準のC言語では、オーバーフローを伴う演算処理の結果は未定義となっており、コンパイル条件などにより得られる結果が異なる場合があります。

演算処理を行うプログラムでは、オーバーフローを発生させないようにご注意ください。

実際に結果が異なる例を次に示します。

 

float 型→unsigned short 型への変換

float f = 2147483648.0f;
unsigned short ui2;
void ex1func(void)
{
       ui2 = f; /* float → unsigned short への変換 */
}

ex1func関数を実行して得られるui2の値は、FPUあり(-fpu)とFPUなし(-nofpu)とで次のように異なります。

FPUあり: ui2 = 65535

FPUなし: ui2 = 0

これは、float型からunsigned short型への変換の方法がFPUありとFPUなしで異なるためです。

 

(10)

アンダーバー(_)を2つ並べたシンボルの記述

アンダーバーを2つ以上並べたシンボルは記述しないようにしてください。

生成されるコードは問題なく動作しますが、リンクマップ表示などでは関数名がそのまま表示されず、異なる名前のC++の関数名として表示される場合があります。

 

 int sample__Fc(void) { return 0; }

この場合、リンクマップ出力には、_sample__Fc ではなく、sample(char)と表示されます。