インクルードファイルは、分割コンパイルをするCのソースごとに書く必要があるのでしょうか?
またグローバル変数を2つのファイルに書くとリンクをするときに重複しているとエラーが出て、
片方だけに書くと、書いてない方のファイルでコンパイルエラーが出ます。
グローバル変数を2つのソースコード感で共有したい場合、どうするのが正しいやり方なのでしょうか?
関数が複数のファイルで使われている場合、関数も同じようにexternするのでしょうか?
externはよくわからないので勉強します。
ちなみに分割されたファイルをインクルードするとエラーは出ません。
よろしくお願いします。
例えば、
c
1 2 3//多項式を表示する(default) 4void printpol (vec a){ 5 int i, n; 6 7 n = deg (a); 8 if (n < 0) 9 { 10 printf ("baka\n"); 11 exit (1); 12 } 13 14 15 for (i = n; i > -1; i--) 16 { 17 if (a.x[i] > 0) 18 { 19 printf ("%u", a.x[i]); 20 if (i > 0) 21 printf ("x^%d", i); 22 if (i > 0) 23 printf ("+"); 24 } 25 } 26 // printf("\n"); 27 28 return; 29}
のような関数を、それが記述されていたファイルからひとつだけ分離したような場合どのようにすればこの分離したファイルを参照してコンパイルできるのかがわかりません。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
file1.c、file2.c、file3.c、file4.c の 4つのファイルに分割したとします。
file4.c に、他のファイルから参照してほしい変数 int var4; と
他のファイルから呼び出してほしい関数 void func4(int n) { ... } があるとします。
他のファイルに
extern int var4;
void func4(int);
このような宣言を書くことで、var4 と func4 にアクセスできます。でも
このような変数や関数が増えると、書かないといけないものが多くなって不便です。
そこで、file4.h というヘッダファイルを作って、その中に
extern int var4;
void func4(int);
と書いておけば、他のファイルに書くのは、#include "file4.h" だけで済みます。
これで、ヘッダファイルと extern の使い方が分かりませんか?
追記
file4.c に次のような関数と変数の定義があったとします。
void func41(int x) { ... } // 他から呼び出してほしい関数
void func42(int x) { ... } // 他から呼び出されなくてもよい関数
static void func43(int x) { ... } // 他から呼び出してほしくない関数
int var41; // 他から参照してほしい変数
int var42; // 他から参照されなくてもよい変数
static int var43; // 他から参照してほしくない変数
ヘッダファイル file4.h には
void func41(int);
extern int var41;
だけを書けばよい。
投稿2020/03/02 06:22
編集2020/03/02 06:49総合スコア8224
0
ベストアンサー
グローバル変数を2つのソースコード感で共有したい場合、どうする
先輩から教わって愛用している方法をご紹介します。
共有するグローバル変数を、ひとつのヘッダファイルに並べます。仮に global.h とします。冒頭に工夫があります(変数はテキトー)。
C
1/* global.h */ 2#ifdef MAIN 3# define EXTERN 4#else 5# define EXTERN extern 6#endif 7 8EXTERN int ndegree; 9EXTERN char filename[10]; 10EXTERN type vars; // 変数名を並べていく 11
としておき、Cファイルのひとつ(仮に main.c)だけにグローバル変数の実体を持たせます。
C
1/* main.c */ 2#define MAIN // ここだけ MAIN を定義する 3#include "global.h" // 変数の実体 int ndegree; 等が割り当てられる 4 5int main(int argc, char *argv[]) 6{
変数を共有したい、他のCファイルは global.h を単純にインクルードする。
C
1/* others.c */ 2#include "global.h" // extern int ndegree; 等が展開される 3 4void func(int num) 5{ 6 ....
グローバル変数としてゼフ対数表(gf)を共有して計算に使っているのですが、それもグローバル変数として扱ってもいいのでしょうか?
unsigned short 型の gf[8192], fg[8192] の2つですね。一般論としては、こうした特別な配列をグローバル変数とすることは、よくあります。現状のヘッダファイル 8192.h を、Cのソースコード 8192.c に変えて、そのままグローバルな配列としてコンパイルすることもあります。しかしこの件に関して、私ならそうはしません。
現在の構成は
- chash.cpp が #include "8192.h" している(chash.cpp自身は gf[], fg[] をアクセスしない)
- gf[], fg[] を読み出すコードが oplib.c (だけ)にある
そうであるなら、gf[], fg[] は oplib.c の中にあれば十分ですから、
gf[], fg[] を oplib.c 内の static な配列にしたほうが良いと思います。しかもどちらも(書き変え無い)定数テーブルのようですから const 属性もつけられます。即ち、こうします。
C
1/* 8192.h */ 2static const unsigned short gf[8192] = { 3 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 4 // 途中省略 5static const unsigned short fg[8192] = { 6 0, 1, 2, 7259, 3, 6326, 7260, 1858, 4, 925,
こうしておいて、oplib.c が 8192.h をインクルードするのが現状では最善に思えます。extern宣言は不要です。なお、obenor.c も oplib.c と同様の位置づけのコードのようですから、同じ修正を施せると思います。
投稿2020/03/02 03:39
編集2020/03/02 13:31総合スコア1382
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/02 05:12 編集
退会済みユーザー
2020/03/02 05:19
2020/03/02 05:30
退会済みユーザー
2020/03/02 05:35 編集
2020/03/02 06:18
退会済みユーザー
2020/03/02 06:51
退会済みユーザー
2020/03/02 09:08
2020/03/02 09:22
退会済みユーザー
2020/03/02 09:52 編集
退会済みユーザー
2020/03/02 09:40
2020/03/02 13:55 編集
退会済みユーザー
2020/03/02 23:27
退会済みユーザー
2020/03/02 23:42
2020/03/03 00:51
退会済みユーザー
2020/03/03 10:55
0
知らなきゃいけないことはいくつかあります。
まず、「リンカ」の働き。
Cでは、いくつかのファイルに分割してコンパイルというのが出来ますが、これはなにかというとつまり「変数や関数の実体が他にあることを'信じて'、最低限の情報を元にコンパイル作業をすすめる。実体が同じファイル内に無いなら、その名前が未解決であることをマークしておく。
実行ファイルを生成しようとするなら、いくつかのファイルにある「未解決」のシンボルを他のファイルから探して、未解決であったものを解決します。これがリンカのやっていること。
なので、変数や関数の実体が見つからなければエラーになりますし、変数や関数の実体が2つ以上見つかってしまったら、どれと未解決の部分を結びつければいいのかわからないのでこれもエラーになります。
次に、「インクルードファイル」というものはなんだか考えて/調べてみてください。
JIS X3010でググれば(Cとしてはちょっと古いですけれど)JISの規格書がみつかります。
6.10.2項に
include < h文字列 > 改行
は,処理系定義の場所を順に探索して,<区切り記号と>区切り記号の間で指定した文字列で一意に決まる
ヘッダを見つけ,そのヘッダの内容全体でこの指令を置き換える
とあります。(h文字列ってのは、'>'以外のファイル名に使える文字の集合、ととりあえず考えておいてください。また、#include "q文字列"の場合はq文字列とは"以外のファイル名に使える文字の集合、です)
つまり、#include命令のある行を、ファイルの中身で置き換えて、以降のコンパイル作業をすすめるということです。
C
1//sample.h 2int a;
C
1//sample1.c 2#include "sample.h" 3int main(void){ 4 return 0; 5}
これは#includeが展開されると
C
1//sample1.c 2//sample.h 3int a; 4int main(void){ 5 return 0; 6}
となってコンパイルが進行します。
一方、
C
1//sample2.c 2#include "sample.h"
とというファイルがあればこれは、
C
1//sample2.c 2//sample.h 3int a;
となります。
これら2つは、ファイル単位のコンパイルではそれぞれに
int a;
があるというとことなので結果として変数aの実体がそれぞれに生成され、これらをリンクしようとすればエラーになります。
なので、インクルードするヘッダファイルの中では実体を生成しないように指示し、しかしどこかのソース中では実体を生成するようにする必要があります。そのためのキーワードがextern。「外部参照」つまり、その宣言ではないどこかで実体が生成されるので、その場では実体を作らないことを指示します。
C
1//sample.h 2extern int a;
C
1//sample1.c 2#include "sample.h" 3int main(void){ 4 return 0; 5}
C
1//sample2.c 2#include "sample.h" 3int a;
と、smaple1.cは
C
1//sample1.c 2extern int a; //実体は生成されない 3int main(void){ 4 return 0; 5}
こうすると、sample2.cの方は
C
1//sample2.c 2extern int a; //ここでは実体は生成されない 3int a;//ここで実体が生成される
となり、いくつかのファイルを結合する時に実体が一つしかない、ということで正しくリンクが行われます。
関数の場合は、ヘッダには「プロトタイプ(関数原型)宣言」を記述し、ソースに定義を記述するのが普通です。
int func(int) ; //プロトタイプ宣言。返り値の型、関数名、引数の型リストのみ(仮の引数名があっても構わない)等が記述され、関数の()のあとは;で終端になる。
int func(int a){ //関数の定義。返り値の型、関数名、引数のリスト(引数名必須)が記述され、関数の()に続いて{ }に囲んで関数の内容を記述する
return a*2;
}
プロトタイプ宣言はそもそもがextern属性ということになっているのでexternを記述する必要はありません。
投稿2020/03/01 14:43
編集2020/03/02 12:50総合スコア7703
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/01 15:45
2020/03/02 09:27
退会済みユーザー
2020/03/02 11:10 編集
2020/03/02 12:45
退会済みユーザー
2020/03/02 13:13 編集
2020/03/02 13:44
0
インクルードファイルは、分割コンパイルをするCのソースごとに書く必要があるのでしょうか?
はい、その通りです。型や関数プロトタイプの宣言は、ファイルごとに必要となります。
投稿2020/03/01 06:26
総合スコア146063
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/03/01 06:34
2020/03/01 06:37 編集
退会済みユーザー
2020/03/01 06:45
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2020/03/02 06:25
退会済みユーザー
2020/03/02 06:35
退会済みユーザー
2020/03/02 06:39
退会済みユーザー
2020/03/02 06:52
2020/03/02 07:06 編集
退会済みユーザー
2020/03/02 07:06
退会済みユーザー
2020/03/02 07:07
2020/03/02 07:14
退会済みユーザー
2020/03/02 07:20