C言語の開発を進める中で、コンパイラから「error: redefinition of ‘x’」というエラーメッセージを提示され、作業が止まってしまった経験を持つ方は多いのではないでしょうか。
このエラーは、プログラム内で同じ識別子(変数名や関数名など)が複数回定義されていることをコンパイラが検知した際に発生します。
C言語の文法規則では、同一のスコープ内において同じ名前を二重に定義することは認められていません。
本記事では、このエラーが発生する具体的な原因とその解決策について、詳しく掘り下げていきます。
error: redefinition of ‘x’ とは何を意味するのか
C言語における「定義(Definition)」とは、変数や関数のためのメモリ領域を確保し、その実体を作成する行為を指します。
コンパイラはソースコードを上から順番に読み込みますが、一度定義された名前を再度定義しようとすると、どちらの実体を参照すべきか判断できなくなります。
「error: redefinition of ‘x’」の「x」の部分には、重複している変数名や関数名が具体的に表示されます。
このエラーを解消するためには、プログラム内のどの範囲でその名前が有効であるかという「スコープ」の概念を理解することが重要です。
宣言と定義の違いを理解する
C言語を扱う上で、宣言(Declaration)と定義(Definition)の区別は非常に重要です。
宣言は「このような名前の変数が存在します」という情報をコンパイラに伝えるだけの行為であり、メモリの確保は行いません。
一方で、定義は実際にメモリ上の場所を割り当てるため、一つのプログラム内で一つの実体に対して一度しか行うことができません。
多くの初心者は、宣言と定義を混同して記述してしまうことで、この再定義エラーを引き起こしてしまいます。
エラーが発生する主な原因
再定義エラーが発生する状況はいくつかパターン化されています。
まずは、最も単純な同一関数内での重複定義について見ていきましょう。
1. 同一スコープ内での重複定義
同じ関数の中で、全く同じ名前の変数を二度宣言・定義しようとした場合に発生します。
#include <stdio.h>
int main() {
int score = 100; // 1回目の定義
int score = 200; // 2回目の定義(ここでエラーが発生)
printf("Score: %d\n", score);
return 0;
}
このコードをコンパイルしようとすると、コンパイラはscoreという名前が二度定義されていると警告し、処理を中断します。
2. ヘッダーファイルの二重インクルード
大規模なプロジェクトにおいて非常に発生しやすいのが、ヘッダーファイルの二重読み込みによる再定義です。
例えば、あるヘッダーファイルで構造体やグローバル変数が定義されており、それを複数のソースファイルから直接、あるいは間接的にインクルードした場合に起こります。
インクルードは単なるテキストの置換作業であるため、同じ内容が何度もソースコードに展開される結果、再定義とみなされます。
3. グローバル変数の競合
複数のソースファイル(.cファイル)で、externを付けずに同じ名前のグローバル変数を定義してしまうケースです。
それぞれのファイル単体ではコンパイルが通りますが、最後にファイルを結合する「リンク」の段階で「再定義」のエラー(または重複シンボルエラー)が発生します。
具体的な解決策と修正方法
エラーの原因を特定できたら、適切な修正を行う必要があります。
以下の表は、原因別の主な対処法をまとめたものです。
| 発生原因 | 解決策 |
|---|---|
| 同一関数内での重複 | 変数名を変更するか、既存の変数に代入を行う。 |
| ヘッダーの二重読み込み | インクルードガード(#ifndef)または #pragma once を使用する。 |
| グローバル変数の重複 | 定義は1箇所のみで行い、他は extern 宣言にする。 |
解決策1:インクルードガードの導入
ヘッダーファイルによる再定義を防ぐための標準的な手法が「インクルードガード」です。
以下のように、ヘッダーファイルの先頭と末尾にプリプロセッサ命令を記述します。
/* my_header.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H
struct Player {
int id;
char name[32];
};
#endif
このように記述することで、二度目以降のインクルード時には#ifndefの条件が偽となり、中身が読み飛ばされるため、再定義を防ぐことができます。
また、最近のコンパイラであれば、ファイルの先頭に#pragma onceと記述するだけでも同様の効果が得られます。
解決策2:extern 宣言の活用
複数のファイル間で変数を共有したい場合は、定義と宣言を明確に分ける必要があります。
一つのファイルで実体を「定義」し、他のファイルではその存在を知らせる「宣言」のみを行います。
/* global.c (定義) */
int global_counter = 0;
/* main.c (利用側) */
#include <stdio.h>
extern int global_counter; // 宣言のみ。メモリ確保は行わない。
int main() {
global_counter = 10;
printf("%d\n", global_counter);
return 0;
}
10
externキーワードを付けることで、「この変数はどこか別の場所で定義されています」という合図になり、再定義エラーを回避できます。
エラーを防ぐためのベストプラクティス
コードが複雑になる前に、再定義エラーを未然に防ぐ習慣を身につけることが大切です。
変数名に意味のあるプレフィックスを付ける
大規模なプログラムでは、単純な名前(i, j, data, countなど)は衝突の危険性が高まります。
モジュール名や機能名を接頭辞として付けることで、意図しない名前の重複を避けることができます。
変数のスコープを最小限に抑える
可能な限り、グローバル変数の使用は避けるべきです。
変数は必要な関数内、あるいは特定のブロック内でのみ有効となるように設計してください。
関数内でしか使わない変数であれば、再定義エラーが起きた際も原因の特定が容易になります。
static修飾子の活用
特定のソースファイル内だけで使用するグローバル変数や関数には、static修飾子を付与しましょう。
staticを指定することで、その名前の有効範囲がそのファイル内に限定されるため、他のファイルで同じ名前が使われていても干渉しなくなります。
/* module_a.c */
static int local_val = 1; // このファイル内でのみ有効
/* module_b.c */
static int local_val = 2; // module_a.cのlocal_valとは別物として扱われる
まとめ
「error: redefinition of ‘x’」は、C言語の基本的なルールである「二重定義の禁止」に抵触した際に発生するエラーです。
同じスコープ内で同じ名前を二度使っていないか、あるいはヘッダーファイルが何度も読み込まれていないかを確認することが解決の近道です。
特にヘッダーファイルには必ずインクルードガードを施し、複数のファイルで共有する変数はextern宣言を正しく使い分けるようにしましょう。
これらのルールを徹底することで、コンパイルエラーに悩まされる時間を減らし、より本質的なロジックの実装に集中できるようになります。
エラーメッセージはプログラムからの重要なフィードバックですので、一つずつ冷静に原因を紐解いていくことがスキルアップに繋がります。
