memorandums

日々の生活で問題解決したこと、知ってよかったことなどを自分が思い出すために記録しています。

C言語で他ファイルから参照したければstaticはつけちゃダメよダメダメ(個人メモ)

サークルでシステム構築中の学生さんからプログラムについての相談があり、昨夜、一緒にごにょごにょやっていました。

こちらに赴任してからはoFでC++を触ることはあってもC言語はなかなか触る機会がなく。。。昔は仕事で使っていたのですが。。。忘れていました。悲しいですがしかたがありません。

とりあえず、逃げの方法でしのいでもらってのですが納得いかず。MacにVS2017をいれてC++環境を作り調べました。

まず、学生さんが持ち込んだ問題は以下。

  • main.cpp
#include "stdafx.h"

extern void a();

int main()
{
    a();
    return 0;
}
  • Source.cpp
#include "stdafx.h"

static void a() {
    printf("A");
}  

これをビルドすると以下のリンカーエラーが出ます。わかる人であればビルドする前にわかるんでしょうね。。。

f:id:ke_takahashi:20170622134913p:plain

a()が見えないというエラーです。その学生さんも私もLNK2019でぐぐったりしてみたわけですが、外部DLLを使用するネタとか、また、extern "C"をつけろというメモとかありまして。。。どれも対策にはならなかったんですね。

関数a() にstaticキーワードをつけているわけですが、C言語では見慣れないキーワードではありませんし、スコープの意識が高いんだな。。。くらいに見過ごしていました。

関数a() を使用するmain.cppではextern宣言していますのでリンクできるはずだ、という思い込みもありました。また、学生さんが持ち込んだソースには他に似たような関数があり、それはリンクに成功していることも盲点になったのかと思います。

結論は、このstaticキーワードがダメ、ということです。staticキーワードをつけることによって、グローバル変数や関数がそのファイル中のみ参照できるようにすることができます。これをファイルスコープというそうで。オブジェクト指向言語カプセル化と同じ効果があります。

ファイルスコープはコンパイル時だけではなく、リンク時にも有効ということがわかっていなかった。。。のだと思います。そりゃそうですね。せっかくファイルスコープで宣言した識別子がリンク時に参照できてしまうなら意味ないですものね。。。

手元に昨日、学生さんが持ち込んだソースがないので解決方法として有効だったかわかりませんが、staticを削除して他ファイルのソースに公開することを宣言すればリンクエラーはなくなります。私の理解の中では当然の結果と思います。

  • Source.cpp
#include "stdafx.h"

void a() {
    printf("A");
}  

すぐに忘れてしまいそうなので、とりあえずメモしておきたいと思います。公開したい関数にはstaticはつけない、他ファイルのソースに非公開にしたい関数にはstaticをつける、ですね。

上記について、間違いがありましたらお手数ですがご指摘いただけると幸いです。