Programming Place Plus トップページ – C言語編
以下は目次です。
ここまでに登場したプログラムは、どれも1つのソースファイルだけで完結していました。しかし、もう少し規模の大きなプログラムを作るときには、ソースファイルを複数個に分割することが一般的です。
「1つのソースファイルだけで完結していた」と書いたばかりですが、本当はこれまでのプログラムでも、複数のファイルが関わっています。第23章で少し解説した #include がカギです。
#include <stdio.h>
「#include <stdio.h>」という記述の意味は、「stdio.h というファイルの内容を、この位置に取り込め」というものです。
.h という拡張子を持つファイルを一般にヘッダファイルと呼びます。
Windows においての拡張子についての説明が「Windows編>拡張子」にあります。
C言語の標準規格としては、ヘッダファイルのことを単にヘッダと呼んでいますが、ヘッダファイルという呼び名は非常に一般的に定着しています。
【上級】標準規格では、ヘッダがファイルであることを求めていません。ヘッダファイルと呼ばないのはこのためです。また、処理系が区別できるのであれば、#include の < と > で囲まれた部分がファイル名になっている必要もありません。
大抵の開発環境には、<stdio.h> などのヘッダファイルが含まれています。普通、コンパイラメーカーが、コンパイラとセットで出荷しており、コンパイラをインストールしたときに自動的に作成されています。
このように、C言語の標準規格が、用意すべきであることを明示しているヘッダがいくつかありますが、これらのことを標準ヘッダと呼びます。標準ヘッダには、標準ライブラリ関数など、これまたやはり標準規格によって定められた「必要なもの一式」が記述されています。
標準ヘッダと、その中に含まれている機能の一覧が、「標準ライブラリのリファレンス(ヘッダ別)」にあります。
【上級】正確にいえば、<stdio.h> のようないくつかの標準ヘッダについては用意しなくてもよいとされている環境もあります。これは、フリースタンディング環境と呼ばれ、OS が存在しないような実行環境のことをいいます。一方、OS の制御下でプログラムを実行できる環境は、ホスト環境といいます。
このように、C言語の処理系は、C言語の標準規格が定めたルールに従って、標準ヘッダを用意います。そのおかげで、標準ヘッダに含まれている機能を、標準規格が定めた仕様に従うように使えば、どの処理系でも共通のソースコードがコンパイルでき、実行できるはずです。
話を戻しますが、ほとんどのプログラムは複数のファイルの連携で成り立っています。C言語では、主に .c という拡張子を持つソースファイルと、主に .h という拡張子を持つヘッダファイルを作ります。拡張子には制約はないので、他の名前を使っても構いません。
Visual Studio では、拡張子 .c に代表されるソースファイルと、拡張子 .h に代表されるヘッダファイルという名称を区別して用いていますが、C言語としては、どちらもソースファイルと呼んで構わないものです。
巨大なプロジェクトになると、ヘッダファイルを含めたソースファイルの総数が数千個、あるいはそれ以上にもなることがあります。これまでの章で何度か書いてきた、「スコープは極力狭くするべき」「分かりやすい名前を付けるべき」「部品化することを考えるべき」といったガイドラインは、巨大なプロジェクトを構築し、維持管理するために非常に重要です。
ヘッダファイルは #include を使って、その内容を取り込んで使います。#include の意味は、「指定したヘッダの内容を、この記述を書いた箇所へ取り込む」ということです。この行為をよく「ヘッダをインクルードする」と表現します。
さて、ここでは自分でヘッダファイルを作ってみましょう。拡張子に制約はありませんが、ここでは一般的なやり方に従って、.h とします。ここでは、utility.h という名前のヘッダファイルを作ります。
Visual Studio でヘッダファイルを作成する方法は、こちらのページにあります。
utility.h の内容は以下のようにします。
/* 大きい方の値を返す */
int max(int a, int b);
/* 小さい方の値を返す */
int min(int a, int b);
このヘッダファイルに記述されている内容は、関数プロトタイプだけです。通常、ヘッダファイルには関数の定義は書きません。定義は(拡張子 .c などの)ソースファイルの方に記述します。
イメージとしては、ヘッダファイルは、カタログとかメニュー表のようなものです。具体的な処理工程(実装)はヘッダファイルにはありませんが、具体的な部分を気にせずに、ヘッダファイルを経由して使わせてもらうという感覚です。
たとえば、printf関数を使うときには、<stdio.h> というカタログから printf関数を選んで使わせてもらっているということです。printf関数の具体的な定義は、stdio.h には書かれていませんが(大抵の製品では非公開です)、それを気にせずに使えています。
これまた “一般的には” ということですが、ヘッダファイルと、(拡張子 .c などの)ソースファイルを対応付けるように作るのが基本です。つまり、utility.h に対応して utility.c を作るということです。utility.c は次のようにしてみます。
#include "utility.h"
/* 大きい方の値を返す */
int max(int a, int b)
{
if( a >= b ) {
return a;
}
return b;
}
/* 小さい方の値を返す */
int min(int a, int b)
{
if( a <= b ) {
return a;
}
return b;
}
ヘッダファイルで宣言した関数の定義を記述しています。
対応するヘッダファイルがある場合は、必ず、そのヘッダファイルを1番最初にインクルードするように徹底してください。これはそうする必要がない場合もありますが、つねにこうするように書いた方が余計なトラブルを防げます。
なお、自分で用意したヘッダファイルをインクルードする場合は、<stdio.h> のように < と > で囲む形式ではなく、"" で囲む形式を使います。
【上級】両者の差は、どのディレクトリ(フォルダ)からヘッダファイルを探すかの違いですが、使い分けの基準としては、先ほどの考え方で問題ありません。
残すは、これらの関数を実際に使う側の準備です。そもそも、まだ main関数がありませんから、これだけでは実行できません。それでは、main関数を含んだ main.c を作成してみましょう。
#include <stdio.h>
#include "utility.h"
int main(void)
{
int a = 50;
int b = 100;
( "MAX: %d\n", max(a,b) );
printf( "MIN: %d\n", min(a,b) );
printf}
実行結果:
MAX: 100
MIN: 50
ここまでに用意した max関数と min関数の定義は、utility.c に書かれていますが、#include で取り込むのは utility.h の方です。すると、utility.h に記述した関数プロトタイプが取り込まれるので、max関数と min関数が見える場所に置かれます。これで、main.c から呼び出せます。
Visual Studio のような開発環境を使っているとすると、プログラムを作成した後、ビルド (build)という操作を行い、何もエラーが出なければ、「実行(Run)」して動作を確認すると思います。
Visual Studio でビルドを行う方法が、「Visual Studio編>ビルドを行う」に、実行を行う方法が「Visual Studio編>実行する」にあります。
ビルドには、プリプロセス、コンパイル、リンクといった複数の過程が含まれています。
C言語の標準規格としては、ここで取り上げるとおりの仕組みを用いることを求めてはいません。しかし、ここで取り上げる方法は、非常に一般的なものです。
コンパイルとは、ソースコードを変換して、別の言語の表現にすることぉいいます(ここでの言語というのは、プログラミング言語の話であって、日本語とか英語とかいう意味ではありません)。また、コンパイルを行うプログラムをコンパイラといいます。
Visual Studio はコンパイルもできるし、リンクもできるし、デバッグ作業もできるし、コードエディタも持っています。コンパイルは機能の1つに過ぎませんから、単にコンパイラと呼ぶのは本来は適切ではありません。Visual Studio のように、開発に必要な複数の機能をひとまとめにして提供されている環境を、統合開発環境(IDE)と呼びます。
コンピュータの世界の言語には、人間が比較的読みやすい高級言語と、人間には読みづらいがコンピュータには理解できる低級言語があります。この区別は明確なものではなく、言語1つ1つにおいて読みやすさの段階はあります。C言語は高級言語ですが、比較的、低級言語に近い言語だといわれています。
コンパイルは、高級の側に近い言語を、低級の側に近い言語へと変換します。多くの場合、機械語(マシン語)と呼ばれる、最も低級な言語へと翻訳します。コンピュータが直接的に理解できて、実行できる言語はマシン語だけだからです。
そのためプログラムは、本来なら機械語で書かないといけないのですが、「最も低級な言語」と言ったように、人間が機械語を覚えてプログラムを書くことは、ほとんど不可能に近い作業なのです。そこで、C言語のような、人間が読み書きしやすい高級言語が考案されたのです。
さて、1つのプログラムには、複数のソースファイルが含まれていることがあるので、1つ1つのファイルをコンパイルしたところで、全体像が見えないと正しく動作できません。
たとえば、先ほどのサンプルでいうと、utility.c だけをコンパイルしても、そこには main関数が含まれていませんから、実行をどこから始めればよいか分かりません。一方で、main.c だけをコンパイルしても、そこで使われている max関数や min関数の定義がありませんから、関数呼び出しを行った結果、何を実行すればいいのかわかりません。
そこで、1つ1つのソースファイルは、それぞれをオブジェクトファイルという、いわば中間的なファイルに変換しておきます。そして、1つのプログラム全体を形作るのに必要なオブジェクトファイルがそろったところで、これらを1つにまとめます。このまとめる過程のことをリンクと呼び、リンクを行うプログラムをリンカといいます。
Visual Studio ならば、ビルドを行った後、エクスプローラーでプロジェクトがあるフォルダを見ると、Debug という名前のフォルダが作成されていることが分かります。この中にある .obj という拡張子のファイルがオブジェクトファイルです。ソースファイルの名前と同じ名前で、拡張子だけが異なるファイルが作成されているはずです。
複数のオブジェクトファイルがリンクされた結果、1つの実行可能ファイルが作成されます(Windows であれば .exe という拡張子を持ちます)。この実行可能ファイルは、Windows のエクスプローラーなどから実行できる状態になっています。Visual Studio で、実行のコマンドを選択したときに実行されているのは、こうして生成された実行可能ファイルです。
Visual Studio でビルドを行うと、そのときの設定に応じて、Debugフォルダや Releaseフォルダの中に実行可能ファイルが生成されています。
ヘッダファイルを自作し、複数のファイルが連携するプログラムをつくると、多重インクルードという問題が起こることがあります。
多重インクルードは、その呼び名のとおり、1つのヘッダファイルを重複してインクルードすることで起きる問題です。#include は、指定されたファイルの中身をそっくりそのまま取り込むだけですから、同一のヘッダファイルを何度もインクルードすると、同じ宣言や定義がくりかえし登場します。
特に問題なのは、ヘッダファイルからさらに別のヘッダファイルをインクルードしているケースです。たとえば、aaa.h は bbb.h をインクルードしているときに、main.c が aaa.h と bbb.h を両方ともインクルードすると、bbb.h は 2度取り込まれます。
実際のところ、関数宣言や、extern付きのグローバル変数といったものは、重複してもエラーにはなりません。マクロ定義も、置換後の結果が同じであれば、重複しても問題ありません。しかし、#if が使われているなどして、#define の置換結果が場合によって変化するのなら、重複定義が問題になる可能性もあります。
実際にそのような問題が起きていないとしても、多重インクルードによる多重定義問題への対策はつねに取るようにすべきです。問題が起こるのは、ヘッダファイルを #include で取り込んだ側ですが、この対策を取れるのはヘッダファイルを作った側であるからです。きちんと対策されたヘッダファイルを提供することが望まれます。
多重定義問題への対策には、プリプロセスでの分岐処理が利用できます。すべてのヘッダファイルを次のように記述することで、多重インクルードによる問題を防止できます。
#ifndef MY_HEADER_H_INCLUDED
#define MY_HEADER_H_INCLUDED
// ヘッダの中身はここに書く
#endif
「MY_HEADER_H」の部分は、ヘッダファイルごとに異なる名前を使うようにします。確実に異なる名前を付けるために、ヘッダファイル自身の名前を元に命名することが多いです。
異なるディレクトリに同じ名前のファイルがある場合は、これだけではダメです。その可能性があるのなら、ディレクトリ名(グループ名)を付け足すなどの工夫が必要です)
処理系に依存しますが、「#pragma once」を使う方法もあります(第29章)
このヘッダファイルが初めてインクルードされるときには、MY_HEADER_H というマクロは定義されていない状態なので、#ifndef は真となり、内側にある記述は有効です。#ifndef の直後には、#define があり、ここで MY_HEADER_H が定義されます。
2度目以降のインクルード時には、1度目のときに MY_HEADER_H が定義されているので、#ifndef が偽となり、内側にある記述はすべて無視されます。こうして、2度目以降の取り込みは行われはするものの、#ifndef の効果によって、中身が事実上、空の状態になり、多重定義の問題は起こり得なくなります。
さて、今度は main.c、print.c、print.h という 3つのファイルを用意します。
// main.c
#include "print.h"
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum}
// print.c
#include <stdio.h>
#include "print.h"
int gLastPrintNum = 0;
void printNum(int num)
{
( "[[ %d ]]\n", num );
printf= num;
gLastPrintNum }
// print.h
#ifndef PRINT_H_INCLUDED
#define PRINT_H_INCLUDED
void printNum(int num);
#endif
実行結果:
[[ 100 ]]
[[ 200 ]]
[[ 300 ]]
printNum関数は、引数で指定された int型の整数を [[ ]] で囲んで出力します。また、もう1つの機能(?) として、最後に出力した整数をグローバル変数 gLastPrintNum に保存しています。
ここで、main.c から gLastPrintNum を使いたいとしましょう。main.c を次のように書き換えてビルドを行ってみます。
// main.c
#include <stdio.h>
#include "print.h"
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", gLastPrintNum ); // コンパイルエラー
printf}
これはコンパイルエラーになります。main.c は print.h を #include で取り込んでいますが、gLastPrintNum が宣言されているのは print.c の方なので、main.c からは可視でない(第22章)ためです。グローバル変数はファイルスコープである(第22章)ことも思い出しましょう。
それなら、main.c にも gLastPrintNum を宣言したらどうでしょう?
// main.c
#include <stdio.h>
#include "print.h"
int gLastPrintNum = 0; // リンクエラー
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", gLastPrintNum );
printf}
今度は、コンパイルの過程は成功していますが、リンクの過程でエラーになります。このエラーは、重複宣言と呼ばれるものです。重複宣言は、同じブロック内や、関数の外側に、同じ名前の定義が複数登場したときに起こります。
main.c の関数の外側と、print.c の関数の外側に gLastPrintNum の定義が登場したため、これが重複宣言と判定されたということです。このルールは、識別子(名前)に対して起こるものなので、変数でも関数でも同様です。このように、別のソースファイルであっても重複宣言となってしまうのは、グローバル変数が外部結合を持つからです。
結合(リンケージともいいます)とは、複数の異なる有効範囲、または1つの有効範囲内で、2つ以上の宣言が行われている識別子が、結局のところ、何を指すのかを決定することをいいます。
外部結合の場合は、複数ある宣言のすべてが1つの同じ定義を指します。ですから、外部結合の宣言が複数あっても構わないのですが、定義が複数あってはならないということです。
外部結合の宣言が複数あっても構わないという部分が、今回の重複宣言を回避するポイントです。つまり、main.c と print.c のどちらか一方の gLastPrintNum の定義を、宣言に変更すれば良いのです。
ただ、グローバル変数の宣言が宣言になるのか、定義になるのかについてのルールは複雑です。確実に言えることは、明示的に初期値を与えていれば定義であるということと、extern指定子を使えば(そして初期値を与えなければ)宣言であるということです。
extern指定子は、変数を宣言する際に、型名の手前に付加します。
extern 型名 変数名;
extern付きの変数宣言にも、明示的に初期値を与えられます。しかし、そうすると定義ということになるので、宣言のつもりであるのなら初期値を与えないのが正解です。
ここまでをまとめると、グローバル変数を複数のソースファイルで適切に使用するには、以下を守るようにします。
これらを踏まえて、main.c を書き換えます。
// main.c
#include <stdio.h>
#include "print.h"
extern int gLastPrintNum;
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", gLastPrintNum );
printf}
// print.c
#include <stdio.h>
#include "print.h"
int gLastPrintNum = 0;
void printNum(int num)
{
( "[[ %d ]]\n", num );
printf= num;
gLastPrintNum }
// print.h
#ifndef PRINT_H_INCLUDED
#define PRINT_H_INCLUDED
void printNum(int num);
#endif
実行結果:
[[ 100 ]]
[[ 200 ]]
[[ 300 ]]
300
gLastPrintNum の「定義」をしていたところに、extern を付けて、初期値を与えないようにすることで「宣言」に変更しました。こうすると、ビルドが成功して、実行結果の最後に、ちゃんと 300 が出力されます。
このような方法できちんと解決できますが、実際のところ、あまりできる方法ではありません。この方法に慣れてしまうと、プログラムがもっと大規模になったときに、どこかのソースファイルにあるたった1つの定義が、どこから参照されているのかを把握することが困難になる恐れがあります。
また、今回のサンプルプログラムでいえば、定義が書かれている print.c と、実際に使用している main.c とを結びつけているものが何もありません。唯一それらしいつながりは print.h なのですが、実のところこれがインクルードされていなくても、main.c から gLastPrintNum を使えてしまいます。
別の視点でいうと、カタログであるヘッダファイルに書かれていないはずの gLastPrintNum の定義を、main.c が勝手に使っているとも言えます。これは行儀が悪いプログラミングスタイルです。
このようなスタイルは、C言語では昔から非常によくあるやり方ではあります。しかし、他のソースファイルに公開しても良い部分(ヘッダファイルに記述する)と、公開しない部分(ヘッダファイルに記述しない)とはきちんと意識して分けるようにしましょう。このような考え方をする癖を付けておくと、他のプログラミング言語を学ぶときに非常に役立ちます。
ではどうするかというと、次のルールを守るようにします。
このルールに従って修正すると、次のようになります。
// main.c
#include <stdio.h>
#include "print.h" // ヘッダファイルにある「宣言」を使う
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", gLastPrintNum );
printf}
// print.c
#include <stdio.h>
#include "print.h"
int gLastPrintNum = 0; // 唯一の「定義」
void printNum(int num)
{
( "[[ %d ]]\n", num );
printf= num;
gLastPrintNum }
// print.h
#ifndef PRINT_H_INCLUDED
#define PRINT_H_INCLUDED
extern int gLastPrintNum; // 「宣言」
void printNum(int num);
#endif
実行結果:
[[ 100 ]]
[[ 200 ]]
[[ 300 ]]
300
print.c に定義があり、print.h に宣言があります。main.c は、print.h をインクルードすることによって、print.h に書かれている「宣言」を取り込んでいます。このように、ほかのソースファイルにも公開するグローバル変数を作るのなら、その「宣言」をヘッダファイルに書くようにしましょう。
しかし、実はもう少し改良できます。この件は、次の項で取り上げます。
なお、extern は、関数宣言にも付けられます。しかし、関数宣言は暗黙的に extern が付いているものとみなされるので、省略してしまって構いません。
サンプルプログラムをもう少し改良します。
グローバル変数gLastPrintNum ですが、この変数の目的は「printNum関数が、最後に出力した整数を記録しておくこと」でした。最後に出力した値であることを保証するためには、printNum関数以外の場所から、gLastPrintNum の値を書き換えられてはいけませんし、書き換える必要性自体がないはずです。
しかし、ヘッダファイルに「宣言」を公開したため、他のソースファイルからでも gLastPrintNum にアクセスできます。ヘッダファイルに公開しなくとも、最初の方法に戻って、main.c で extern すれば、やはりアクセスできます。かといって、extern をやめると、重複宣言を避けられなくなります。
改良の鍵は、static指定子です。グローバル変数の定義に static を付加すると、結合のルールを、内部結合に変更できます。
内部結合では、1つの有効範囲内にあるすべての宣言が、たった1つの同じ定義を指します。また、定義を行ったソースファイル以外からは、その定義を使えなくなります。つまりは、ソースファイルの中に閉じ込めるような効果を持つということです。
static指定子が付加されたグローバル変数を使うと、次のように書けます。
// main.c
#include <stdio.h>
#include "print.h"
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", gLastPrintNum );
printf}
// print.c
#include <stdio.h>
#include "print.h"
static int gLastPrintNum = 0; // 内部結合
void printNum(int num)
{
( "[[ %d ]]\n", num );
printf= num;
gLastPrintNum }
// print.h
#ifndef PRINT_H_INCLUDED
#define PRINT_H_INCLUDED
void printNum(int num);
#endif
まだコンパイルは通りません。gLastPrintNum は内部結合を持つようになったため、定義を書いた print.c の中でしか使用できません。ではどうするかというと、アクセスするための関数をヘッダファイルに宣言するのです。
// main.c
#include <stdio.h>
#include "print.h"
int main(void)
{
( 100 );
printNum( 200 );
printNum( 300 );
printNum
( "%d\n", getLastPrintNum() );
printf}
// print.c
#include <stdio.h>
#include "print.h"
static int gLastPrintNum = 0; // 内部結合
void printNum(int num)
{
( "[[ %d ]]\n", num );
printf= num;
gLastPrintNum }
int getLastPrintNum(void)
{
return gLastPrintNum;
}
// print.h
#ifndef PRINT_H_INCLUDED
#define PRINT_H_INCLUDED
void printNum(int num);
int getLastPrintNum(void);
#endif
実行結果:
[[ 100 ]]
[[ 200 ]]
[[ 300 ]]
300
getLastPrintNum関数を追加しました。例によって、宣言をヘッダファイルに、定義を(拡張子 .c などの)ソースファイルに記述します。また、main.c からは gLastPrintNum を直接アクセスするのをやめて、getLastPrintNum関数を呼び出すように変更します。
変数gLastPrintNum は、内部結合になったため、print.c 以外の場所からは直接的にアクセスすることはできなくなりました。print.c 以外のソース・ファイルからは、getLastPrintNum関数を通した、値の取得だけが許されます。
長い時間を割いて説明してきましたが、最終的な方針としては、安易に extern を使うのはやめて、static を使おうということになります。他のファイルから値を取得したければ、取得のための関数を用意します。もし、値の書き換えもしたいというのならば、やはりそういう関数を用意します。
static指定子を関数に付加すると、関数を内部結合にできます。したがって、その定義を書いたソースファイル内でしか呼び出せません。
内部結合なので、関数宣言をヘッダファイルに書いてはいけません。定義も含めて、(拡張子 .c などの)ソースファイルに記述します。static指定子はそれぞれに付けます。
次のプログラムで確認してみましょう。
// main.c
#include "score.h"
int main(void)
{
( 70 );
printScore( 90 );
printScore( 50 );
printScore( 91 );
printScore}
// score.c
#include <stdio.h>
#include "score.h"
static void printRank(int score);
void printScore(int score)
{
( "SCORE: %d ", score );
printf( score );
printRank( "\n" );
printf}
static void printRank(int score)
{
( "RANK: " );
printf
if( score > 90 ){
( "S" );
printf}
else if( score > 70 ){
( "A" );
printf}
else if( score > 50 ){
( "B" );
printf}
else{
( "C" );
printf}
}
// score.h
#ifndef SCORE_H_INCLUDED
#define SCORE_H_INCLUDED
void printScore(int score);
#endif
実行結果:
SCORE: 70 RANK: B
SCORE: 90 RANK: A
SCORE: 50 RANK: C
SCORE: 91 RANK: S
main.c、score.c、score.h の 3つを用意します。printScore関数に、得点を渡せば、それに応じた結果を出力します。
ここで、score.c の中に、static指定子が付いた関数があります。printRank関数は、引数で受け取った得点から、ランクを決定して出力する関数です。ランクを決定する基準(たとえば、ランクAとランクSの境目がどこにあるのかなど)を、他のファイルにまで公開する理由がなければ、このように内部結合の関数にする価値があります。
他のファイルに判定基準を公開しないことによって、後から「今の基準では、ランクA以上になることが多すぎる」などの事情で、判定基準を変えることが簡単にできます。詳しい実装方法を「非公開」にすることの価値は、こういうところにあります。
問題① 次のプログラムの間違いを指摘してください。
// main.c
#include "sub.h"
int main(void)
{
();
getString();
putString}
// sub.c
#include <stdio.h>
#include "sub.h"
extern char gStr[80];
void getString()
{
( gStr, sizeof(gStr), stdin );
fgets}
void putString()
{
( gStr );
puts}
// sub.h
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED
extern char gStr[80];
void getString();
void putString();
#endif
問題② 問題①のプログラムを、extern指定子を生かす形と、static指定子を使う形の2通りに修正してください。
問題③ この章の最初の方で、max関数と min関数を持った utility.c と utility.h を作成しました。同じように、汎用的に使えそうな関数をこれらのファイルに追加し、便利な関数群を作ってください。
return 0;
を削除(C言語編全体でのコードの統一)≪さらに古い更新履歴を展開する≫
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
Twitter でツイート | Twitter をフォロー | LINE で送る |
![]() |
管理者情報 | プライバシーポリシー |