error: implicit declaration of function 'X' の原因と直し方
標準ライブラリの関数を使う際に、対応する#includeを書き忘れたときに出るエラーです。以前のC言語では警告どまりでしたが、現在は多くの環境でエラーとして扱われます。
エラーメッセージの読み方
main.c:5:21: error: implicit declaration of function 'strlen' [-Wimplicit-function-declaration]
main.c- ファイル名
5- 行番号
21- 列番号 — この位置でgccが構文解析に失敗しました
implicit declaration of function 'strlen' [-Wimplicit-function-declaration]- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 #include <stdio.h> 2 ^ 3 4 int main(void) { 5 printf("%zu\n", strlen("hi")); 6 return 0; 7 }
strlenはstring.hで宣言されている関数です。宣言が見えない状態で呼び出すと、コンパイラはその存在を知らないためエラーになります。
直し方: (空) を #include <string.h> にします。
パターン2
1 #include <stdio.h> 2 ^ 3 4 int main(void) { 5 printf("%d\n", abs(-5)); 6 return 0; 7 }
absはstdlib.hで宣言されています。関数名だけ合っていても、宣言をインクルードしていなければコンパイラは受け付けません。
直し方: (空) を #include <stdlib.h> にします。
パターン3
1 #include <stdio.h> 2 ^ 3 4 int main(void) { 5 printf("%.1f\n", sqrt(9.0)); 6 return 0; 7 }
sqrtはmath.hで宣言されています。数学関数だからといって自動で使えるわけではなく、対応するヘッダが必要です。
直し方: (空) を #include <math.h> にします。
パターン4
1 #include <stdio.h> 2 ^ 3 4 int main(void) { 5 printf("%d\n", toupper('a')); 6 return 0; 7 }
toupperはctype.hで宣言されています。文字判定・変換系の関数はこのヘッダにまとまっています。
直し方: (空) を #include <ctype.h> にします。
パターン5
1 #include <stdio.h> 2 ^ 3 4 int main(void) { 5 char buf[10]; 6 strcpy(buf, "hi"); 7 printf("%s\n", buf); 8 return 0; 9 }
strcpyもstring.hで宣言されている関数です。宣言が見えないまま呼び出すとエラーになります。
直し方: (空) を #include <string.h> にします。
よくある誤解
「関数名さえ合っていれば動くはず」というのは古いC(C89)の話です。現在のコンパイラは、宣言(プロトタイプ)が見えない関数呼び出しをその場で拒否します。関数がどのヘッダに属するかを覚えておく必要があります。
実務での勘所
implicit declarationが警告で済んでいた時代(C89)は、コンパイラは宣言の見えない関数を「intを返し、任意の引数を取る」と仮定して、とりあえずコンパイルを通していました。この仮定が実際の関数の戻り値の型(たとえばポインタを返す関数)と食い違うと、32ビット時代はintとポインタのサイズが同じだったため偶然動くことが多かったのですが、64ビット環境ではポインタのサイズがintの2倍になるため、この仮定に基づいたコードは値が壊れたりクラッシュしたりする危険な作りになります。現在の多くのコンパイラがこれを警告からエラーに格上げしているのは、この64ビット環境での実害が明確になったためです。