質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

Q&A

解決済

5回答

1677閲覧

分割コンパイルでエラーが出ます

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

コンパイルエラー

コンパイルのフェーズで生成されるエラーです。よく無効なシンタックスやタイプが含まれているとき発生します。

0グッド

0クリップ

投稿2020/03/01 06:07

編集2020/03/02 13:17

インクルードファイルは、分割コンパイルをする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ページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/03/02 06:25

とりあえず手を動かしてうまく行くかどうか試します。ヘッダファイルを作ったことがないので初体験です。
退会済みユーザー

退会済みユーザー

2020/03/02 06:35

もし外部参照宣言をヘッダに書くとしたら、どのファイルからも参照できるようにすべての関数とグローバル変数にexternを列挙したヘッダを作ることになるのでしょうか?
退会済みユーザー

退会済みユーザー

2020/03/02 06:39

例えば、復号化関数をdecode.c、多項式演算(表示など)をoplib.c、メイン関数をmain.cにすると、分割したすべてのファイルに、すべての関数とグローバル変数を外部参照宣言したoplib.hというヘッダをインクルードさせればいいのでしょうか?
退会済みユーザー

退会済みユーザー

2020/03/02 06:52

なるほど、わかりやすい例ですね。やってみます。
kazuma-s

2020/03/02 07:06 編集

decode.c で定義した関数の宣言を decode.h に書き、 oplib.c で定義した関数の宣言を oplib.h に書きます。 main.c でそれらの関数を呼び出すのだったら、main.c に #include "decode.h" #include "oplib.h" を書きます。 decode.c が oplib.c の関数を呼び出す必要があるのなら、 decode.c に #include "oplib.h" を書きます。
退会済みユーザー

退会済みユーザー

2020/03/02 07:06

ありがとうございます。各ファイルごとにヘッダを作るのですね?
退会済みユーザー

退会済みユーザー

2020/03/02 07:07

飲み込みが悪くてすみません。苦労してC言語を覚えたので。大学ではプログラミングが苦手で、3回も再履修しました。必須科目なので留年したくらいです。
kazuma-s

2020/03/02 07:14

各ファイルごとに作ってもよいし、ひとつのファイル hoge.h に decode.c と oplib.c の 関数宣言を書いて、main.c に #include "hoge.h" を書くだけで済ますやり方もあります。 decode.c が oplib.c の関数を呼び出すときも、 decode.c に #include "hoge.h" を書くことで問題ありません。
退会済みユーザー

退会済みユーザー

2020/03/02 07:20

じゃあヘッダは一つにまとめますね。過去に3回位プログラマーをやりましたがいずれも首でしたw
guest

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
rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/03/02 04:12 編集

いつも親切にありがとうございます。今日は体調が悪いので後日直したいと思います。 あのプログラムでやっていることは、誤り訂正符号の復号なんですが、あまりいい本が日本語ではありません。私のGitHubには、はるか昔に実装した楕円曲線暗号もECDSAもあるので、興味があれば見てほしいです。あと、グローバル変数としてゼフ対数表(gf)を共有して計算に使っているのですが、それもグローバル変数として扱ってもいいのでしょうか? 私も基礎理論はよく理解してないのですが、オックスフォード出版局から出されている教科書や、ネットで拾った論文などを参照してコードを書きました。数理工学のようなちょっと専門的な分野なので、何かそのへんでレビュアーの理解の妨げになっている部分があるのなら、どのようなことをすればいいのか知りたいです。例えばあのプログラムの基礎となっている数学は、有限体という数学からの物体X(素数や素数のべき乗で割った剰余の集合は四則演算ができる)を係数に持つ一変数多項式を計算しています。 また、下手に分割すると関数が見つからないとエラーになるので、関数の共有の仕方も知りたいです。
fana

2020/03/02 05:12 編集

「コードを書く側の人が」楽をする方法なのでしょうけど, //読み手にとっては「謎の」define群 #define ALPHA #define BLAVO #define CHARLIE ... #include "A.h" #include "B.h" #inlucde "C.h" ... みたいなのは何か嫌な気がします.(ただの個人的な感想ですが)
退会済みユーザー

退会済みユーザー

2020/03/02 05:19

そうですね。そう思います。今まで自分しか見てなかったので、人に見せるときは意識しないといけないですね。がんばります。
fana

2020/03/02 05:30

A.hやB.hを他者に提供したとき,それを使おうとした人が #define ALPHA //こいつはA.hに関する仕掛けのつもり #include "A.h" #define BLAVO //こいつはB.hに関する仕掛けのつもり #include "B.h" とか書いてエラー食らって怒鳴り込んできそう.
退会済みユーザー

退会済みユーザー

2020/03/02 05:35 編集

長い目で見てやってください。今日はWallオプションで警告のでる、いらない変数をちまちま取ってます。
fana

2020/03/02 06:18

(応答されている(?)内容がよくわかりませんが) この回答の方法には,「利点」と「それを享受する際に気を付ける点」とが存在すると思うので, この書き方を採用されている(?)のであれば,後者側にどんな要素があるのかを先に理解しておいた方が良いように思います. (状況次第では #undef EXTERN とかしとかないとやばいかもなぁ…みたく,自分で考えながら取り込まないと.)
退会済みユーザー

退会済みユーザー

2020/03/02 06:51

下手に分割して動かなくなったら困るので、作業ディレクトリにサブディレクトリを作って、その中に今のファイルをコピーして分割する予定です。
退会済みユーザー

退会済みユーザー

2020/03/02 09:08

ちょっといじったらもう動かなくなりました。怖くて分割コンパイルできません。
rubato6809

2020/03/02 09:22

gitを使ってるなら、ひとつ前の版に戻してやりなおせば良いのでは。
退会済みユーザー

退会済みユーザー

2020/03/02 09:52 編集

そうしました。今は元通りです。 https://www.amazon.co.jp/Head-First-%E2%80%95%E9%A0%AD%E3%81%A8%E3%81%8B%E3%82%89%E3%81%A0%E3%81%A7%E8%A6%9A%E3%81%88%E3%82%8BC%E3%81%AE%E5%9F%BA%E6%9C%AC-David-Griffiths/dp/4873116090/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=9784873116099&qid=1583141056&sr=8-1 これを持っているので基本的な考えを勉強します。分割コンパイルが難しいのは、ファイルを分けた時、同時にコンパイルしてやれば、他の関数定義ファイルから関数を持ってきて共有することが出来ないことですね。以前から分割コンパイルを試したことがあるのですが、なかなか出来なくて諦めてしまいました。それでインクルードしてごまかすようになったのです。分割コンパイルの問題は、他で使用している同じ名前の別の方の変数が出てくると、リンクするときに重複しているとエラーがでることです。これが一つのファイルならこういうことは起きません。
退会済みユーザー

退会済みユーザー

2020/03/02 09:40

楕円曲線暗号も作ってあるので興味があれば見てください。
rubato6809

2020/03/02 13:55 編集

手元では oplib.c も obenor.c も、lu.c と chash.c (とリネームした)との分割コンパイルが通っています。ミニマムなMakefileもあります。コードはかなり私流儀に書き変えてしまってます。私が調査・リファクタリングする時はコーディングスタイルから書き変えるので、悪しからず。 ただ、あくまでプログラムのソースコードとして扱っていて、暗号アルゴリズムの中には入っていけてないです(苦笑)。
退会済みユーザー

退会済みユーザー

2020/03/02 23:27

自分にはCよりもっと適した言語があるように思うのですが、どう思いますか? どんな言語なら私にあっていますか?今のままCでもいいのでしょうか?
退会済みユーザー

退会済みユーザー

2020/03/02 23:42

設計思想の異なる言語を覚えるのは苦痛なので、今のままでレベルアップをするべきでしょうか?
rubato6809

2020/03/03 00:51

> Cよりもっと適した言語があるように思う それはありうるでしょう。その問いは私の狭い知見に頼るより、目的・条件を具体的に示して新たな問いを立てたらいかがでしょうか。たとえば「プログラミング言語」タブとか。
退会済みユーザー

退会済みユーザー

2020/03/03 10:55

暗号に関しては組み込み環境で使えるようにとcでかく必要性を感じますが、JavaScriptでも同じようなことができると思います。他の言語はほとんど知りません。
guest

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
thkana

総合スコア7659

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuma-s

2020/03/01 15:45

> これら2つは、ファイル単位のコンパイルではそれぞれに > int a; > があるというとことなので結果として変数aの実体がそれぞれに生成され、これらをリンクしようとすればエラーになります。」 実際に sample1.c と sample2.c をコンパイルしてリンクしてみてください。 エラーになりません。 その説明は C++ では正しいのですが、C には、規格書の 「6.9.2 外部オブジェクト定義」に「仮定義(tentative definition)」というのがあるからです。
thkana

2020/03/02 09:27

あぁ、そういうのありましたね。積極的に使ったことがなかったので忘れてました... というか、仮定義って積極的に使うもの?
退会済みユーザー

退会済みユーザー

2020/03/02 11:10 編集

やっと2つのファイルに分割できました。大事なのはそれぞれが自己完結していること。特にグローバル変数を複数のファイルの関数で使っている場合、それぞれのファイルにヘッダを読み込むとリンクするときに重複しているとエラーが出ます。グローバル変数の副作用とでも言うのでしょうか?グローバル変数をヘッダに入れた場合、そのグローバル変数を使った関数は全て1つのファイルに挿れなければ面倒なことに鳴る感じです。
thkana

2020/03/02 12:45

> それぞれのファイルにヘッダを読み込むとリンクするときに重複しているとエラーが出ます 上記回答を読んで頂いた上でのお話でしょうか。
退会済みユーザー

退会済みユーザー

2020/03/02 13:13 編集

すみません、コンパイラの動作と関係があるのかもしれないです。頭では理解できないので試行錯誤しています。とりあえずエラーや暗黙的なと警告が出たら、その都度extern宣言をして、外部にあるとコンパイラをなだめれば無事コンパイルして動作するように見えるのですが、これであっているでしょうか?変数の場合はうまく行きますが、これが独自定義の構造体を引数にもつ関数だと、その定義を読み込まないとエラーになり読み込むと重複してリンクできないです。何だかファイルがネストした感じになって理解できません。
thkana

2020/03/02 13:44

> とりあえずエラーや暗黙的なと警告が出たら、その都度extern宣言をして、外部にあるとコンパイラをなだめれば無事コンパイルして動作するように見える とてもとても良くないやり方です。「動作するように見える」のがそれで正しいとどうして言えるのでしょう? 他の言語ならともかく、Cですよ。一見動いているように見えて、実は破綻しているなんてことはザラにあるでしょう? プログラミングは「おまじないを唱えると動く魔法」ではないのです。
guest

0

インクルードファイルは、分割コンパイルをするCのソースごとに書く必要があるのでしょうか?

はい、その通りです。型や関数プロトタイプの宣言は、ファイルごとに必要となります。

投稿2020/03/01 06:26

maisumakun

総合スコア145208

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/03/01 06:34

ありがとうございます。この辺はプログラミングのテクニックの一つのようなので、自分のようなほぼ未経験の人間にはよくわからないことが多いです。プログラミングスタイルとでも言うべきものでしょうか。
maisumakun

2020/03/01 06:37 編集

> プログラミングスタイルとでも言うべきものでしょうか。 スタイルの問題ではありません。必要なヘッダファイルがないとコンパイルに失敗します。
退会済みユーザー

退会済みユーザー

2020/03/01 06:45

自分は分割コンパイルをしたことがありません。ネットにあるサンプルも必要な関数が全て入っているファイルを選びます。この辺は訓練する必要があります。
guest

0

グローバル変数は1箇所だけで定義して、あとのファイルにはextern付きで宣言しておきます
一般的に、そういうextern付きの宣言をまとめてヘッダファイルに書いておいて、そのヘッダファイルを全ソースでインクルードするようにします

投稿2020/03/01 06:12

y_waiwai

総合スコア87800

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/03/01 06:14

externが何だかわかりませんがやってみます。ありがとうございます。
y_waiwai

2020/03/01 06:18

「C言語 extern」でぐぐると詳しい解説出てきますよ
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問