C言語とexternの全解説!7つの具体的な使い方とサンプルコード

C言語とexternの全解説と使い方を学ぶためのガイドC言語
この記事は約14分で読めます。

※本記事のコンテンツは、利用目的を問わずご活用いただけます。実務経験10000時間以上のエンジニアが監修しており、基礎知識があれば初心者にも理解していただけるように、常に解説内容のわかりやすさや記事の品質に注力しております。不具合・分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。(理解できない部分などの個別相談も無償で承っております)
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)


はじめに

C言語のプログラミングを学んでいく中で、externというキーワードを見かけることがあるでしょう。

初めて見ると混乱を引き起こすかもしれませんが、externはC言語の重要な一部分を担っています。

本記事では、初心者でもわかるようにexternの全解説と7つの具体的な使い方を行い、それぞれにサンプルコードを紹介します。

これを読めば、あなたもexternを活用したプログラムを作ることができます。

●C言語とexternとは

○C言語の基本

C言語は、1970年代に開発されたプログラミング言語で、その性能と汎用性から広く使われています。

OSからエンベデッドシステムまで幅広い領域で利用されており、さまざまなプログラムを記述することができます。

○externの役割

externは、C言語で使用されるキーワードの一つで、変数や関数が他のファイルで定義されていることを表します。

これにより、別のファイルで定義された変数や関数を、現在のファイルで使用することができます。

これは、大規模なプログラムで必要となる機能です。

●externの詳細な使い方とサンプルコード

externの具体的な使用例とそれぞれのサンプルコードをご紹介します。

○サンプルコード1:関数宣言とextern

このコードではexternを使って他のファイルで定義されている関数を宣言しています。

この例では、関数funcを別ファイルで宣言して、現在のファイルで使用しています。

extern void func(); 

int main() {
    func();
    return 0;
}

このコードを実行すると、externで宣言されたfunc関数が呼び出され、その結果が出力されます。

ただし、func関数が実際に別のファイルで定義されていることが前提です。

○サンプルコード2:グローバル変数とextern

このコードではexternを使って他のファイルで定義されているグローバル変数を使用しています。

この例では、変数numを別のファイルで定義し、それを現在のファイルで使用しています。

extern int num;

int main() {
    printf("%d\n", num);
    return 0;
}

このコードを実行すると、externで宣言されたnum変数の値が出力されます。

ここでも、numが実際に別のファイルで定義されていることが前提です。

○サンプルコード3:複数ファイル間でのexternの使い方

C言語プログラムが規模を拡大し、複数のファイルにまたがるようになると、externキーワードの真価が発揮されます。

ここでは、複数のファイル間でexternを使って変数を共有する方法を説明します。

具体的には、2つのファイル、”main.c”と”sub.c”があると仮定します。

“main.c”で定義された変数を”sub.c”で使用したいとします。

この場合、externを使うことで実現できます。

// main.c

#include <stdio.h>

int global_var = 100; // グローバル変数を定義

void print_var(void);

int main(void) {
  print_var(); // sub.cにある関数を呼び出す
  return 0;
}

上記は”main.c”の内容です。

このコードではglobal_varというグローバル変数を定義し、print_varという関数を呼び出しています。

このprint_var関数は別のファイル”sub.c”で定義されています。

次に”sub.c”の内容を見てみましょう。

// sub.c

#include <stdio.h>

extern int global_var; // main.cにある変数をexternで宣言

void print_var(void) {
  printf("%d\n", global_var); // main.cのglobal_varを出力
}

このコードでは、”main.c”で定義されたglobal_varをexternで宣言し、print_var関数内でその値を出力しています。

このようにexternを使用すれば、複数のファイル間で変数を共有することができます。

これらのコードをコンパイルし、実行すると、出力結果は以下のようになります。

100

これは、”main.c”で定義したglobal_varの値が正しく”sub.c”へ共有され、print_var関数で出力された結果です。

この例からも見て取れるように、externキーワードを用いることで、複数のファイル間でデータをやり取りすることが可能になります。

さらに大規模なプログラムであればあるほど、その恩恵は大きくなるでしょう。

ただし、この方法には注意点があります。

externで宣言した変数は、元の変数が定義されているファイルがコンパイルされるまで、実際に存在しないという点です。

このため、ファイルの依存関係やコンパイルの順序を適切に管理することが重要となります。

それらを怠ると、リンクエラーや予期せぬバグの原因となる可能性があります。

○サンプルコード4:ヘッダーファイルでのexternの利用

ヘッダーファイルとは、プログラム中で共有するための情報、例えば関数の宣言やグローバル変数の定義等を記述したファイルです。

externを使うことで、一つのヘッダーファイルに記述した変数や関数を他のファイルから参照することが可能となります。

これは大規模なプロジェクトで役立つことが多く、各ファイルがヘッダーファイルを介してデータを共有し、処理を分散することでプログラム全体の組織化を図ることができます。

では、具体的なコードを見ていきましょう。

【sample.h】

#ifndef SAMPLE_H
#define SAMPLE_H

extern int global_variable;

void print_global_variable();

#endif

【sample.c】

#include "sample.h"
#include <stdio.h>

int global_variable = 10;

void print_global_variable() {
    printf("%d\n", global_variable);
}

【main.c】

#include "sample.h"

int main() {
    print_global_variable();
    return 0;
}

ここでは、ヘッダーファイル「sample.h」を利用しています。

このヘッダーファイル内でexternを用いてグローバル変数「global_variable」を宣言し、関数「print_global_variable」の宣言も行っています。

この後、sample.cで具体的な定義を行い、main.cで呼び出して使用しています。

main.cを実行すると、global_variableの値「10」が出力されます。

これは、main.cがprint_global_variable関数を呼び出し、その関数がsample.cで定義されたglobal_variableを参照しているからです。

この例からも分かるように、externを使うことでヘッダーファイルを介したデータの共有が可能となります。

これにより、プログラムの構造化や分割が容易となります。

○サンプルコード5:関数の定義を別のファイルで行う例

この例では、関数の宣言を一つのファイルで行い、その定義を別のファイルで行うという使用例を見ていきます。

externを使うことで、関数の宣言と定義を分けて記述することができます。

これは大規模なプロジェクトで有用で、ソースコードの可読性を高める効果があります。

下記のコードはその一例です。

【function.h】

#ifndef FUNCTION_H
#define FUNCTION_H

extern void print_hello();

#endif

【function.c】

#include <stdio.h>
#include "function.h"

void print_hello() {
    printf("Hello, world!\n");
}

【main.c】

#include "function.h"

int main() {
    print_hello();
    return 0;
}

このコードでは、function.hでprint_hello関数を宣言し、function.cで定義を行っています。

main.cでは、function.hをインクルードすることでprint_hello関数を利用しています。

このコードを実行すると、「Hello, world!」が出力されます。

これは、main.cがprint_hello関数を呼び出し、その関数がfunction.cで定義されているからです。

このように、関数の宣言と定義を分けて記述することで、ソースコードの可読性を向上させることができます。

○サンプルコード6:externと静的変数の関係

ここでは、externキーワードと静的変数(static)の関係について解説します。

externとstaticは一見相反する性質を持っているように思えますが、それぞれがどのように動作するか理解することで、これらをどのように組み合わせて使用するかが明らかになります。

static変数は、その変数が定義されたファイル内でのみ参照可能な変数を指します。

つまり、他のファイルからは参照できません。

それに対して、externは変数や関数が他のファイルでも使用できることを表します。

したがって、基本的にexternとstaticは同じ変数に対して同時に使用することはできません。

しかし、externとstaticが同じ関数内でどのように振る舞うかについては、次のサンプルコードで説明します。

#include <stdio.h>

void func() {
    static int x = 0;
    x++;
    printf("x: %d\n", x);
}

int main() {
    func();
    func();
    return 0;
}

このコードでは、func関数内でstatic int x = 0と宣言し、その値をインクリメントして表示しています。

main関数内では、func関数を二回呼び出しています。

この例では、static変数xはfunc関数が呼び出される度に初期化されるのではなく、前回の値を保持したままであることがわかります。

つまり、static変数は関数の呼び出しごとにその値がリセットされるのではなく、保持され続ける性質があるということです。

さて、externとstaticが同じ関数内でどのように振る舞うかを確認しました。そ

れでは、externを使った場合と使わなかった場合の差を次のサンプルコードを使って説明します。

#include <stdio.h>

void func() {
    int x = 0;
    x++;
    printf("x: %d\n", x);
}

int main() {
    func();
    func();
    return 0;
}

このコードでは、先程のコードからstaticキーワードを取り除いています。

したがって、xは毎回関数が呼び出される度に初期化されます。

これを実行すると、出力結果はxの値が毎回1であることが確認できます。

つまり、staticが無い場合、変数xはfunc関数が呼び出される度に0に初期化され、その後インクリメントされて1になるのです。

○サンプルコード7:externとconstの組み合わせ

ここでは、externとconstを組み合わせた使用方法について説明します。

constは「定数」という意味で、一度設定した値を変更できない変数を定義する際に用います。

この性質を利用し、プログラムの各部分で一貫性を保つために利用します。

では、サンプルコードを見てみましょう。

// file1.c
extern const int MAX;
int main() {
  printf("%d\n", MAX); // MAXの値を出力
  return 0;
}

// file2.c
const int MAX = 100; // MAXを定義し、初期値100を設定

このコードでは、file2.cで定義されたconst変数MAXを、file1.cでextern宣言して使用しています。

ここではconst変数MAXを使って値を表示するコードを紹介しています。

この例では、一度定義した定数MAXの値を他のファイルから参照して表示しています。

このコードを実行すると、”100″と表示されます。

これは、file2.cで定義したMAXの初期値をfile1.cから正しく参照できていることを意味します。

しかし、externで宣言したconst変数の値を変更しようとするとエラーが発生します。

このことからも、externとconstの組み合わせは、「全体で共有される一定の値」をプログラム内で利用したい場合に有効であることがわかります。

●externを使う上での注意点と対処法

externの使用にはいくつかの注意点があります。

初めてexternを使用する際は、特に注意が必要です。

まず、externは変数や関数が他のファイルで定義されていることを宣言するためのキーワードであり、extern自体が変数を生成するわけではありません。

そのため、externで宣言した変数がどこにも定義されていないと、リンクエラーが発生します。

これは、コンパイラが変数の定義を探すものの見つけられず、その結果としてエラーが出るためです。

また、externで宣言した変数を初期化しようとすると、その変数はもはやexternではなくなり、新たに定義されたものと見なされます。

例えば、次のようなコードを見てみましょう。

// file1.c
extern int x = 10; // xの初期化を試みている
int main() {
  printf("%d\n", x);
  return 0;
}

// file2.c
int x = 20; // xを定義し、初期値20を設定

このコードを実行すると、”10″が出力されます。

なぜなら、file1.cのextern int x = 10;は、実際には新たな変数xを定義し、その初期値を10に設定しているからです。

これは、初心者がexternを使用する際によく陥る誤解であり、注意が必要です。

●externのカスタマイズ方法

プログラミングの世界では、問題解決の手段は一つではありません。それはexternの利用においても同様です。

externの基本的な使い方を理解したら、次はそれを自分のニーズに合わせてカスタマイズする方法を学びましょう。

一つの方法として、externを使って別のファイルで定義された配列を使用することが挙げられます。

これはデータを共有したり、大きなプログラムを管理しやすくするために有効です。

ここではexternを使って配列を共有する方法について詳しく解説します。

サンプルコードを紹介します。

// data.c
#include<stdio.h>
int numbers[5] = {1, 2, 3, 4, 5}; // 外部ファイルで定義された配列

// main.c
#include<stdio.h>
extern int numbers[5]; // data.cで定義された配列を参照

int main(){
    for(int i=0; i<5; i++){
        printf("%d ", numbers[i]); // 配列の要素を表示
    }
    return 0;
}

data.cでは5つの要素を持つ整数型の配列numbersを定義しています。

一方main.cではexternを使ってdata.cで定義されたnumbers配列を参照しています。

そしてmain関数内でその配列の要素を出力しています。

このコードを実行すると、配列numbersの全ての要素が順に出力されます。つまり、’1 2 3 4 5’という結果が得られます。

このように、externは様々な形でカスタマイズが可能です。

まとめ

この記事では、C言語のexternの詳細な使い方とサンプルコードについて解説しました。

externは、プログラムの複数のファイル間で変数や関数を共有するための重要なキーワードです。

正しく理解し使用することで、コードの可読性や保守性を向上させることができます。

しかし、初心者がexternを使用する際には注意点も多いです。

特に、externで宣言した変数がどこにも定義されていない場合や、externで宣言した変数を初期化しようとした場合には、問題が発生します。

それらの注意点を理解し、適切にexternを活用することで、あなたも効率的で質の高いコードを書くことができるでしょう。

この記事が参考になれば幸いです。

最後までお読みいただき、ありがとうございました。