mcommit's message

大阪でソフトウェア開発の仕事をしている simotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

No More No Prototype Function

■2016/7/4 追記
こちらの記事に書いている内容について、2016/7/2にシステムプログラミング会で発表させて頂きました。
参加者の皆様には釈迦に説法な話で、お時間を取ってしまい失礼しました! _(m_m)_
参考までにプレゼンの内容はこちらにあげております。

www.slideshare.net

主観ですが記事より、もう少し具体的かつ、分かりやすくまとまっているように思いますのでこちらの上の方に張り付けておきたいと思います。


[記事本文]

ルネサスコンパイラではプロトタイプ宣言がなくてもリンクまでは通ります。

ワーニングレベルがゆるゆるの場合、
警告されず、プロトタイプ宣言がきちんとできていないことすら気付かないかもしれません。

単純なプログラムの例だと、
main関数で呼び出した関数の定義がmain以前に存在しないと、
プロトタイプ宣言がない状態になります。

// プロトタイプ宣言がないとき → ワーニングとかインフォとかで警告されるかも。
#include <stdio.h>

int main(int argc,char **argv) {
  if (1 == hoge()) {
    printf("One!\n");
  }
  return 0;
}

unsigned char hoge(void) {
  return 1;
}


これを解決するためにはmain関数以前にプロトタイプ宣言を書く必要があります。

// プロトタイプ宣言があるとき → 問題ない!
#include <stdio.h>

unsigned char hoge(void);
int main(int argc, char **argv) {
  if (0 ==hoge()) {
    printf("One!\n");
  }
  return 0;
}

unsigned char hoge(void) {
  return 1;
}


プロトタイプ宣言を書くのが邪魔くさい人は、
main関数をファイルの一番下に書いたりします。

mainと同様にexternな関数も下の方に書かれることになる。
public→privateな並びが好きなので、この流派は個人的には嫌い。

さて、

プロトタイプ宣言がない状態で上記のプログラムを動かすと、

"One!"が表示されないことがあります。

■理由

プロトタイプ宣言が見つからなかった場合、 呼び出し側をコンパイルする際に、
関数hogeの戻り値はint型と解釈されます。

コンパイラ的には、

え~あんた何者!?
ちゃんと身分証明書提示してくれないと困るんだけど。
まあお宅も悪気はなかったみたいだし、今回はとりあえずintとして通しておくから。
あっ、その代りなんか問題あってもおれ知らないからね!

とまぁ融通を利かしてくれてのことだと思います。

したがって、

  if (1 ==hoge()){
  }

の部分でifの比較を行う際に、直値「1」はint型のサイズのレジスタに格納されます。

一方で、関数hoge側は、実装上はunsigned charを返すことになっているので

関数がreturnするコードは、1byteのレジスタに1を設定します。

このときレジスタの上位1byteは1にはなりません。

仮にレジスタが、

0x1234

だった場合、

0x1201

になるだけです。

そしてこれら二つのレジスタの比較はint型のサイズで行われます。

その結果、

直値1のレジスタ            :0x0001
関数hogeの戻り値のレジスタ :0x1201

※int型が16bitの場合。

のような状態で比較が行われ、結果明らかに正しいと思われるifの中を通らないことがあります。

■結論

No more "No prototype function."

■補足

今回の話は、処理系(コンパイラ)により異なるとは思います。ちゃんと動作するコンパイラもあると思います。

このような現象に遭遇した場合、出力されているアセンブラをみれば原因に気づくことができると思いますが、C言語はわかるけどアセンブラはちょっとみたいな状況ではつらいと思います。

プロトタイプ宣言はきちんと書きましょう。※GCCでいう -Wallをつけるのも。