一からC言語をマスター!プログラミング言語C やさしい入門!

プログラム言語

このブログは、テキストであるプログラミング言語Cの第1章についてまとめています。

ここでは、C言語のプログラムに慣れるために、変数と定数、算術式、制御の流れ、関数、基本的な入出力といった基本事項を重点に述べられています。

テキストの内容を理解し、問題を解くという順序で進めていく工程を記録しています。

この工程で確実にC言語をマスターしていける!
この信念で、一歩ずつ前進していきます。

1.1 手始めに

ここでは、C言語のプログラムに慣れるために、変数と定数、算術式、制御の流れ、関数、基本的な入出力といった基本事項を重点に述べられています。

まずは、小さなプログラムを組み、動きを確認することからです。

“hello, world”を表示するプログラムから、エラーケースを考えてみる演習問題が2問ありました。

「手始め」の演習です。
簡単そうなので、まずは手始めに解いてみました。

プログラム1
実行結果

【演習1-1】
手元のシステムでこのプログラムをランさせよ。プログラムの一部を省いて、どんなエラー・メッセージが出るか、試みてみよ。


【演習1-2】
xが上にあげなかったある文字であるとして、printfの引数に/xをふくめるとどうなるかを調べる実験を行なえ。

【演習1-1】では、5行目の”;”を除いてコンパイルしてみました。
すると、次のようなエラーメッセージが出力されました。

演習1-1の結果

5行目の20桁目がエラーです。
「expected ‘;’ before ‘printf’」は、「”printf”の前に”;”が必要です」の意味です。
6行目のprintf前(5行目の最終)に”;”が無いというエラーメッセージです。

同様に【演習1-2】の実験も行ってみました。

演習1-2の結果

5行目の21桁目がエラーです。

このケースは、「バックスラッシュ+”X”」の使い方が間違っています。
「バックスラッシュ+”X”」の後続は、16進数の定数でなければなりません。

1.2 変数と算術

変数の宣言算術式を学習します。

変数はデータ型が割り当てられて、プログラムの先頭で宣言します。

例えば整数の変数を宣言する場合は、次のように宣言します。
int 変数;

また、浮動小数点数を表す場合は、floatで宣言します。
float 変数;

その他の基本的なデータ型は、以下のようなものがあります。
char  1バイトの文字
short 短い整数
long 長い整数
double 倍精度浮動小数点数

温度換算のプログラムを例に、変数の宣言と値の代入、ある値を求めるための算術式の記述を重点に学習します。

また、算術式はwhile文によって、複数回繰り返されることも学習します。
次のプログラムは、摂氏の温度を華氏の温度から公式℃=(5/9)(℉-32)を使用して対応表を印字するプログラムです。

摂氏-華氏温度変換のプログラム
実行結果

prontfの書式指定子を簡単にまとめると、
%d・・・10進整数として印字
%6d・・・10進整数として印字、少なくとも6文字幅に
%f・・・浮動小数点数として印字
%6f・・・浮動小数点数として印字、少なくとも6文字幅に
%.2f・・・浮動小数点数として印字、小数点の後は2桁
%6.2f・・・浮動小数点数として印字、少なくとも6文字幅で、小数点の後は2桁
%o・・・8進
%x・・・16進
%c・・・文字列
%%・・・%自体

【演習1-3】
表の上に見出しを印字するように温度換算プログラムを変更せよ。

換算結果の印字の前に、見出しとなるprintfを追記します。

【演習1-3】のプログラム
【演習1-3】の実行結果

【演習1-4】
温度を摂氏から華氏に換算するプログラムを書け。


公式℃=(5/9)(℉-32)を摂氏の温度から華氏の温度を求める式に変換します。
℉-32=℃/(5/9) → ℉=(℃*9)/5+32 
この式をプログラムに組み入れます。

【演習1-4】のプログラム
【演習1-4】の実行結果

1.3 For文

For文(Forループ)の学習をします。
温度換算プログラムをWhile文からFor文に書き換えてみます。

For文を使用して摂氏-華氏温度対応表プログラムを作成
For文で記述した温度換算プログラムの実行結果

ここで、For文の実行順序について説明します。

for (①初期化式; ②条件式; ④反復式);
③処理部;
For文は上記のような構成になっています。

実行順序は、「①初期化式→②条件式→③処理部→④反復式→②条件式→③処理部→④反復式→…」の順序で実行されます。

【演習1-5】
温度換算プログラムにてを加えて、表を逆順に、すなわち300度から0度へという順に印字すうように直せ。

【演習1-5】のプログラム
【演習1-5】の実行結果

1.4 記号定数

“記号定数”、あまり聞きなれないですね。
C言語では、数値や文字列をある記号名すなわち記号定数として定義することができます。

そのとき使用する構文が、#define構文です。
次のように記述します。

#define 記号名 置き換える数値や文字列

記号定数を使って、温度換算プログラムを書きなおすと、次のようになります。
注意点は、#defineの行の最後にセミコロンが無いことです。

記号定数を使ったプログラム

1.5 文字入出力

文字データを扱う簡単な操作を学習します。

標準ライブラリに準備されている関数、getchar()とputchar()を使って練習していきます。
この関数は、一度に1文字を読み書きを行います。

1.5.1 ファイルの複写

ここでは、1文字ずつ入力して出力(複写)するプログラムを例で文字の入出力を学習します。
getchar()で1文字入力を行い、putchar()で1文字出力を行います。
プログラムを記述します。

EOFは<stdio.h>で定義されている整数であって、入力データが無いことを示します。
次に実行結果です。

1文字複写のプログラムの実行結果

文字「A」を入力すると、文字「A」を出力します。

「B」、「C」、「1」、「10」、「100」と続けて入力をしていきました。
「B」、「C」、「1」、「10」、「100」の出力を確認できました。

【演習1-6】
getchar() != EOFという式の値が0か1であることを確認せよ。

【演習1-6】のプログラム

【演習1-6】
の実行結果

getchar()関数で値が取得され、EOFではないので条件式は真となります。
また、Linuxの場合、「ctrl+”d”」を実行すると、シェルに対してEOFシグナルが送信されます。
条件式は、偽となりループを抜けて、プログラム終了します。

【演習1-7】
EOFの値を印字するプログラムを書け。

【演習1-7】のプログラム
【演習1-7】の実行結果

1.5.2 文字のカウント

複写のプログラムを改造して、入力文字をカウントするプログラムを作成します。
ここでは、2種類のプログラムを紹介します。

文字数をカウントするプログラム1

文字をカウントする変数は、int(整数)ではなく、long(長整数)で定義されている。
これは、intの範囲が16bit(-32768~32767)であるため、longの32bit(-2,147,483,648~2,147,483,647)を使用している。

printfの書式指定子は、「%ld」を使用し、値がlongであることprintfに伝えています。
カウンターに使用している変数は、ncでインクリメントしています。

文字数をカウントするプログラム1の実行結果

入力文字は、10文字だが結果は、11になります。
これは、0の後に改行コード(LF:Line Feed)が存在するので、1つ多くなります。

改行の後にCtrl+d(EOFシグナル)でループを抜けます。

次のプログラムは、同様に文字をカウントするプログラムですが、for文の中でカウントされています。
ここで使用する変数は、longより大きな数値を想定して、double(倍精度実数)を使用します。

printfの書式指定子は、「%.0f」を使用しています。
また、あり得ない小数点は抑止されます。

文字数をカウントするプログラム2
文字数をカウントするプログラム2の実行結果

1.5.3 行数のカウント

標準入出力ライブラリのgetchar()を使って、今度は、改行コードを読み取ることで行数をカウントするプログラムを作成します。

しかし、getchar()では入力の文字をよみとるので、改行コードを特定できません。
文字を読み取った後で、if文により改行コードを判定させます。

行数をカウントするプログラム
行数をカウントするプログラムの実行結果

【演習1-8】
空白、タブ、改行を数えるプログラムを書け。

【演習1-8】のプログラム

「行数をカウントするプログラム」では、改行コードをカウントしてきました。
今回は、空白文字(” “)と水平タブ(\t)と改行(\n)です。
この文字を見つければ、カウントするプログラムを作成します。

【演習1-8】の実行結果

上の実行結果の内容は、1行目「abcde(空白)ghij(タブ)lmn(改行)」、2行目「123456(空白)(空白)(空白)0abcd(タブ)(タブ)ghijklmn(改行)」、3行目「aaa(空白)bbbccc(タブ)ddd(空白)eee(改行)」で、結果は13となります。

【演習1-9】
二つ以上の空白を一つの空白に置き換えながら、入力を出力に複写するプログラムを書け。

【演習1-9】のプログラム
【演習1-9】の実行結果

【演習1-10】
各タブを\tに、各バックスペースを\bに、各バックスラッシュを\\に置き換えながら、入力を出力に複写するプログラムを書け。

【演習1-10】のプログラム
【演習1-10】の実行結果

タブは「TABキー」、バックスラッシュは「\キー」、バックスペースはバックスペースキーを使用しないで、ショートカット「CTRL+”H”」を使用します。

1.5.4 単語のカウント

行と単語(空白・タブ・改行を含まない任意の文字列)と文字をカウントするプログラムを考えます。

行と単語と文字をカウントするプログラム
実行結果

【演習1-11】
単語カウント・プログラムのテストは、どのようにするか?もしバグがあるとしたら、それをあばきだすにはどんな入力をするのがよいのか?

いくつかのテストケースを実施しました。
ケース①:入力値がない

ケース②:入力値が改行

ケース③:入力値が空白とタブと改行

ケース④:1行に1語と改行

ケース⑤:1行に単語と改行

ケース⑥:1行に単語と空白と改行

もしバグがあれば、正常な値は得られない。

【演習1-12】
入力した単語を1行に一つずつ印字するプログラムを書け。

【演習1-12】のプログラム
【演習1-12】の実行結果

【演習1-13】
入力された単語の長さのヒストらグラムを水平方向に出力するプログラムを書け。

【演習1-13】のプログラム

単語の判定は、空白と改行とタブで判定します。
単語の長さは、あらかじめ10文字までとし、11文字以上はオーバーフロー文字でカウントします。

ヒストグラムの長さのMAXは、15です。
maxvalueには、w[1]~w[10]に格納されているカウントの最大値が格納されます。

以下のケースでは、文字数9個がMAXになるので、9個のときヒストグラムの長さは15になります。
その値を式に当てはめて、ヒストグラムの長さをそれぞれ定めます。

【演習1-13】の実行結果

1.6 配列

入力文字(数字と空白文字(ブランク・タブ・改行)とその他文字)をカウントするプログラムを作成するのですが、配列を使ってコーディングしてみます。

入力のカテゴリは、数字の0~9、空白文字、その他文字となります。
数字をそれぞれカウントするのに、10個の変数を使用するよりも、配列(ndigit[])を使用します。

数字、空白文字、その他の文字をカウントするプログラム
実行結果

数字のカウントは、左から0,1,2,3,4,5,6,7,8,9の数になります。

【演習1-14】
入力文字の文字ごとの頻度のヒストグラムを出力するプログラムを書け

入力した文字の中に、それぞれのキャラクター文字が何個存在しているかをカウントし、
ヒストグラムを表示するプログラムです。
タイトル入りで作成してみました。

【演習1-14】のプログラム

改行もカウントされています。
2回改行をして、CTRL+”D”でループを抜けました。

【演習1-14】の実行結果

1.7 関数

C言語の関数は、Fortranのサブルーチンあるいは関数、Pascalの手続きあるいは関数に対応する。
関数とは、入力値がその中身で定義されているロジックにより、別の値を得るものである。

これまで、C言語でデフォルトで定義されている関数を使用してきているが、ここでは自作の関数を練習する。
まずは、べき乗する関数を作成してみる。

なぜならC言語には、Fortranのように指数演算子(**)が無いので、整数mを正整数nでべき乗するpower(m,n)を作成する。
例えば整数2を5乗する場合、power(2,5)で関数にパラメータを与え、戻り値で32が求められる。

関数の定義は、mainルーチンの下に記述する

2と-3のべき乗を求めるプログラム
実行結果

【演習1-15】
1.2節の温度換算プログラムを、変換のための関数を使うように書き直せ。

【演習1-15】のプログラム
【演習1-15】の実行結果

1.8 引数ー値による呼出し(Call by value)

C言語では、呼ばれた関数が呼んだほうの変数を変えることができません。
関数のルーチン内では、引数は局所的変数として使用されます。

C言語は、他の言語(Call by reference(参照による呼出し))とは違った特徴を持っています。
当然、引数で渡される変数値も同様に、関数内で値を変えても呼出したほうの変数値に影響しません。 

もし、呼び出されたほうで変数が変更され、呼び出したほうでもその値を必要とするのであれば、アドレス(ポインタ指定)での受け渡しをすることになる。

1.9 文字配列

C言語における配列で最も一般的なものは、文字の配列です。
ここでは、文字配列の使い方とそれを操作する関数を作成し、実際の動きを確認してみます。

プログラムの骨組みは、以下のようになります。

while (別の行がある)
if (以前に一番長かった行よりも長い)
その行を格納する
その長さを格納する
一番長い行をプリントする

これをプログラムで記述します。
getline関数はすでにライブラリに定義されているので、getline1という名前で定義します。

また、文字列の終わりを示すために、文字列の最後に’/0’(ヌル)を置きます。
実際のgetline関数でもこの方法が使われているので、それに準じて使用します。

一番長い行をプリントするプログラム
一番長い行をプリントするプログラムの実行結果

【演習1-16】
一番長い行を印字するプログラムのmainルーチンを書き直して、任意の長さの入力行群の長さ、およびテキストのできるだけ多くの部分を正しく印字できるようにせよ。

【演習1-16】のプログラム

このプログラムは、1000文字を超える文字列も比較の対象とし、1000文字を超える文字を省略して表示するようにしています。
文字列の最後を示すために、必ずヌルを代入しています

また、プログラムではMAXLINEを1000としているので、1000文字を超えるテストがやりにくい。
テストの便宜上、MAXLINEを50に変更して、50文字を超える場合のテストを行います。

【演習1-16】のプログラム(MAXLINEを変更)
【演習1-16】の実行結果

【演習1-17】
80字より長い行をすべて印字するプログラムを書け。

【演習1-17】のプログラム
【演習1-17】の実行結果

80文字を超える文字列だけを表示しています。

【演習1-18】
各入力行から、行の後のブランクやタブを取り除き、かつ空白行はすべて削除するようなプログラムを書け。

ブランクやタブは目で確認ができないので、テストでは、ブランクは「*」を表示し、タブは「#」を表示させることにした。

【演習1-18】のプログラム
【演習1-18】の実行結果

【演習1-19】
文字列sを逆に並べる関数reverse(s)を書け。さらに、この関数を使って、入力を一時に1行ずつ逆転するプログラムを書け。

【演習1-19】のプログラム
【演習1-19】の実行結果

1.10 外部変数と通用範囲

mainの中の変数は、main固有のものであり、局所的なものであります
他の関数から直接アクセスできない。

そのような変数は、関数が呼ばれたときだけ存在し、制御から離れれば消えてします。
通常、それを自動変数と呼びます。

自動変数とは逆に、関数が呼ばれたり、終了しても値が存在する変数のことを外部変数と呼びます。
ここでは、外部変数の定義方法と関数と関数間での値のありかたを解説します。

外部変数は、関数の外側で定義しなければなりません。
実際の記憶領域が割り当てられ、また、これらの変数は、使用する関数でも宣言する必要があります。

externではきりと宣言(省略可能)します。
実際のやり方は、プログラムの先頭で外部変数を定義すれば、以降のextern宣言は省略されます。

ここで注意することは、変数の定義と宣言との違いです。

「定義」とは変数が実際につくられ、あるいは記憶が割り当てられたところを示すのに対し、「宣言」とは変数の性質は指定されているが記憶が、記憶割当てはされていないことを示す

【演習1-16】で作成した、最大長の文字列を求めたプログラムを外部変数を使用して書き換えます。

外部変数を使用したプログラム
外部変数を使用したプログラムの実行結果

自動変数を使用していたときと同じ結果が得られることが確認できました。

【演習1-20】
入力されたタブを、次のタブ・ストラップまでのスペースを埋める適当な数のブランク(空白)で置き換えるプログラムdetabを書け。タブ・ストラップの位置は、例えばn文字ごとというように固定して考えよ。nは変数にすべきか、記号パラメータにすべきか?

ブランク(空白)ではわかりにくいので、”*”に置き換えました。

【演習1-20】のプログラム
【演習1-20】の実行結果

【演習1-21】
ブランクの列を同じスペーシングを行う最少の数のタブおよびブランクで置き換えるプログラムentabを書け。detabと同じタブ・ストップを使え。タブ・ストップに達するのに、タブあるいは単一のブランクのいずれかで十分なときに、どちらを使うべきか?

8文字未満のブランクならブランクのままに、8文字ならタブに置き換えます。
半端なブランクもブランクのままです。

ブランクは不可視なので、アスタリスク(‘*’)に置き換えて表示させ、タブも文字列(‘<-TAB–>’)に置き換えて表示させるようにしました。

【演習1-21】のプログラム
【演習1-21】の実行結果

【演習1-22】
長い入力行を入力のn字目にある最後の非ブランク文字の後で、折りたたむプログラムを書け。プログラムは、行が非常に長くても、また指定桁までにブランクもタブもない場合についても、ちゃんと動作するようにせよ。

【演習1-22】のプログラム
【演習1-22】の実行結果

【演習1-23】
Cプログラムからすべてのコメントを除去するプログラムを書け。引用符で囲まれた文字列や文字定数を正しく扱うことを忘れないこと。Cのコメントは入れ子
になっていない。

【演習1-23】のプログラム
【演習1-23】の実行結果

【演習1-24】
カッコ、中カッコ、大カッコのつり合いがとれていないといったプログラムの基本的な構文エラーのチェックを行なうプログラムを書け。引用符・2重引用符・コメントなどの処理も忘れないこと。(このプログラムを完全に汎用的にしようとすると、これを書くのは難しくなる)

【演習1-24のプログラム

「main()」の直後の”{“を省いて、アンバランスエラーにしてみました。

【演習1-24】の実行結果

まとめ

挫折を経験したC言語の再学習に挑戦することにしました。
昔に買ったテキストを使用します。

プログラム言語の習得は暗記ではなく、とにかくコードを多く記述し、実行を繰り返すことです。
習うより慣れろ!」というところでしょうか。

まだ入り口段階ですが、この鉄則に従い、C言語の習得に励んでいこうと決めました。
今度こそ、最後までめげずに学習を進めてまいります!

目標は「バリバリのC言語プログラマー」です!

また、C言語を拡張したC++やC#などがWebアプリケーションの開発で使われていますが、原形のC言語が理解できていないと、拡張言語の方も習得が苦しいのではないでしょうか。

柔軟性を考えれば、まずはC言語を征服することだと言えます。

コメント

タイトルとURLをコピーしました