🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

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

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

Q&A

解決済

6回答

3309閲覧

プログラムの関数を指定したファイルに分割する方法

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

0グッド

0クリップ

投稿2021/03/26 00:59

編集2021/03/26 04:37

私は今1週間で身につくc言語の基本というのを勉強しています。
回答がないため質問させていただきます。
以下のプログラムは、じゃんけんゲームです。このプログラムの関数を、以下に指定したファイルに分割しなさい。なお、グローバル変数は、表の指定したファイルの中で定義すること。

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <time.h> 4 5// コンピュータの手 6int player = 0; 7// プレイヤーの手 8int computer = 0; 9char results[3][16] = {"グー","チョキ","パー"}; 10// 乱数の初期化 11void init(); 12// プレイヤーの手値設定 13void setPlayer(int); 14// コンピュータの手設定 15void setComputer(); 16// 判定 17void Judge(); 18 19void main(){ 20 int num; 21 init(); 22 printf("じゃんけんゲーム¥n"); 23 printf("0:グー、1:チョキ、2:パー¥n"); 24 while(1){ 25 printf("あなたの手は?(0-2):"); 26 scanf("%d",&num); 27 if(num < 0 || num > 2){ 28 printf("終了します¥n"); 29 break; 30 }else{ 31 setPlayer(num); // 自分の手設定 32 setComputer(); // コンピュータの手設定 33 Judge(); // 判定 34 } 35 } 36} 37 38// 初期化 39void init() 40{ 41 srand((unsigned) time(NULL)); 42} 43// 指定した範囲の乱数を発生 44void setPlayer(int num) 45{ 46 player = num; 47} 48// 指定した範囲の乱数を発生 49void setComputer() 50{ 51 computer = rand() % 3; 52} 53 54void Judge(){ 55 printf("プレイヤー:%s¥n",results[player]); 56 printf("コンピューター:%s¥n",results[computer]); 57 if((player == 0 && computer == 1) 58 || (player == 1 && computer == 2) 59 || (player == 2 && player == 0)){ 60 printf("プレイヤーの勝ち¥n"); 61 }else if((player == 1 && computer == 0) 62 || (player == 2 && computer == 1) 63 || (player == 0 && computer == 2)){ 64 printf("コンピュータの勝ち¥n"); 65 }else{ 66 printf("あいこです。¥n"); 67 } 68}

|ヘッダーファイル|ソースファイル|関数|グローバル変数| |:--|:--|:--|:--| |logic.h|logic.c|init(),setPlayer(),setComputer()|player,computer| |showResult.h|showResult.c|Judge()|results|

c

1#include <stdio.h> 2#include <string.h> 3#include "logic.h" 4 5int init=0; 6int setPlayer=0; 7int setComputer=0; 8 9char results[3][16] = {"グー","チョキ","パー"}; 10void init(); 11void setPlayer(int); 12void setComputer(); 13void Judge(); 14 15void main(){ 16 int num; 17 init(); 18 printf("じゃんけんゲーム\n"); 19 printf("0:グー、1:チョキ、2:パー\n"); 20 while(1){ 21 printf("あなたの手は?(0-2):"); 22 scanf("%d",&num); 23 if(num < 0 || num > 2){ 24 printf("終了します\n"); 25 break; 26 }else{ 27 setPlayer(num); 28 setComputer(); 29 Judge(); 30 } 31 } 32}

c

1#include <stdio.h> 2#include <string.h> 3#include "showResult.c" 4 5int Judge=0; 6void init() 7{ 8 srand((unsigned) time(NULL)); 9} 10void setPlayer(int num) 11{ 12 player = num; 13} 14void setComputer() 15{ 16 computer = rand() % 3; 17} 18 19void Judge(){ 20 printf("プレイヤー:%s\n",results[player]); 21 printf("コンピューター:%s\n",results[computer]); 22 if((player == 0 && computer == 1) 23 || (player == 1 && computer == 2) 24 || (player == 2 && player == 0)){ 25 printf("プレイヤーの勝ち\n"); 26 }else if((player == 1 && computer == 0) 27 || (player == 2 && computer == 1) 28 || (player == 0 && computer == 2)){ 29 printf("コンピュータの勝ち\n"); 30 }else{ 31 printf("あいこです。\n"); 32 } 33}

私はこのように書きました。
ファイル分割をする方法がいまいち分からないので教えていただきたいです。
すみません。
ソースファイルが抜けてますがまだかけてません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

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

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

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

fana

2021/03/26 02:41

特にファイル名に指定がある話をする場合においては, 提示する各コードがどのファイル名に対応するものなのかを示すべきではないでしょうか.
dodox86

2021/03/26 03:23

>@質問者さん 先のコメント、fanaさんの指摘に同意ですが、質問文中で使えるマークダウン記法を使って分かり易くファイルとコードの配置の関係を表にするのであれば、例えば以下のようになります。(質問文中に貼って、プレビューで確認してみてください) |ヘッダーファイル|ソースファイル|関数|グローバル変数| |:--|:--|:--|:--| |logic.h|logic.c|init(),setPlayer(),setComputer()|player.computer| |showResult.h|showResult.c|Judge()|results|
退会済みユーザー

退会済みユーザー

2021/03/26 04:33

確かにそうですね。参考になります。 ご指摘ありがとうございます。
Zuishin

2021/03/26 09:43

回答と回答者のスコアも見た方がいいんじゃないですかね。酔っ払いの回答を一番信用してるみたいだけど。
dodox86

2021/03/27 00:34

> @質問者さん 私の先のコメントはマークダウン記法で表を描くときの書き方の例だったのですが、うまく表示できていないようです。 (こちらでは回答欄にコピー&ペーストで正しく表形式になることをプレビューで確認したのですが) うまくできないようであれば、無理して使っていただかなくてもよいです。閲覧した方に伝わらないのが一番よくないので。尚、以下の過去質問・回答も参考になるかもしれません。 [teratailでのMarkdownの詳細を知りたい!!] https://teratail.com/questions/62095
dodox86

2021/03/30 00:03

質問がなかなか閉じないなと思って質問者さんの過去質問を見直したのですけど、先のJavaに関する質問を解決してから取り組めばよいのに。と思いました。 [インターフェースの実装] https://teratail.com/questions/329173 大きなお世話でしょうけど、初心で複数の言語の勉強はなかなか身に付かないと思いますよ。
guest

回答6

0

課題として

text

1|ヘッダーファイル |ソースファイル| 関数 |グローバル変数 | 2|:-- |:-- |:-- |:-- | 3|logic.h |logic.c |init(),setPlayer(),setComputer() |player,computer| 4|showResult.h |showResult.c |Judge() |results |

と書いてあるということは、主プログラム(たとえばrps.c)とは別に、
logic.h、logic.c、showResult.h、showResult.cを作成しなさいと指示されているという意味です。

そして、rps.c はlogic.hとshowResult.hと必要最小限の.hファイルをインクルードします。
logic.cはlogic.hとと必要最小限の.hファイルをインクルードします。
showResult.cはshowResult.hとと必要最小限の.hファイルをインクルードします。
というように5個のファイルに分割してください。

投稿2021/03/26 07:47

ppaul

総合スコア24670

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

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

退会済みユーザー

退会済みユーザー

2021/04/02 00:05

コメントありがとうございます。 参考にさせていただきます。
guest

0

いくつか返信はされていますが解決にならないのですね。じゃあ、なんで分割するときにそんなように書くのか、的な方向でだらだら書いてみますか。
(分割自体は、ソースファイルごとに主題を明確にするため、ということで納得しているものとします)

まず、C言語のコンパイルの原則。(実はウソがありますが...)

「プログラム中にコンパイラが知らない言葉(シンボル)が出てきてはいけない」

C言語には、規格で決められている「予約語」があります。ifとかforとか。それと、演算子やなんかも予め決められています。#includeとか#defineとかのプリプロセッサ命令も予め知っています。

また、変数や関数の定義、宣言によって新しいシンボルの意味をコンパイラに教えることができます。型宣言とかマクロ定義もできます。
ソースコードを上から順に読んでいって、コンパイラが知らない言葉に出会わない。これが、エラーにならない原則の一つです。出てくる語は、全て予約語であるか、あるいはどこかで宣言・定義されている(教えられている)語であること。

ここでちょっと脇道に逸れて、'#include'の役割について。漠然と「ライブラリの関数を使うときには#includeする」なんて言葉だけ覚えているかも知れませんが具体的に「なにをする」のでしょう。
#includeは、(一部そうでない場合もありますが)指定されたファイルをその部分に取り込んでしまう働きをします。
極端な例を示せば

text

1// inc.txt 2 printf(

C

1//main.c 2#include <stdio.h> 3int main(void){ 4#include "inc.txt" 5 "Hello world.\n"); 6 return 0; 7}

main.cをコンパイラにかけたなら、コンパイルの最初の「プリプロセス(前処理)」の段階で#includeで指定したファイルが展開されて

C

1//stdio.hの中身をここに展開(略...というか書ききれない) 2int main(void){ 3// inc.txt 4 printf( 5 "Hello world.\n"); 6 return 0; 7}

となります。もちろん、#includeでこんな使い方をすることはまずないでしょう。こんな形で別のファイルにする意味がないですから。
じゃあ、#includeはどう使うか...他のソースファイルで生成される変数や関数の宣言、マクロや型の定義などを記述したファイルを作っておいて、それを#includeします。つまり、それらの宣言や定義がソースコード中に書かれていることになります。宣言や定義を教えたら、以降はそれらの変数や関数、マクロや型をソースコード中で使えるようになります。(ということでもとの話に合流)

C

1//func.h 2void func(void);

C

1//main.c 2#include "func.h" 3int main(void){ 4 func(); //func.hを取り込むことでfuncという関数の素性を教えられているので適切なコードを生成できる 5 return 0; 6}

(ここが前述の「ウソ」があるところ。Cコンパイラは歴史的経緯から、知らない関数名に出会うととりあえず記述している名前で、int型を返し、記述されている引数を受け取る関数と仮定してエラーにせずにコンパイルを進めてしまいます。ただし、その仮定が正しいとは限りません。プロトタイプなんてものがなかった過去はともかく、いまは仮定に頼るような処理はよくない、と言い切れますので「事前に知っている」を原則と考えてください)

なお。これまでの話からすると実は

C

1//func.c 2void func(void){ 3}

C

1//main.c 2#include "func.c" //func.hではなく 3int main(void){ 4 func(); //func.hを取り込むことでfuncという関数の素性を教えられているので適切なコードを生成できる 5 return 0; 6}

という書き方も成立してしまうことはわかるかと思います。
しかし、関数や変数の定義はプログラム全体で唯一でなければいけない、ということがあります(これも変数については「仮定義」なんていうモノがありますが...原則としてそう考えてください)。こういう書き方をしていると、あちこちで使われるような関数はあちこちで#includeされて、あちこちに定義がある、ということが起こるかも知れません。ので、こういうことはしません。

もう一つ、#includeで宣言を取り込む意味があります。
先程から「宣言・定義」などと書いています。「宣言」というのはその関数とか変数とかが「こういう名前で、こういう性質を持っています」という記述。一方「定義」は、宣言の内容に加えて関数なら「こういう振る舞いをします」だったり変数なら「そういう変数をメモリ上に確保します」という命令です。定義があれば宣言は必要ではない(その情報は全て含まれている)ので、質問の例で言えば関数を定義しているlogic.cやshowRersult.cファイルにおいて、その宣言を記述しているlogic.hやshowResult.hを#includeすることは必須ではありません。
でも...定義は、「宣言の内容に加えて」と書きました。Cは古い言語なので宣言と定義が重複した情報を含んでいて、それを人間が管理しなきゃいけないのですが、人間ゆえこの宣言と定義が矛盾するという間違いが起こる可能性があります。ここで、宣言と定義が両方あると、情報の突き合わせが行われるので矛盾が発見されます。このため、定義を行っているソースで宣言を行っているヘッダを#includeすることはメリットがあります。

以上が、分割コンパイルをする際にヘッダを切り出し、#includeでそのヘッダを取り込む意味ということになるかと思います。(長過ぎる?)


どうでもいいですが、

1週間で身につく

そんなわけないので、何より先に胡散臭さを感じてしまうタイトルです。

投稿2021/03/29 23:33

thkana

総合スコア7703

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

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

0

ベストアンサー

  • 元のファイルにあるinclude をすべてのファイルに記載する。
  • 表に従って、ファイルに記載する
  • 表にないmainは別ファイル(main.c)に記載する。
  • showResult.cでは、logic.cのplayer変数を使用しているので、logic.hをインクルードする。
  • main.cには、作ったlogic.hとshowResult.hをincludeする
  • グローバル変数は、ヘッダファイルでは、=0は記載しない。

という感じで分割できるのではないでしょうか。

別な説明だと、こんな感じになると思います。

  • 関数、変数をファイルに分割する。
  • それぞれのヘッダを作成して、定義だけ書く。

-- int xxx(); //関数定義
-- int yyy; //グローバル変数定義

  • 関数や、グローバル変数を参照しているファイルがあったら、ヘッダをincludeする。

-- 今回だと、showResult.cで、logic.hをインクルードする。
-- main.cで、logic.hとshowResult.hをインクルードする。

C

1//logic.h 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6 7// コンピュータの手 8int player; 9// プレイヤーの手 10int computer; 11 12void init(); 13// プレイヤーの手値設定 14void setPlayer(int); 15// コンピュータの手設定 16void setComputer(); 17

C

1//logic.c 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6 7// コンピュータの手 8int player = 0; 9// プレイヤーの手 10int computer = 0; 11 12 13// 初期化 14void init() 15{ 16 srand((unsigned) time(NULL)); 17} 18// 指定した範囲の乱数を発生 19void setPlayer(int num) 20{ 21 player = num; 22} 23// 指定した範囲の乱数を発生 24void setComputer() 25{ 26 computer = rand() % 3; 27} 28

C

1//showResult.h 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6// 判定 7void Judge(); 8

C

1//showwResult.c 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6#include "logic.h" 7 8char results[3][16] = {"グー","チョキ","パー"}; 9 10 11void Judge(){ 12 printf("プレイヤー:%s¥n",results[player]); 13 printf("コンピューター:%s¥n",results[computer]); 14 if((player == 0 && computer == 1) 15 || (player == 1 && computer == 2) 16 || (player == 2 && player == 0)){ 17 printf("プレイヤーの勝ち¥n"); 18 }else if((player == 1 && computer == 0) 19 || (player == 2 && computer == 1) 20 || (player == 0 && computer == 2)){ 21 printf("コンピュータの勝ち¥n"); 22 }else{ 23 printf("あいこです。¥n"); 24 } 25}

C

1//main.c 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6 7#include "logic.h" 8#include "showResult.h" 9 10 11void main(){ 12 int num; 13 init(); 14 printf("じゃんけんゲーム¥n"); 15 printf("0:グー、1:チョキ、2:パー¥n"); 16 while(1){ 17 printf("あなたの手は?(0-2):"); 18 scanf("%d",&num); 19 if(num < 0 || num > 2){ 20 printf("終了します¥n"); 21 break; 22 }else{ 23 setPlayer(num); // 自分の手設定 24 setComputer(); // コンピュータの手設定 25 Judge(); // 判定 26 } 27 } 28} 29

sh

1gcc -c logic.c 2gcc -c showResult.c 3gcc -c main.c 4gcc main.o logic.o showResult.o 5

投稿2021/03/28 08:14

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2021/03/28 23:59

丁寧な回答ありがとうございます。 すごく参考になります。 最後にひとつお聞きしたいのですがshというのは どのようなファイルなのですか?
退会済みユーザー

退会済みユーザー

2021/03/29 02:19

分割したファイルをlinux条等でコンパイルするためのスクリプトになりますー。
guest

0

提供するサービスごとに分割することを考えると良いかと思います。

今のままでも良いのですが、ゲームというサービスを分割するとして、例えばinitで乱数の初期化という機能としていますが、ゲームの初期化という形にして、playerやcomputerも初期化するとか、results文字列もゲームサービス固有の情報だと思いますので、ゲームサービス側に持たせ、適宜必要な情報をmain側で取得できるような機能(関数)をゲームサービス側で用意すると言うように考えていけば良いかと。

サービス利用者としてmain側からはサービスの実装はあまり見えないようにすると、実装が変わっても影響が最小限にすることができます。
グローバル変数は変更した場合の影響が大きいので、少なくともサービス側だけで参照できるようにstatic変数とし、main側で必要に応じて参照できるような機能(関数)を提供するのが良いです。

投稿2021/03/26 01:36

t_obara

総合スコア5488

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

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

退会済みユーザー

退会済みユーザー

2021/03/26 01:58

回答ありがとうございます。 すごくわかりやすい説明でなるほど、と思ったのですがまだまだ未熟なので なかなかそれをコードに書くのが難しいですが参考にさせていただきます。
guest

0

キーワード: 『分割コンパイル

なので、『C言語 分割コンパイル』とかで検索してください。

かなりヒットします。

投稿2021/03/26 06:54

BeatStar

総合スコア4962

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

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

退会済みユーザー

退会済みユーザー

2021/03/26 06:58

調べてみます。 ありがとうございます。
guest

0

showResult.h がない。
普通.cはincludeしない。

投稿2021/03/26 01:12

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2021/03/26 01:14

なるほど、まずそこを変更します。
退会済みユーザー

退会済みユーザー

2021/03/26 03:30

それとlogic側にはshowResultをincludeしないshowResult側にlogicをincludeする
退会済みユーザー

退会済みユーザー

2021/03/26 03:33

void init(); void setPlayer(int); void setComputer(); void Judge(); これらはヘッダーの中
退会済みユーザー

退会済みユーザー

2021/03/26 04:42

logic側にはlogic.h,logic.cそれぞれであってますか? showResult側もソースファイルとヘッダーファイル2つ あるのですがどちらにincludeするのか聞いてもよろしですか?
退会済みユーザー

退会済みユーザー

2021/03/26 09:36

ちょっと勘違いしてました。 課題を素直に解釈するとshowResult側にはJudge関数だけが良いと思います。 それでlogicにshowResultをincludeする。(先程は勘違いして逆のことを言ってしまいました。) そして2つのcファイルにまたがったグローバル変数を上手に使うための課題だと思います。 グローバル変数は良くないと言う主張もありましたが、この課題は複数のcファイルをまたいでグローバル変数を使う方法を学ぶ課題です。
退会済みユーザー

退会済みユーザー

2021/03/29 00:01

なるほど。 質問にも答えてくださりありがとうございます。 参考にさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問