【C言語の基本】変数のスコープと寿命を理解しよう!staticとexternの使い分け

C言語でプログラミングをしていると、「この変数は、どこからどこまで使えるんだろう?」「関数を呼び出すたびに、変数の値がリセットされてしまうのはなぜ?」といった疑問にぶつかることがあります。これらの疑問を解決する鍵が、**変数の「スコープ」と「記憶域期間(寿命)」**の理解です。

今回は、変数がプログラムのどこで有効になるのか、そしていつまで値を保持し続けるのか、というC言語の非常に重要な概念を、staticexternといったキーワードと共に徹底解説します。


目次

変数が使える範囲「スコープ」とは?

スコープとは、一言でいうと**「その変数や関数が参照(利用)できる範囲」**を定めたルールのことです。変数は宣言された場所によって、どこからアクセスできるかが決まっています。

ブロックスコープ(ローカル変数)

最も基本的なスコープが「ブロックスコープ」です。これは、{} (波括弧) で囲まれたブロックの中で宣言された変数は、そのブロック内でしか使えない、というルールです。関数の中で宣言される変数(ローカル変数)がこれにあたります。

void myFunction() {
    int local_val = 10; // local_valはmyFunctionの中だけで有効
    // ここでは local_val を使える
}

int main(void) {
    // ここから local_val にアクセスしようとするとコンパイルエラーになる
    return 0;
}

この性質により、他の関数で同じ名前の変数を使っていても、互いに影響を与えず、安全にプログラミングができます。

ファイルスコープ(グローバル変数)

一方、どの関数の内側でもなく、ファイルのトップレベルで宣言された変数は「ファイルスコープ」を持ちます。これをグローバル変数と呼びます。

グローバル変数は、宣言されたファイル全体、どの関数からでもアクセスできるという特徴があります。

#include <stdio.h>

// g_app_status はグローバル変数
int g_app_status = 100;

void checkStatus(void);

int main(void) {
    printf("main関数から参照: %d\n", g_app_status);
    checkStatus();
    return 0;
}

void checkStatus(void) {
    printf("checkStatus関数から参照: %d\n", g_app_status);
}

実行結果:

main関数から参照: 100
checkStatus関数から参照: 100

このように、main関数とcheckStatus関数の両方から同じ変数 g_app_status が参照できています。


変数が存在する期間「記憶域期間(寿命)」とは?

記憶域期間とは、「変数がメモリ上にいつ生成され、いつまで存在し続けるか」という、変数の寿命に関するルールです。

自動記憶域期間(普通のローカル変数)

関数の中で普通に宣言されたローカル変数は、「自動記憶域期間」を持ちます。これは、その変数が宣言されたブロック {} が実行される時にメモリが確保され、ブロックを抜ける瞬間に自動で破棄されるという性質です。

そのため、関数が呼び出されるたびに変数は初期化されます。

サンプルコード:

#include <stdio.h>

void countUp(void);

int main(void) {
    countUp();
    countUp(); // もう一度呼び出す
    return 0;
}

void countUp(void) {
    int number = 0; // 自動記憶域期間を持つ(呼び出されるたびに0になる)
    number += 5;
    printf("変数numberの値: %d\n", number);
}

実行結果:

変数numberの値: 5
変数numberの値: 5

2回呼び出しても、結果は同じ5です。countUp関数が終了するたびに変数numberが破棄され、次に呼び出されたときに再び0で作り直されるためです。

静的記憶域期間(グローバル変数や静的変数)

一方、プログラムの開始から終了まで、ずっとメモリ上に存在し続ける変数を「静的記憶域期間」を持つ変数といいます。グローバル変数がこの性質を持ちます。

そして、staticというキーワードを使うことで、ローカル変数に静的記憶域期間を持たせることも可能です。


2つの意味を持つstaticキーワード

staticキーワードは、付ける場所によって少し意味が変わる、少し特別なキーワードです。

1. 値を保持し続ける「静的ローカル変数」

関数内のローカル変数の宣言にstaticを付けると、その変数は静的ローカル変数になります。

  • スコープ: 通常のローカル変数と同様、宣言された関数内のみ。
  • 寿命: プログラムの実行終了まで。関数を抜けても破棄されず、値を保持し続けます。

先ほどのcountUp関数のnumberstaticを付けてみましょう。

サンプルコード:

#include <stdio.h>

void staticCountUp(void);

int main(void) {
    staticCountUp();
    staticCountUp(); // もう一度呼び出す
    return 0;
}

void staticCountUp(void) {
    // 静的ローカル変数(初回のみ0で初期化され、以降は値を保持する)
    static int static_number = 0; 
    static_number += 5;
    printf("静的変数static_numberの値: %d\n", static_number);
}

実行結果:

静的変数static_numberの値: 5
静的変数static_numberの値: 10

今度は、2回目の呼び出しで値が10になりました。static_numberstaticCountUp関数を抜けても値を保持しているため、前回の5にさらに5が加算されたのです。

2. 公開範囲をファイル内に限定するstatic

グローバル変数の宣言にstaticを付けると、その変数のスコープ(正確にはリンケージ)を、そのファイル内に限定することができます。

これにより、他のCファイルからそのグローバル変数を参照できなくなり、意図しない場所からのアクセスを防いだり、他のファイルで定義された同名のグローバル変数との名前の衝突を避けたりすることができます。大規模なプログラム開発において、非常に重要な機能です。

// このファイルの中だけで使えるグローバル変数
static int file_local_counter = 50; 

複数のファイルで変数を共有するextern

externは、「別のファイルで定義されているグローバル変数を、このファイルでも使いますよ」とコンパイラに伝えるためのキーワードです。

例えば、main.csub.cという2つのファイルがある場合、

sub.c

// 実体を定義
int g_shared_value = 200; 

main.c

#include <stdio.h>

// sub.cで定義されているg_shared_valueを参照することを宣言
extern int g_shared_value; 

int main(void) {
    printf("別のファイルで定義された変数の値: %d\n", g_shared_value);
    return 0;
}

このように、externを使うことで、プロジェクト内の複数のファイルにまたがって変数を共有できます。

ただし、どこからでもアクセスできるグローバル変数の多用は、プログラムの構造を複雑にし、バグの温床になりやすいため、利用は慎重に行うべきです。


まとめ

今回は、C言語における変数のスコープ(有効範囲)と記憶域期間(寿命)について学びました。

種類スコープ(範囲)寿命(記憶域期間)特徴
ローカル変数ブロック内部ブロック実行中(自動)最も基本的。関数内で完結する。
静的ローカル変数ブロック内部プログラム終了まで(静的)関数を抜けても値を保持する。
グローバル変数ファイル全体プログラム終了まで(静的)どこからでもアクセス可能。多用は注意。
静的グローバル変数ファイル内部のみプログラム終了まで(静的)他のファイルから隠蔽される。

これらの違いを正しく理解し、適切な場所で適切な種類の変数を使い分けることが、バグが少なく、メンテナンス性の高いコードを書くための第一歩です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次