プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

C言語でファイル間で変数を共有する(グローバル変数)

組み込みエンジニアなのでバリバリC言語を書いています

初めてC言語を学んだ時になかなか情報を見つけられなくて苦労したテクニックを少しずつ紹介したいと思います。
C++でも同じはずです。

例1

単一ファイルで変数を共有したいときは、関数の外で宣言します。
これによって変数varのスコープはこのmain.cファイル全体となります。

//main.c
#include <stdio.h>

int var;
int func()
{
  var += 1;
  return 0;
}

int main()
{
  var = 2;
  func();
  printf("%d", var);  //  3

  return 0;
}

例2

次に、変数を複数のファイルで共有したい場合があると、以下のように書きたくなります。

//main.c
#include <stdio.h>
#include "sub.c"

int var1;
int func()
{
  var1 += 1;
  return 0;
}

int main()
{
  var1 = 2;
  printf("%d\n", sub());
  var1 = 3;
  printf("%d\n", sub());

  return 0;
}
//sub.c
int var1;  //  Error
int sub()
{
  int a = 1;
  int c = a + var1;
  return c;
}

しかし、以下のように「言が重複している」とErrorになります。

sub.c:1:16: note: 'int var1' previously declared here
int var1;

では、2つめの宣言を削除すると「宣言されていない」というErrorになります。

sub.c:6:15: error: 'var1' was not declared in this scope
   int c = a + var1;

この解決方法は、sub.cでの変数定義にexternを付加します。
これで、この変数は「他の場所で定義されている変数である」と宣言することになります。

//sub.c
extern int var1;

ここで、逆にmain.cのほうにexternを付加してもErrorになります。

undefined reference to `WinMain'
collect2.exe: error: ld returned 1 exit status

これは、(おそらく)C言語では一般にmain.cからコンパイルされるため、その変数が最初に出てくるときにexternを付加して、「他の場所で定義されている変数である」と宣言すると「宣言されてない」というErrorになっているため、と考えられます。

例3

3つ以上のファイルで変数を共有する場合も、初出の時以外にexternを付ければよいようです。

//main.c
#include <stdio.h>
#include "sub.c"
#include "sub2.c"

int var1;
int func()
{
  var1 += 1;
  return 0;
}

int main()
{
  var1 = 2;
  printf("%d\n", sub());
  var1 = 3;
  printf("%d\n", sub2());

  return 0;
}
//sub.c
extern int var1;

int sub()
{
  int a = 2;
  int c = a + var1;
  return c;
}
//sub2.c
extern int var1;

int sub2()
{
  int a = 4;
  int c = a + var1;
  return c;
}

ちなみに、sub()sub2()のなかでint a = xのように同じ名前の変数を定義していますが、これはそれぞれの関数の中でしか有効でないローカル変数であるためexternを付加しなくてもErrorになりません。

まとめ

C言語をでのグローバル変数の定義・宣言の仕方をまとめました。
一般に「グローバル変数はどこから参照されるているか?が分かりにくくなり、意図しない場所から変数が書き換えられるのでなるべく使わないほうがいい」といわれます。
Web上にもそのような記事はたくさん出ています。
一方、実務でもよく使われています。
これは、上記のようなリスクがあっても使い勝手がいい部分があったり、どうしてもグローバル変数として使わないと逆にスパゲッティコードになってしまう、といったことがあるからだと思います。
どこから書き換えられるかわからない、という点では大規模なコードであればある程度避けられないことのように思います。
グローバル変数を使わなくても、不揮発性メモリから読み書きしたりもあります。
そういった面でもグローバル変数を使わなければいい、というものでは無いのではないか?と思います。
そのために、VS code等でもプロジェクトファイルのグローバル検索が可能ですし、Bugにならないように何重にもDebugを行ってソフトウェアの品質を確保するようになっています。