C プログラミングの仕組み

C プログラミング言語は非常に人気があり、その理由は簡単にわかります。 C でのプログラミングは効率的であり、プログラマーは高度な制御を行うことができます。 C++、Java、Python などの他の多くのプログラミング言語は C を使用して開発されました。

あなたがプログラマーであれば、仕事でのみ C を使用するわけではない可能性が日に日に高まっています。ただし、C を頻繁に使用しない場合でも、C を学習すると非常に有益なことがいくつかあります。その理由は次のとおりです。

小型のマイクロコントローラーからデスクトップ、ラップトップ、モバイルのオペレーティング システムに至るまで、さまざまな種類のコンピューター プラットフォームで使用できるソフトウェアのコードを読み書きできるようになります。

メモリ管理やガベージ コレクションなど、高級言語が舞台裏で何を行っているかをよりよく理解できるようになります。これを理解すると、より効率的に動作するプログラムを作成するのに役立ちます。

情報技術 (IT) の専門家である場合は、C 言語を学習することも有益です。IT 専門家は、仕事の一環としてスクリプトを作成、保守、実行することがよくあります。スクリプトは、コンピュータのオペレーティング システムが従うべき命令のリストです。特定のスクリプトを実行するために、コンピュータはシェルと呼ばれる制御された実行環境をセットアップします。ほとんどのオペレーティング システムは C ベースのシェルを実行するため、 C シェルはIT プロフェッショナルによって使用される C の一般的なスクリプト化です。

この記事では、C の背後にある歴史を取り上げ、C がなぜそれほど重要なのかを考察し、いくつかの基本的な C コードの例を示し、データ型、演算、関数、ポインター、メモリ管理などの C の重要な機能をいくつか探ります。この記事は C プログラミングの取扱説明書ではありませんが、平均的な C プログラミング ガイドの最初の数章を超える方法で、C プログラミングのユニークな点について説明しています。

まずは、C プログラミング言語がどこから来たのか、どのように発展してきたのか、そして今日のソフトウェア開発において C プログラミング言語が果たしている役割を見てみましょう。

Cって何ですか?

C を定義する最も簡単な方法は、C をコンピューター プログラミング言語と呼ぶことです。これは、コンピューターで実行できるソフトウェアを作成できることを意味します。その結果、Web ブラウザのような大規模なコンピュータ アプリケーションが作成されたり、マイクロプロセッサやその他のコンピュータ コンポーネントに埋め込まれた小さな命令セットが作成されたりする可能性があります。

C 言語は 1970 年代初頭にベル研究所で開発され、主に Ken Thompson と Dennis Ritchie の研究によるものとされています。プログラマーは、当時アセンブリ言語で書かれたプログラムを必要としていた UNIX オペレーティング システム用の、よりユーザー フレンドリーな命令セットを必要としていました。コンピュータのハードウェアと直接対話するアセンブリ プログラムは長くてデバッグが難しく、新しい機能を追加するには退屈で時間のかかる作業が必要でした 。

トンプソン氏の高級言語への最初の試みは B と呼ばれ、そのベースとなったシステム プログラミング言語 BCPL に敬意を表しました。 Bell Labs が Digital Equipment Corporation (DEC) の UNIX システム モデル PDP-11 を取得したとき、Thompson は、より新しく優れたシステム ハードウェアの要求にさらに適合するように B を作り直しました。こうしてBの後継者Cが誕生した。 1973 年までに、C は十分に安定しており、この革新的な新しい高水準言語を使用して UNIX 自体を書き直すことができるようになりました 。

人々が時間の経過とともに独自の方言を作成しないようにするために、C 開発者は 1980 年代を通じて言語の標準の作成に取り組みました。 C の米国標準である米国規格協会 (ANSI) 標準 X3.159-1989 は 1989 年に正式に制定されました。国際標準化機構 (ISO) 標準である ISO/IEC 9899:1990 は 1990 年に続きました。C のバージョンK&R がこれらの規格とその以降の改訂版 (C89、C90、および C99) を参照した後。 C89 は「ANSI C」、「ANSI/ISO C」、または「ISO C」と呼ばれることもあります。

C と UNIX での C の使用は、1980 年代までのオペレーティング システム開発ブームの一部にすぎませんでした。ただし、以前のバージョンに比べて C はあらゆる点で改善されていますが、大規模なソフトウェア アプリケーションの開発に C を使用するのはまだ簡単ではありませんでした。コンピューターがより強力になるにつれて、より簡単なプログラミング体験に対する需要が高まりました。この需要により、プログラマは C を使用して独自のコンパイラを構築し、したがって独自の新しいプログラミング言語を構築するようになりました。これらの新しい言語を使用すると、多くの可動部分を伴う複雑なタスクのコーディングを簡素化できます。たとえば、C++ や Java などの言語はどちらも C から開発され、プログラマのコード再利用能力を最適化するプログラミング アプローチであるオブジェクト指向プログラミングを簡素化しました。

背景を少し理解したところで、C 自体の仕組みを見てみましょう。

C コードの編集とコンパイル

C はコンパイル言語と呼ばれるもので、コードを実行するにはコンパイラを使用してコードを実行可能ファイルに変換する必要があります。コードは 1 つ以上のテキスト ファイルに書き込まれ、Windows のメモ帳、Mac の TextEdit、Linux の gedit などのテキスト エディタで開いて、読み取り、編集できます。実行ファイルとは、コンピュータが実行(実行)できるファイルのことです。コンパイラはコードにエラーがないかチェックし、エラーがないと思われる場合は、実行可能ファイルを作成します。

C コードの内容を確認する前に、C コンパイラを見つけて使用できることを確認しましょう。 Mac OS X およびほとんどの Linux ディストリビューション (Ubuntu など) を使用している場合、その特定の OS 用の開発ツール ソフトウェアをインストールすると、コンピュータに C コンパイラを追加できます。これらの無料の C コンパイラはコマンド ライン ツールです。つまり、通常はターミナル ウィンドウのコマンド プロンプトから実行します。これらの C コンパイラのいずれかを実行するコマンドは、「cc」または「gcc」にいくつかのコマンド ライン オプションと引数を加えたものです。これらは、コマンドの後に Enter キーを押す前に入力する単語です。

Microsoft Windows を使用している場合、またはコマンド ラインではなくグラフィカル ユーザー インターフェイスを使用したい場合は、C プログラミング用の統合開発環境 (IDE) をインストールできます。 IDE は、コードの作成、コンパイル、テスト、エラーの迅速な発見と修正を行うことができる単一のインターフェイスです。 Windows の場合は、C と C++ の両方のプログラミング用の IDE である Microsoft Visual C++ ソフトウェアを購入できます。もう 1 つの人気のある IDE は Eclipse です。これは、Windows、Mac、Linux 上で動作する無料の Java ベースの IDE で、C やその他の多くのプログラミング言語のコンパイルに使用できる拡張機能があります。

C の場合、他のコンピューター プログラミング言語と同様に、使用するコンパイラーのバージョンは非常に重要です。プログラムで使用している C 言語のバージョンと同じか、それよりも新しいバージョンの C コンパイラを使用する必要があります。 IDE を使用している場合は、IDE が作業中のプログラムのターゲット C バージョンを使用するように設定を調整してください。コマンド ラインを使用している場合は、次のコマンドのようにコマンド ライン引数を追加してバージョンを変更できます。

gcc –std c99 –o myprogram.exe myprogram.c

上記のコマンドでは、「gcc」はコンパイラを実行するための呼び出しであり、それ以外はすべてコマンド ライン オプションまたは引数です。 「-std」オプションの後に「c99」が追加され、コンパイル中に C の C99 標準バージョンを使用するようにコンパイラに指示しました。 「-o」オプションの後に「myprogram.exe」が追加され、実行可能ファイル (コンパイラの出力ファイル) に myprogram.exe という名前を付けるように要求されました。 「-o」を指定しないと、実行可能ファイルには自動的に a.out という名前が付けられます。最後の引数「myprogram.c」は、コンパイルする C コードを含むテキスト ファイルを示します。つまり、このコマンドは、「ねえ、gcc、C99 C プログラミング標準を使用して myprogram.c をコンパイルして、結果を myprogram.exe という名前のファイルに入れてください」と言っているのです。 gcc であろうと他のものであろうと、特定のコンパイラーで使用できるオプションの完全なリストについては、Web を参照してください。

コンパイラがインストールされたら、C でプログラミングする準備が整います。まず、作成できる最も単純な C プログラムの 1 つの基本構造を見てみましょう。

最も単純な C プログラム

簡単な C プログラムを見て、それを使用して C の基本と C のコンパイル プロセスを理解しましょう。前に説明したように C コンパイラがインストールされた自分のコンピュータがある場合は、sample.c という名前のテキスト ファイルを作成し、それを使用してこの例を手順に従って進めることができます。ファイル名で .c を省略した場合、またはエディターがファイル名に .txt を追加した場合、コンパイル時に何らかのエラーが発生する可能性があることに注意してください。

サンプルプログラムは次のとおりです。

/* サンプルプログラム */

#include <stdio.h>

int main()

{

printf(“これは私の最初のプログラムからの出力です!\n”);

0を返します。

}

このプログラムはコンパイルして実行すると、「これは最初のプログラムからの出力です!」という行を出力するようにコンピューターに指示します。そして停止します。これ以上簡単なことはありません。次に、各行が何をしているかを見てみましょう。

行 1 — これは、C で 1 行以上の /* と */ の間にコメントを記述する 1 つの方法です。

行 2 — #include コマンドは、既存の C コードの他のソース、特に再利用可能な一般的な命令を含むファイルであるライブラリを調べるようにコンパイラーに指示します。は、ユーザーから入力を取得し、出力を画面に書き込むための関数を備えた標準 C ライブラリを参照します。ライブラリについては後ほど詳しく見ていきます。

行 3 — この行は関数定義の最初の行です。すべての C プログラムには、少なくとも 1 つの関数、またはプログラムの実行時にコンピューターが行うべきことを表すコード ブロックがあります。関数はタスクを実行し、他の関数で使用できる戻り値と呼ばれる副産物を生成します。プログラムには少なくとも、ここで示したような main という関数があり、データ型が int (整数を意味する) の戻り値を持ちます。後ほど関数を詳しく調べるときに、空の括弧が何を意味するかがわかります。

行 4 と 7 — 関数内の命令は中かっこで囲まれています。プログラマの中には、ここに示すように、中括弧で囲まれたブロックを別の行で開始および終了する人もいます。関数定義の最初の行の最後に開き中括弧 ({) を置く場合もあります。プログラム内のコード行を別々の行に入力する必要はありませんが、プログラマーは通常、後でコードを読みやすく編集しやすくするために、各命令をスペースでインデントして別々の行に配置します。

行 5 — これは printf という名前の関数への関数呼び出しです。この関数は 1 行目に含まれる stdio.h ライブラリにコーディングされているため、自分で記述する必要はありません。 printf へのこの呼び出しは、画面に何を出力するかを指示します。ただし、引用符で囲まれた最後の \n は出力されません。これは、カーソルを画面上の次の行に移動するように printf に指示するエスケープ シーケンスです。また、ご覧のとおり、関数内のすべての行はセミコロンで終わる必要があります。

6 行目 — 値を返すすべての関数には、このような return ステートメントを含める必要があります。 C では、プログラム内で使用されない場合でも、main 関数の戻り値の型は常に整数でなければなりません。ただし、C プログラムを実行しているときは、基本的にその main 関数を実行していることに注意してください。したがって、プログラムをテストするときに、プログラムの実行からの戻り値を表示するようにコンピューターに指示できます。プログラマーは通常、プログラムが正常に実行されたことを確認するテストでその値を探すため、戻り値 0 が推奨されます。

プログラムをテストする準備ができたら、ファイルを保存し、プログラムをコンパイルして実行します。コマンド ラインで gcc コンパイラを使用しており、プログラムがsample.c というファイル内にある場合は、次のコマンドでコンパイルできます。

gcc -o サンプル.exe サンプル.c

コードにエラーがない場合、このコマンドを実行すると、sample.c と同じディレクトリにsample.exe という名前のファイルが作成されます。最も一般的なエラーは構文エラーです。これは、行末のセミコロンを省略したり、引用符や括弧を閉じなかったりするなど、入力ミスを意味します。変更を加える必要がある場合は、テキスト エディタでファイルを開いて修正し、変更を保存してコンパイル コマンドを再試行します。

Sample.exe プログラムを実行するには、次のコマンドを入力します。 ./ に注目してください。これにより、コンピュータは実行可能ファイルを見つけるために現在のディレクトリを参照するようになります。

./sample.exe

これらは C のコーディングとコンパイルの基本ですが、コンパイルについては他の C プログラミング リソースからさらに多くのことを学ぶことができます。さて、箱を開けて、C がプログラムを構築するためにどのような要素を備えているかを見てみましょう。

C における一般的なプログラミング概念

一般的なプログラミング概念のいくつかを C コードで実践する方法を見てみましょう。これらの概念を簡単にまとめたのが次のとおりです。

関数— 前に述べたように、関数は、プログラムの実行時にコンピューターが行うべきことを表すコードのブロックです。一部の言語ではこれらの構造をメソッドと呼びますが、C プログラマは通常この用語を使用しません。プログラムでは複数の関数を定義し、それらの関数を他の関数から呼び出すことができます。後で、C の関数の構造を詳しく見ていきます。

変数— プログラムを実行するとき、値が何であるかを事前に知らなくてもプログラムを実行できる柔軟性が必要な場合があります。他のプログラミング言語と同様、C では柔軟性が必要な場合に変数を使用できます。代数の変数と同様、コンピューター プログラミングの変数は、未知またはまだ見つかっていない値を表すプレースホルダーです。

データ型— プログラムの実行中にデータをメモリに保存し、そのデータに対してどのような操作を実行できるかを知るために、C などのプログラミング言語は認識できる特定のデータ型を定義します。 C の各データ型には、バイナリのビットまたはバイトで測定される特定のサイズと、そのビットが何を表すかについての特定の規則があります。 C を使用する場合、タスクに適切なデータ型を選択することがいかに重要であるかが今後わかります。

演算— C では、数値に対して算術演算 (加算など) を実行したり、文字列に対して文字列演算 (連結など) を実行したりできます。 C には、データに対して実行したいことのために特別に設計された組み込みの操作もあります。 C のデータ型を確認するときは、操作についても簡単に見ていきます。

ループ— プログラマーが実行したい最も基本的なことの 1 つは、プログラムの実行中に発生する特定の条件に基づいてアクションを何度も繰り返すことです。与えられた条件に基づいて繰り返すように設計されたコードのブロックはループと呼ばれ、C 言語では、while、do/while、for、 continue/break、goto などの一般的なループ構造が提供されます。 C には、一般的な if/then/else 条件文と switch/case ステートメントも含まれています。

データ構造— プログラムで処理するデータが大量にあり、そのデータを並べ替えたり検索したりする必要がある場合、おそらく何らかのデータ構造を使用することになるでしょう。データ構造は、同じデータ型の複数のデータを表す構造化された方法です。最も一般的なデータ構造は配列です。これは、指定されたサイズのインデックス付きリストにすぎません。 C には、いくつかの一般的なデータ構造を処理するために使用できるライブラリがありますが、いつでも関数を作成して独自の構造を設定することもできます。

プリプロセッサの操作— コードを実行可能ファイルにコンパイルする前に、コードを処理するための指示をコンパイラーに与えたい場合があります。これらの操作には、定数値の置換や、C ライブラリからのコードの組み込み (前にサンプル コードで示した) が含まれます。

また、C では、多くのプログラミング言語で簡素化または自動化されているいくつかの概念をプログラマが処理する必要があります。これらには、ポインタ、メモリ管理、ガベージ コレクションが含まれます。後のページでは、C でプログラミングするときにこれらの概念について知っておくべき重要なことについて説明します。

まだプログラマーでない場合、この概念の簡単な概要は圧倒されるように思えるかもしれません。緻密な C プログラミング ガイドに取り組む前に、関数から始めて、上記の概念の中核となる概念をわかりやすく見てみましょう。

C の関数

ほとんどのコンピューター プログラミング言語では、何らかの関数を作成できます。関数を使用すると、長いプログラムを名前付きセクションに分割して、プログラム全体でそれらのセクションを再利用できるようになります。一部の言語、特にオブジェクト指向プログラミング手法を使用する言語のプログラマは、 functionの代わりにメソッドという用語を使用します。

関数はパラメータを受け取り、結果を返します。関数を構成するコードのブロックが関数定義です。関数定義の基本構造は次のとおりです。

<戻り値の型> <関数名>(<パラメータ>)

{

<ステートメント>

return <戻り値の型に適した値>;

}

C プログラムには、少なくとも main という名前の関数が 1 つあります。コンパイラは、main 関数がプログラム内の他の関数を呼び出している場合でも、プログラムの開始点として main 関数を探します。以下は、以前に見た単純な C プログラムで見たメインです。これは戻り値の型が整数で、パラメータをとらず、2 つのステートメント (関数内の命令) があり、そのうちの 1 つは return ステートメントです。

int main()

{

printf(“これは私の最初のプログラムからの出力です!\n”);

0を返します。

}

main 以外の関数には、定義と 1 つ以上の関数呼び出しがあります。関数呼び出しは、別の関数内のステートメントまたはステートメントの一部です。関数呼び出しでは、呼び出している関数の名前を括弧で囲んで指定します。関数にパラメータがある場合、関数呼び出しにはそれらのパラメータと一致する対応する値が含まれている必要があります。関数呼び出しのこの追加部分は、関数へのパラメーターの受け渡しと呼ばれます。

しかし、パラメータとは何でしょうか?関数のパラメーターは、関数が動作するために必要な特定のデータ型のデータです。 C の関数は、引数とも呼ばれるパラメーターを無制限に受け入れることができます。関数定義に追加される各パラメーターは、関数ブロック内のデータ型と変数名の 2 つを指定する必要があります。複数のパラメータはカンマで区切られます。次の関数には 2 つのパラメーターがあり、どちらも整数です。

int doubleAndAdd(int a, int b)

{

戻り値 ((2*a)+(2*b));

}

次に、ズームアウトして関数の観察を続け、より大きな C プログラム内で関数がどのように適合するかを見てみましょう。

関数プロトタイプ

C では、プログラム内のどこにでも関数定義を追加できます (別の関数内を除く)。唯一の条件は、関数がコードの後半のどこかに存在することをコンパイラに事前に通知する必要があることです。これは、プログラムの最初に関数プロトタイプを使用して行います。プロトタイプは、定義の最初の行に似たステートメントです。 C では、プロトタイプのパラメーターの名前を指定する必要はなく、データ型のみを指定する必要があります。 doubleAndAdd 関数の関数プロトタイプは次のようになります。

int doubleAndAdd(int, int);

関数プロトタイプがプログラムのパッキングリストであると想像してください。コンパイラは、新しい本棚を開梱して組み立てるのと同じように、プログラムを開梱してアセンブルします。パッキングリストは、本棚の組み立てを始める前に、必要な部品がすべて箱に入っていることを確認するのに役立ちます。コンパイラは、プログラムのアセンブルを開始する前に、同じ方法で関数プロトタイプを使用します。

前に説明したsample.cプログラムに従っている場合は、ファイルを開いて編集し、ここに示すdoubleAndAdd関数の関数プロトタイプ、関数定義、および関数呼び出しを追加します。次に、前と同じようにプログラムをコンパイルして実行し、新しいコードがどのように機能するかを確認します。次のコードをガイドとして使用して試してみることができます。

#include <stdio.h>

int doubleAndAdd(int, int);

int main()

{

printf(“これは私の最初のプログラムからの出力です!\n”);

printf(“2 倍して 2 と 3 を加算すると、結果は次のようになります: %d \n”, doubleAndAdd(2,3));

0を返します。

}

int doubleAndAdd(int a, int b)

{

戻り値 ((2*a)+(2*b));

}

これまで、C プログラムの基本的な構造要素をいくつか見てきました。ここで、C プログラムで操作できるデータの種類と、そのデータに対して実行できる操作を見てみましょう。

関数の宣言

C では、特に古い C プログラマの間では、関数プロトタイプよりも関数宣言という用語をよく聞くでしょう。ただし、この記事では関数プロトタイプという用語を使用していますが、これには重要な違いがあるためです。元々、関数宣言にはパラメーターは必要なかったため、戻り値の型、関数名、および空のかっこのペアで十分でした。ただし、関数プロトタイプは、呼び出すパラメーターの数とデータ型を含めることにより、コンパイラーに重要な追加情報を提供します。プロトタイプは、今日のプログラマーの間で、C やその他のプログラミング言語のベスト プラクティス アプローチとなっています。

C のデータ型と演算

コンピュータの観点から見ると、プログラムはすべて 1 と 0 の連続に過ぎません。 C のデータ型は、コンピューターにこれらのビットの一部を使用する方法を指示します。

コンピュータの観点から見ると、データはハード ドライブまたはコンピュータのプロセッサまたはメモリ内の電子ビットのオンとオフの状態を表す一連の 1 と 0 にすぎません。これらの数十億の 2 進数をどのように理解するかを決定するのは、コンピューター上で実行しているソフトウェアです。 C は、特定のデータ型に基づいてデータを解釈するだけでなく、ビット レベルでデータを簡単に操作できる数少ない高級言語の 1 つです。

データ型は、一連のビットをどのように理解するかを示す小さなルールのセットです。データ型には特定のサイズと、その型のデータに対して演算 (加算や乗算など) を実行するための独自の方法があります。 C では、データ型のサイズは使用しているプロセッサに関係します。たとえば、C99 では、整数データ型 (int) のデータは、16 ビット プロセッサでは 16 ビット長ですが、32 ビットおよび 64 ビット プロセッサでは 32 ビット長です。

C プログラマが知っておくべきもう 1 つの重要なことは、言語が符号付きデータ型と符号なしデータ型をどのように処理するかということです。符号付き型は、そのビットの 1 つが、それが正の数であるか負の数であるかを示すインジケーターとして予約されていることを意味します。したがって、16 ビット システム上の unsigned int は 0 ~ 65,535 の数値を処理できますが、同じシステム上のサインインした場合は -32,768 ~ 32,767 の数値を処理できます。操作によって int 変数がその範囲を超える場合、プログラマは追加のコードでオーバーフローを処理する必要があります。

C のデータ型と演算におけるこれらの制約とシステム固有の特性を考慮すると、C プログラマはプログラムのニーズに基づいてデータ型を選択する必要があります。選択できるデータ型の一部は、C のプリミティブ データ型、つまり C プログラミング言語に組み込まれたデータ型です。 C のデータ型の完全なリストと、ある型から別の型にデータを変換する方法に関する重要な情報については、お気に入りの C プログラミング ガイドを参照してください。

C プログラマーは、プリミティブ データ型と、データの編成と操作の方法を定義する一連の関数を組み合わせたデータ構造を作成することもできます。データ構造の使用は高度なプログラミングのトピックであり、この記事の範囲を超えていますが、最も一般的な構造の 1 つである配列について見ていきます。配列は、すべて同じデータ型のデータを含む仮想リストです。配列のサイズは変更できませんが、その内容を他のより大きいまたは小さい配列にコピーすることはできます。

プログラマは数値の配列をよく使用しますが、文字列と呼ばれる文字配列には最もユニークな機能があります。文字列を使用すると、ユーザーが言う内容 (「こんにちは」など) を一連の文字として保存でき、C プログラムでユーザーから読み取ったり、画面に出力したりできます。文字列操作には非常にユニークな操作セットがあり、一般的な文字列関数を備えた独自の専用 C ライブラリ (string.h) があります。

C の組み込み演算は、ほとんどのプログラミング言語で見られる典型的な演算です。複数の演算を 1 つのステートメントに結合する場合は、演算子の優先順位、つまりプログラムが数式内の各演算を実行する順序を必ず把握してください。たとえば、(2+5)*3 は 21 に等しく、2+5*3 は 17 に等しくなります。これは、そうでないことを示す括弧がない限り、C は加算の前に乗算を実行するためです。

C を学習している場合は、すべての基本データ型と演算、および同じ式内の演算の優先順位を理解することを優先してください。また、さまざまなデータ型の変数や数値に対してさまざまな操作を試してください。

この時点で、いくつかの重要な C の基本の表面をなぞったことになります。次に、C を使用すると、毎回最初からやり直すことなくプログラムを作成できるようにする方法を見てみましょう。

ゼロから始めるのではなく、ライブラリを使用してください

C 言語は必要な最も基本的な機能のみをサポートしているため、C 言語ではライブラリが非常に重要です。たとえば、C には、キーボードから読み取り、画面に書き込むための入出力 (I/O) 関数が含まれていません。基本を超えたものはすべてプログラマーによって書かれなければなりません。コードの塊が複数の異なるプログラムに役立つ場合は、簡単に再利用できるようにライブラリに入れられることがよくあります。

これまでの C の説明では、標準 I/O (stdio) ライブラリという 1 つのライブラリについてすでに説明しました。プログラムの先頭の #include 行は、stdio.h という名前のヘッダー ファイルからライブラリをロードするように C コンパイラーに指示しました。 C メンテナーには、I/O、数学関数、時間操作、および文字列などの特定のデータ構造に対する一般的な操作のための標準 C ライブラリが含まれています。 C89 標準ライブラリおよび C99 の更新と追加に関する情報については、Web またはお気に入りの C プログラミング ガイドを検索してください。

あなたも C ライブラリを作成できます。そうすることで、プログラムを再利用可能なモジュールに分割できます。このモジュール方式のアプローチにより、同じコードを複数のプログラムに組み込むことが容易になるだけでなく、プログラム ファイルが短くなり、読み取り、テスト、デバッグが容易になります。

ヘッダー ファイル内の関数を使用するには、プログラムの先頭に #include 行を追加します。標準ライブラリの場合は、ライブラリの対応するヘッダー ファイルの名前を大なり記号と小なり記号 () の間に入れます。自分で作成したライブラリの場合は、ファイル名を二重引用符で囲みます。 C プログラムの他の部分のステートメントとは異なり、各行の末尾にセミコロンを置く必要はありません。以下に、各タイプのライブラリを 1 つ含めて示します。

#include <math.h>

#include “mylib.h”

包括的な C プログラミング ソースは、C で独自のライブラリを作成するために必要な手順を提供する必要があります。作成する関数定義は、ライブラリ内であってもメイン プログラム内であっても変わりません。違いは、それらをオブジェクト ファイル (名前が .o で終わる) と呼ばれるものに個別にコンパイルし、ヘッダー ファイル (名前が .h で終わる) と呼ばれる 2 番目のファイルを作成することです。ライブラリ内の各関数に対応する関数プロトタイプ。これは、ライブラリを使用する各メイン プログラムの #include 行で参照するヘッダー ファイルであり、そのプログラムをコンパイルするたびにコンパイラ コマンドに引数としてオブジェクト ファイルを含めます。

これまで説明してきた C の機能は、他のプログラミング言語でも一般的なものです。ただし、次に、C がコンピュータのメモリをどのように管理するかについて説明します。

C のポインターに関するいくつかのヒント

C プログラムがメモリ (通常はコンピュータのランダム アクセス メモリ、つまり RAM) にロードされると、プログラムの各部分がメモリ内のアドレスに関連付けられます。これには、特定のデータを保持するために使用している変数が含まれます。プログラムは関数を呼び出すたびに、その関数を実行して値を返すのに十分な時間だけ、その関数とそれに関連するすべてのデータをメモリに読み込みます。関数にパラメーターを渡すと、C は関数で使用する値のコピーを自動的に作成します。

ただし、関数を実行するときに、元のメモリ位置にあるデータに永続的な変更を加えたい場合があります。 C が関数で使用するためにデータのコピーを作成した場合、元のデータは変更されません。元のデータを変更したい場合は、その値を関数に渡す (値渡し) のではなく、そのメモリ アドレスへのポインターを渡す (参照渡し) 必要があります。

C ではポインタがあらゆるところで使用されるため、C 言語を完全に使いこなしたい場合は、ポインタについてよく理解する必要があります。ポインタは他の変数と同様に変数ですが、その目的は他のデータのメモリ アドレスを格納することです。ポインタにはデータ型もあるので、そのメモリ アドレスのビットを認識する方法がわかります。

C コードで 2 つの変数を並べて見ると、ポインターを認識できない場合があります。これは、最も経験豊富な C プログラマにとっても困難な場合があります。ただし、最初にポインターを作成するときは、変数名の直前にアスタリスクが必要なので、より明確になります。これは、C では間接演算子として知られています。次のコード例では、整数 i と整数 p へのポインターを作成します。

int i;

int *p;

現在、i または p には値が割り当てられていません。次に、i に値を代入し、i のアドレスを指すように p を代入しましょう。

i = 3;

p = &i;

ここでは、i の直前にアドレス演算子としてアンパサンド (&) が使用されており、これは「i のアドレス」を意味します。割り当てを行うために、そのアドレスが何であるかを知る必要はありません。プログラムを実行するたびに結果が変わる可能性があるため、これは問題ありません。代わりに、アドレス演算子は、プログラムの実行中にその変数に関連付けられたアドレスを決定します。アドレス演算子を使用しない場合、割り当て p=i は、変数 i のメモリ アドレスではなく、文字通り、p に 3 のメモリ アドレスを割り当てます。

次に、C コードでポインターを使用する方法と、準備が必要な課題を見てみましょう。

C でポインターを正しく使用する

C プログラミングに習熟したい場合は、コード内でポインターを効果的に使用する方法をしっかりと理解する必要があります。

ポインターができたら、操作と関数呼び出しで同じデータ型の変数の代わりにそれを使用できます。次の例では、より大きな操作内でIの代わりにIへのポインターが使用されます。 p(*p)で使用されるアスタリスクは、操作がメモリアドレス自体ではなく、そのメモリアドレスでPが指している値を使用する必要があることを示しています。

int b;

b = *p + 2;

ポインターがなければ、Cプログラムのメイン以外の機能にタスクを分割することはほぼ不可能です。これを説明するために、ユーザーの高さを最も近いセンチメートルに保存するHと呼ばれるメインに変数を作成したと考えてください。また、ユーザーにその高さの値を設定するように促す名前の名前を記述した関数を呼び出します。あなたのメイン関数の線は次のように見えるかもしれません:

int h;

setheight(h); /*ここには潜在的な問題があります。 */

この関数呼び出しは、Hの値をsetheightに渡そうとします。ただし、関数の実行が終了すると、関数がそのコピーのみを使用し、実行が終了したときに破棄したため、Hの値は変更されません。

H自体を変更したい場合は、最初に、機能が新しい値のコピーではなく、既存の値にポインターを使用できるようにする必要があります。その場合、Setheightの最初の行は、そのパラメーターとして値の代わりにポインターを使用します(間接演算子に注意してください):

setheight(int *height){ / *関数ステートメントはこちらに行きます * /}

次に、Setheightを呼び出すための2つの選択肢があります。 1つ目は、hのアドレス演算子を渡されたパラメーター(&h)として使用することです。もう1つは、Hへの別のポインターを作成し、代わりにそれを渡すことです。以下の両方のオプションに次を示しています。

setheight(&h); / * hのアドレスを関数に渡す */

int *p;

p =&h;

setheight(p); / * hのアドレスに個別のポインターを関数に渡す */

2番目のオプションは、ポインターを使用する際の一般的な課題を明らかにします。課題は、複数のポインターを同じ値に持つことです。これは、その1つの値の変更がすべてのポインターに一度に影響することを意味します。これは、プログラムで達成しようとしていることに応じて、良いことも悪いこともあります。繰り返しますが、ポインターの使用を習得することは、Cプログラミングをマスターするための重要な鍵です。これらの課題に直面する準備ができているので、可能な限りポインターで練習します。

これまでに調査したC機能は、他のプログラミング言語でも典型的です。次に、慎重なメモリ管理に対するCの要求を検討します。

cのメモリ管理の重要性

Cをこのような多用語言語にすることの1つは、プログラマーがプログラムを縮小して非常に少量のメモリで実行できることです。 Cが最初に書かれたとき、これは重要な機能でした。これは、コンピューターが今日ほど強力ではなかったためです。携帯電話から小さな医療機器まで、小さな電子機器に対する現在の需要があるため、一部のソフトウェアのメモリ要件を小さく保つことに新たな関心があります。 Cは、メモリ使用量を多くの制御が必要なほとんどのプログラマーにとって頼りになる言語です。

メモリ管理の重要性をよりよく理解するには、プログラムがメモリを使用する方法を検討してください。最初にプログラムを実行すると、コンピューターのメモリにロードされ、コンピューターのプロセッサから指示を送信および受信することで実行を開始します。プログラムが特定の関数を実行する必要がある場合、その機能を実行中にメモリのさらに別の部分にロードし、関数が完了したときにそのメモリを放棄します。さらに、メインプログラムで使用されている新しいデータは、プログラムの期間中にメモリを取り上げます。

これらすべてをさらに制御したい場合は、動的ストレージ割り当てが必要です。 Cは動的ストレージの割り当てをサポートします。これは、必要に応じてメモリを予約し、使用が終了したらすぐにメモリを解放する機能です。多くのプログラミング言語には、これらのメモリ管理タスクを処理する自動メモリ割り当てとガベージコレクションがあります。 Cは、ただし、標準のCライブラリから次のキー関数を使用して、メモリの割り当てについて明示することができます(場合によっては要求されます)。

  • MALLOC-メモリ割り当ての略で、 MALLOCは、プログラムが処理するために必要な特定のタイプのデータをストーリーするために、特定のサイズのメモリのブロックを予約するために使用されます。 Mallocを使用すると、割り当てられたメモリへのポインターが作成されます。これは、1つの整数など、単一のデータには必要ありません。これは、最初に宣言するとすぐに割り当てられます(Int Iのように)。ただし、これは、配列などのデータ構造の作成と管理の重要な部分です。 Cの代替メモリ割り当てオプションはCALLOCであり、これは予約されたときにメモリをクリアし、以前に予約されていたメモリを変更するReallocもクリアします。
  • 無料 – 無料でプログラムを強制して、以前に特定のポインターに割り当てられたメモリを解放します。

MallocとFreeを使用する場合のベストプラクティスは、あなたが割り当てるものはすべて解放されるべきであるということです。一時的な機能であっても、何かを割り当てるたびに、オペレーティングシステムがスペースをクリーンアップするまでメモリのままです。ただし、メモリが無料ですぐに使用できるようにするには、現在の関数が終了する前に解放する必要があります。このメモリ管理は、プログラムのメモリフットプリントを最小限に抑え、メモリリークを回避できることを意味します。メモリリークとは、割り当てに残されないようになり、プログラムが失速またはクラッシュするまで、ますます多くのメモリを使用し続けるプログラムの欠陥です。一方、メモリを解放することを心配しないでください。

この記事を通して、Cプログラミング言語の基本構造とコア概念のいくつかを学びました。私たちは、その歴史、他のプログラミング言語と共通する特性、およびそれをコーディングソフトウェアのためのユニークで多用途のオプションにする重要な機能を検討しました。次のページで、Cへの旅をさらに伝えるプログラミングガイドを含む多くの情報のために開始します。