C言語 staticを変数と関数に付ける価値【保護の仕組みを解説】

C言語
この記事は約9分で読めます。

こんにちは、ナナです。

「static」とは皆さんの作るプログラムを、より安全にするための機能です。

もちろん、使わなくてもプログラミングはできますが、大規模システムになるほど安全性を高めることが重要になるのです。

「安全にするための機能」とはいったい何なのか、それを示します。

本記事では次の悩みを解消する内容となっています。

本記事の悩み解決リスト
  • static修飾子はどうやって記述するものなの?
  • 関数にstaticを付ける方法とその効果とは?
  • グローバル変数にstaticを付ける方法とその効果とは?
  • ローカル変数にstaticを付けた時の効果と特殊な扱いとは?

では、「static」修飾子を学んでいきましょう。

スポンサー

変数と関数に付与するstatic修飾子の使い方と役割

「static」というのは、修飾子と呼ばれるキーワードです。C言語において「static」を使うケースは次の2つです。

static修飾子を付与するもの
  • 関数定義に付与する
  • 変数定義に付与する

結論から言うと、「static」修飾子は

関数と変数の参照範囲を限定的にする効果

があります。

「static」は次のように、複数のソースファイルが存在する際に効果を発揮します。staticにて保護した関数や変数は、他のファイルからの参照を禁止することができます。

では、具体的にそれぞれのケースを解説します。

ナナ

参照範囲を狭くすることが、ソフトウェアシステムの安全性を高めることにつながります。

「static」はシステムを守護するガーディアンなんです。

スポンサー

static修飾子を関数に付与する効果とは?

まずは、関数にstaticを付与する方法と効果を示します。

static関数の定義方法

関数にstatic修飾子を付与するのは簡単です。関数定義の戻り値の型の横に「static」を記述するだけです。

static修飾子を伴う関数定義

書き方
 static 戻り値の型 関数名(引数1、引数2・・・)
 {
    ・・・
 }

関数定義例
 static int subfunc(long num1, short num2)
 {
    return 0;
 }

ナナ

定義の最初にstaticを付けるだけです、簡単ですね。たったこれだけなんです。

staticを付与した関数と付与しない関数の違い

staticの効果を知るためには、複数のソースファイルが必要となります。

例としてsub.cに通常の関数とstatic関数を定義したとします。そしてmain.cではそれらの関数を呼び出すようにしてみましょう。

プログラムは次の通りです。

main.c

#include <stdio.h>

int main(void)
{
    // 通常関数の呼び出し
    // 呼び出し可能
    subfunc();

    // static関数の呼び出し
    // 呼び出し不可
    s_subfunc();

    return 0;
}

sub.c

#include <stdio.h>

// 通常関数の定義
int subfunc(void)
{
    printf("Hello");
    return 0;
}

// static関数の定義
static int s_subfunc(void)
{
    printf("World");
    return 0;
}

このプログラムはビルドすると次のリンクエラーが発生します。

error LNK2019: 未解決の外部シンボル _s_subfunc が関数 _main で参照されました。

このようにstaticを付与した関数というのは、別のファイルから関数を呼び出すことができなくなります。

staticは関数呼び出し可能な範囲を、定義対象のファイル内に閉じ込めることができるのです。

本例では、sub.cのファイル内からしか「s_subfunc関数」を呼び出すことはできません。

ナナ

「関数とはサービスである」、それが本サイトでの関数のイメージです。

「不必要に他のファイルから関数の呼び出しができなくする」とは、サービスを利用できる人を限定的にするということです。誰でも利用できるというサービスから、一部の人だけが利用できるサービスとすることなんです。

このようにすることが、システムの安全性を高めることにつながります。

スポンサー

static修飾子を変数に付与する効果とは?

関数と同様に変数にもstatic修飾子を付与することができます。どちらかというと、関数よりもこちらの方が利用されるケースは多いことでしょう。

変数に付与する場合は、次の2つのケースで扱い方が少し変わるため注意が必要です。

static修飾子を付与する変数の種類
  • グローバル変数の定義に付与する
  • ローカル変数の定義に付与する

static変数の定義方法

変数にstatic修飾子を付与するのも簡単です。変数定義に「static」を記述するだけです。

static修飾子を伴う変数定義

書き方
 static データ型 変数名;

変数定義例
 static int num;

グローバル変数へのstatic修飾子の付与

関数と同様に複数のソースファイルを想定します。sub.cでは通常のグローバル変数と、static付きのグローバル変数を定義したとします。

main.cではsub.cのグローバル変数を参照するため、extern宣言を行ってプログラムを構築します。

main.c

#include <stdio.h>

extern long num;
extern long s_num;

int main(void)
{
	//	グローバル変数の参照
	printf("%d\n", num);

	//	staticなグローバル変数の参照
	printf("%d\n", s_num);

    subfunc();

	return 0;
}

sub.c

#include <stdio.h>

// 通常のグローバル変数定義
long num = 1000;

// staticなグローバル変数定義
static long s_num = 1000;

void subfunc(void)
{
    // グローバル変数の参照可能
    printf("%d\n", num);

    // static変数の参照も可能
    printf("%d\n", s_num);
}

ビルドをすると次のコンパイルエラーが発生します。

error LNK2001: 外部シンボル "_s_num" は未解決です。

このようにstaticを付与した変数というのは、別のファイルから変数を参照できなくなります。

static関数と同様に変数の参照範囲を、定義対象のファイル内に閉じ込めることができるのです。

ナナ

static付きのグローバル変数はよく利用されます。

変数へのアクセスは可能な限り狭い方がよいです。データを更新・参照できる権利というのは、限られているほど安全性が高いのです。

ローカル変数へのstatic修飾子の付与

関数の中で定義された変数を「ローカル変数」と呼びますね。実はローカル変数にも、static修飾子を付与することができます。

この変数は少し特殊な扱いとなるため、利用する際には注意が必要です。

特徴① 対象関数内でしか参照できない

ローカル変数はstaticが付いても付かなくても、定義された関数内からしか参照できません。つまり、参照範囲という点においてはローカル変数の特徴をそのまま引き継ぎます。

#include <stdio.h>

void subfunc(void)
{
    // static付きのローカル変数
    static int num = 10;

    printf("num:%d\n", num);
}

int main(void)
{
    subfunc();

    // subfuncのローカル変数は参照不可
    printf("num:%d\n", num);

    return 0;
}

ビルドすると次のエラーが発生します。main関数からnumを参照することはできません。

error C2065: 'num': 定義されていない識別子です。
ナナ

ローカル変数は、ファイルよりもさらに狭い「関数内」しか参照できません。staticの有無は関係ありません。

特徴② ローカル変数であるにも関わらず値が保持できる

static付きローカル変数は、ローカル変数ですが値を保持し続ける特徴があります。

#include <stdio.h>

void subfunc(void)
{
          int num1 = 0;
    static int num2 = 0;

    num1++;
    num2++;

    printf("num1:%d num2:%d\n", num1, num2);
}

int main(void)
{
    subfunc();
    subfunc();

    return 0;
}
num1:1 num2:1
num1:1 num2:2

num1は1回目も2回目も結果が「1」であるのに対し、static付きのnum2は結果が「1」から「2」に変化しています。

ローカル変数は通常、関数呼び出しとともに生成され、returnされることで破棄されます。しかし、static付きのローカル変数は破棄されなくなります。

そのため、ローカル変数であるにも関わらず前回の設定値を保持し続けることが可能となります。

ナナ

static付きのローカル変数は、対象関数の中でだけ参照できればよいが、値を保持したい時に利用します。

特徴③ staticなローカル変数を使うときは必ず初期化をする必要がある!

static付きのローカル変数の初期値は初期化のみでしか設定できません。そのため、必ず初期化を行ってください。

よく間違えるのが、static付きのローカル変数を代入で初期値を与えようとするプログラムです。

これでは関数が呼ばれる度に代入処理が実施されてしまうため、staticの意味がありません。

#include <stdio.h>

void subfunc(void)
{
    static int num1;        // 未初期化
    static int num2 = 10;   // 初期化

    num1 = 5;   //  代入で初期値を与えようとする

    num1++;
    num2++;

    printf("num1:%d num2:%d\n", num1, num2);
}

int main(void)
{
    subfunc();
    subfunc();

    return 0;
}

このプログラムを動かすとnum1は毎回「6」になってしまいます。

num1:6 num2:11
num1:6 num2:12
ナナ

static付きのローカル変数の初期化は1度しか実施されません。関数が何回呼ばれても初期化は最初の1度だけしかされないのです。