C言語

C言語 ポインタ

はじめてC言語を勉強したほとんどの方がポインタを理解できずに苦しんでいます。


なにをかくそう僕もC言語のポインタが分からず、はじめて学習したときは3日3晩苦しみました。

C言語のポインタが分かりにくいのには2つ理由があります。
  • 使い道がわからない。
  • そもそもC言語の文法が悪い。


続きを読む
このエントリーをはてなブックマークに追加

最後の講義です。ここでは応用的な機能を学習します。

whileforと同じく、ループを実装する機能です。ですが、扱い方はforよりシンプルです。

while(継続条件){

}


whileは{}の中を有効範囲とし、継続条件が真である限り有効範囲を処理し続けます。
継続条件が偽になった時、whileから脱出します。


118


ちなみに、真偽は整数値で表すことができます。
0が偽を表し、それ以外の数が真を表します。

問36:ということはwhileの継続条件に1と書くとどうなるでしょうか。




ご明察の通り、whileでできることはforでもできます。
なぜなら、forにも継続条件を書く欄があるからです。for(初期化; 継続条件; 終了処理)

時代背景的には、まずwhileが先に生まれ、forが存在しなかった時代があります。
whileも単純にコーディングできる点が魅力なのですが、本講義でwhileを取り扱うとなると、forも指導する必要があり、ページ数が増えてしまう為にF限目まで敢えて教えていませんでした。
皆さんはwhileもforもどちらも使いこなして下さい。
アドバイスですが、forは回数が決まっているループに、whileは回数が決まっていないループに使うと良いでしょう。


問37:次のコードをforで書き直しなさい。

118


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
更に、whileの親戚にdo while;があります。しかしこれはあまり使われません。
第一に、whileで事足ります。第二に使いこなすのが少し難しいです。第三に、コードが読みにくくなるので使うべきではありません。

do {

} while(継続条件);


do whilewhileの相違点は、{}の有効範囲が、継続条件の真偽に関わらず1度は実行される点です。
しかし、継続条件がコードの下方に記述されるため、上からプログラムを読み進めていく時に、do whileの条件を探す為に一度下にスクロールする必要性が生じる為、あまりきれいな文法ではありません。使うべきではない文法を練習しても仕方ないのでここでは紹介のみに留めます。どうしても使う必要が生じたら各自調べて下さい。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
goto という文法もあります。しかしgoto は現在では使われていません。gotoは好きな行へ処理を飛ばすことができるのですが、上から下へ流れていく基本原則を破壊するので、プログラムが読みにくくなります。
しかも、理論上gotoを使用しなくてもあらゆるプログラムは書けます。

また、多重ループからの脱出に使用できるという反論もありますが(かつてはボクもこの意見でした)、そういう場合はループそのものを関数化して、return;するべきだと思います。

使うべきではない文法を練習しても仕方ないのでここでは紹介のみに留めます。どうしてもどうしても使う必要が生じたら各自調べて下さい。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
条件演算子「?」はif の単行版です。

短いifを書く時は、?を使うとすっきり書くことができます。
しかし、同じことがifでもできるので覚えても覚えなくても良いです。

122


次のソースコードは同じ挙動をします。


121

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ifは実は単行でも使用できます。{}で囲まなかった場合は、ifの有効範囲は次のセミコロン(;)までとなります。

123

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
forも単行でも使用できます。{}で囲まなかった場合は、forの有効範囲は次のセミコロンまでとなります。

124

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
continue;はループの内側でループの終端へジャンプさせる文です。

125

あまり使用しないので、頭の片隅にでも入れておいて下さい。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if else の後ろに繋げることもできます。else で繋いだ箇所をelse if と呼びます。

else ifは、先頭のifが偽の時に初めて条件が判定されます。
先頭のifが真の時はelse ifは偽扱いとなります。
もちろん文末のelseも偽扱いです。

126


もし、else ifを使用しないと、文末のelseは直近のifのみを判定対象とします。

127

elseif (Number == 200)以外の時に発動するので、Number==100の時も頑張りましょうと表示されるのです。

また、else if は∞に連結できます。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
short型とfloat型はそれぞれint型とdouble型のミニチュアです。

int(整数型) :-2,147,483,648 ~ 2,147,483,647(-21億から21億までの値を保存可能)
short(整数型):-32,768 ~ 32,767
double(少数型):1.7E +/- 308 (15 桁)
float(少数型):3.4E +/- 38 (7 桁)

現在はあまり使われていません。
今はPCの性能が上がったので、節約の為にミニチュアの変数を使う必要はないのです。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
int型にunsignedを付けると、符号なし型になります。つまり-(マイナス)符号が付けられなくなるということです。その代わり、正数の最大値は2倍になります。

unsigned int:0 ~ 4,294,967,295(0から42億までの値を保存可能)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
変数は、同じ型であれば,演算子で区切ることで連続して宣言、初期化できます。

128


しかし、うっかり初期化を忘れてしまった時に気づきにくいのであまり推奨されていません。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cではソースファイルのどこに関数を書いても構いません。
1枚のソースファイルへ1つの関数を書くか、.hファイルへ記述するのが原則ですが、
main関数の上に書いても認識されます。

131


main関数の下へ書く場合、関数のプロトタイプ宣言が必要です。
プロトタイプ宣言とは、.hのインクルードと同じことをしているだけです。


もしint Add5(int i);がない場合、main関数へ突入した時点でAdd5の正体をコンパイラが掴めません。なので、main関数へ処理が突入する前にプロトタイプ宣言でコンパイラへ関数を教えてあげる必要があります。プロトタイプ宣言は、関数の戻り値と名前と仮引数を記述し、行末へセミコロン;を付けます。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
整数へのポインタを持つこともできます。

129


通常の変数は他の関数から参照することはできませんが、ポインタはそれが可能です。
なぜなら、ポインタ変数へ入っているデータは、メモリ上のアドレスだからです。
つまり、ポインタ変数を引数へ渡せば、その関数から他の関数の変数を操作することができるのです。ただ、数値データをポインタで操作するのは処理速度的にも好ましくないので、あまり推奨されていません。なので頭の片隅にでも入れておいて下さい。

130


文字列定数を代入する時は&を付けませんでしたが、それ以外の型であれば&を付けて下さい。例えば整数型の変数へ入っている値がアドレスかもしれないとコンパイラは解釈するからです。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ポインタで指し示した文字列定数を変更することはできないと紹介しました。
しかし、ポインタ変数へ新しい文字列定数のアドレスを代入することはできます。


134


ところで非公式の呼び方というか、ボクが勝手に呼んでいるだけですが、ポインタ変数ではなく、アドレス変数と呼んでもよいかもしれません。ポインタ変数とは、アドレスを格納する変数なので。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
実はポインタ変数は連続して初期化できません。
ポインタ変数:アドレスを格納した変数
なぜなら、ポインタ変数の正しい宣言方法はint *p;だからです。

しかし、Cはフリーフォーマットなので、int* p;と宣言しても問題ないのです。この方が読みやすいですよね。

ポインタ変数を連続初期化する時は、int *p, *q;と書きます。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
実はchar型は整数型としても使用できます。
その場合、格納できる値の範囲は–128 ~ +127です。
unsigned charの範囲は0 ~ 255です。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
インクリメント演算子とデクリメント演算子は、変数の後ろに付けることもできます。

132


ただし、前置verと後置verは挙動が若干違います。

133


後置インデクリメントはその式の中で最も遅いタイミングで値を更新します。
なので、Answerへ代入が行われた後で値が更新されたのです。

また、後置インデクリメントは前置インクデリメントよりもパワーを必要とするので、現在はあまり使われていません。C++で書くときは基本、前置インデクリメントを使います。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
代入演算子は=だけではありません。
+=は代入先へ右辺の値を加算します。(例:Answer += 2; // Answerへ2を加算)
-=は代入先から右辺の値を減算をします。(例:Answer -= 2; // Answerから2を減算)
*=は代入先へ右辺の値を乗算します。(例:Answer *= 2; // Answer*2をAnswerへ代入)
/=は代入先へ右辺の値を除算します。(例:Answer /= 2; // Answer/2をAnswerへ代入)
%=は代入先へ右辺の値を除算した余りを代入します。(例:Answer %= 2; // Answer%2をAnswerへ代入)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
char型に限らず、int型やdouble型でも配列を宣言することができます。
char型の時と使用方法は同じです。

135


なお、{}で代入できるのは初期化の時だけです。
また、{}で指定しなかった箇所は0が代入されます。
さらに、文字列定数を初期化する時の手法は使えません。
136

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cでは配列は多次元に拡張できます。

137

しかしややこしいのであまり使われません。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#define マクロで定数を定義し、名前を付けることができます。

#define MAX 3
と書いた場合、ソースコード上でMAXという単語はコンパイル時に3として扱われます。

169

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cではメモリを実行時に確保することができます。
通常のプログラムでは、実行前に必要なメモリ(変数の領域)を確保するのですが、実行中に確保することもできるのです。

例えばユーザーが入力した名前の文字数に応じてchar型の変数を確保することもできます。

次のプログラムでは、実行時にint型の領域を3つ確保しています。

139


mallocとfreeを使います。
mallocでメモリを確保し、freeで確保したメモリを解放します。
freeで解放しないとメモリリークを起こします
メモリリーク:使わない領域がメモリにできる現象

mallocとfreeはstdlib.hをインクルードすることで使用できます。
mallocの引数へは確保する領域をバイト単位で指定します。
このプログラムでは整数型(4バイト)を3つ分確保して使用しています。
mallocの値を12以下にするとエラーとなります。しかし、メモリの確保は実行時に行われるので、コンパイラはエラーを検出できません。

戻り値がvoid型なので、キャスト演算子で強制的に型を合わせます。
mallocの戻り値はアドレス変数です。確保した領域の先頭のアドレスを返却します。

freeへは、解放すべきアドレス変数を渡します。

このソースコードの値を代入する部分へ着目してみましょう。
*MemoryPointer = 1;
*を付けるとそのアドレス変数が指し示す実態を意味します。
*MemoryPointerは整数型ですから、整数の値を代入できます。

*(MemoryPointer + 1) = 2;
は、MemoryPointerというアドレス変数の次のアドレス変数を意味します。

mallocで確保した領域は連番で配置(char型の配列と同じ)されているので、アドレス変数へ1を足すことで次の領域を表すことができるのです。つまり+1で4バイト移動したことになります。

メモリの実行時確保を使用せずとも同じ処理をするプログラムは書けるので、初学者のうちは頭の片隅へ置いておく程度でよいでしょう。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
お疲れ様でした!
これでCの基本機能は全て学習し終えました。
しかし、これでもまだ紹介しきれていない機能もあるのです(バイナリファイルの取扱など)

ですが、ここまでの機能を駆使しても様々なプログラムは書けます。
応用的な力は、専門的な力であり、局所的にしか使用できないことが多いです。
なので、どの分野へ行っても、どのライブラリやAPIを使うにしても使用できる機能を中心に紹介してきました。

これから、様々な見たことのないソースコードがあなたを待ち受けるでしょうが、基本は変わりません。また、わからなければこのページへ辿り着いた時と同じように検索すればよいのです。

ここまでお読み頂き本当にありがとうございました。
より上を目指すのであれば他のWEBページを参照したり、本を購入してもよいでしょう。
このエントリーをはてなブックマークに追加

いよいよ最後の講義となりました。
この時間では、テキストファイルを作成し、その中へ文字列を出力するプログラムを作成します。
ゲームでいうところのSaveDateを作成するプログラムですね。

まず、FILE型のポインタ変数を作成します。
FILE* SaveDate;

FILE型とは、構造体で作られた、FILEに関する情報を格納する変数のことです。
FILEポインタをfopen関数が返すFILE型の戻り値で初期化して使用します。

fopen("テキストファイル名.txt", "属性");

テキストファイル名には好きな名前を付けて下さい。正し、.txtを付けるのを忘れないで下さい。
属性は
r (読み込み用に開く)
w (書き込み用に開く)
a (追記用に開く)

があります。今回は書き込み用に開くので、"w"を指定します。

FILE* SaveDate = fopen("SaveDate.txt", "w");

ファイルへ文字列を書き込むには、fprintfを使います。
fprintfには、ファイル構造体へのポインタ変数と、書き込む文字列を指定します。
ポインタ変数がファイル構造体のアドレスを保持しているので、ポインタ変数を指定するのです。
fprintf(SaveDate, "Stage1:Clear");

ファイルへの書き込みが終わったら、fcloseでファイルポインタを解放します。
fcloseにはファイル構造体へのポインタ変数を指定します。
fclose(SaveDate);


実行結果
116

例によって、fopenが安全ではないので、プラグマを指定しています。
実際の開発ではfopen_sを使用して下さい。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
続いて、ファイルからデータを読み込みます。

117


fopenの属性にはrを指定します。
また、ファイルからデータを読み込むには、fscanf関数を使用します。
fscanf(ファイル型のポインタ変数, "読み込むデータの種類", 読み込んだデータの格納先);

fscanf関数は、一行しか読み込まないことにご注意下さい。
複数行読み込みたい時は、その分だけ実行する必要があります。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

お疲れ様でした。
これでC言語の基礎はマスターできたといってよいでしょう。

これにて全ての講義は終了です。次回はおまけとして、応用的な機能を学習しますが、ここまで学習したことを組み合わせていけば、大規模なアプリケーションを書くこともできるでしょう。

しかし、Cの世界はまだまだ奥深く、よく分からない部分もたくさん出てくるでしょう。
そんな時は、ここまでやってきたように、検索して、勉強して下さい。

本を買っても良いですし、より詳しく解説しているHPを参照してもよいでしょう。

ここまで読んで頂き、本当にありがとうございました。挫折しなかったあなたを心から祝福します。
このエントリーをはてなブックマークに追加

ポケットモンスターというゲームをご存知でしょうか?
ポケモンでは、自分が捕まえたポケモンのデータはてんでバラバラです。
しかし、ポケモン1匹に対するステータスは大量にあります。
名前, ニックネーム, レベル, 経験値, HP, 攻撃力, 防御力, 特殊攻撃力, 特殊防御力, 素早さ, なつき度, などです。

これらのデータを全て個別の変数で宣言するとなると大変です。
名前も統一しなければなりませんし、10個の変数が必要です。

しかし、これらの変数をまとめて1つの変数にすることができるのです。
違う型であってもです。

struct 変数名 {
 メンバ;
};


115


このように、構造体を作成することで関連するデータをまとめることができます。
PokeDate1というのはあくまで汎用的なデータに過ぎません。

例えばピカチュウを捕まえた時に、PokeDate1のデータをPokeDate1型のHolding1変数へコピーすることができます。すると、Holding1のデータはピカチュウレベル1のデータとなるのです。

構造体に関しては、正直今から使いこなそうとする必要はありません。
C++では違う使い方をしますし、大量の変数を使う機会ができた時にまた考えれば良いでしょう。

C++では、構造体の代わりにより拡張された概念であるclassを使用します。

また、ポケットモンスターシリーズは任天堂と株式会社ポケモンの知的財産です。
このエントリーをはてなブックマークに追加

C言語には様々な関数(printfやscanf)があることは以前お話ししました。
そして、その時に関数を自作することもできるとお話ししました。

今回は、printfのような関数そのものを作成する方法を学びます。
これができれば、長大なソースコードを書くこともできるようになるでしょう。

ソリューション エクスプローラー→ヘッダーファイル の上で 右クリックし、追加→新しい項目を選んで下さい。
BlogPaint

名前はなんでも良いです。.hファイルを選択しているかだけ確認して下さい。
109


今回は、Original.hというファイルを作成しました。
今までインクルードしてきたStdio.hに似ていますね。
そうです。似ているというか同じなのです。

それでは、Original.hへ関数を記述していきましょう。
今回は、渡された引数1と引数2を加算した結果を返却する。Add関数を書いてみましょう。
110


こんな感じです。

Add関数のiとjに着目してみましょう。これは仮引数と言い、名前の通りその関数内で一時的に使用する変数を言います。今回は、int型の変数iとjを宣言しました。
つまりこの関数は、int型の整数を2つ受け取るということです。
Source.cppファイルにて、Original.hをインクルードすると、Add関数を使用できるようになります。
その時、Add(1, 2);のように指定するのです。すると、Add関数内部では、iは1、jは2となり、Add関数内部にてiとjを足した結果をreturn するようになるのです。

もちろん、Add(3, 5);とSource.cppファイル内で書けば、iは3、jは5となります。
つまり、iとjは渡される引数を汎用化した変数なのです。

この関数はint型の整数を返却するよう指定しているので、return 整数(i + j)を指定しています。

ものは試しで、実際にSource.cppファイル上にOriginal.hをインクルードしてAdd関数を使ってみましょう。
111


実行結果
112


自作の.hファイルをインクルードする時は、<>ではなく" "を使用して下さい。
また、Original.hファイルの1行目に #pragma onceとありますが、
これは万一多重インクルードしたとしても、問題なくプログラムを動かす為に存在するインクルードガードと呼ばれるものです。消さないで下さい。

もし消すと、次のソースコードはコンパイルエラーになります。(消さなかった場合はエラーになりません)


例えば、引数が必要ない関数を作成する場合、()内にvoidを指定することもできます。
……どこかで見たことがありますね。

そうです。main関数です。
main関数は最初に実行されるが故に、引数の取りようがないのです。
main関数へ引数を渡すとなると、もはや実行前に渡す必要があります。そしてその方法もあります。あまり使いませんが。

返却する値(戻り値)がない場合、voidを指定することができます。
その場合は、return ; とだけ書いて関数を返却します。


Add関数の下へP関数を追記してみました。

main関数から使用してみましょう。

113

引数がないので、()だけを書くことになります。Pだけだとコンパイラは変数と勘違いするので、関数を使う時は、引数がない場合でも必ず()を付けます。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ここで、変数の有効範囲についてお話しておきましょう。
変数は、宣言した箇所によって有効範囲が変わります。
関数あるいは{}の内部で宣言した変数はローカル変数と呼ばれ、そのブロック内でのみ有効です。
つまり、その関数あるいは{}が終了した時、その変数は解放されます。

関数の外部で宣言した変数はグローバル変数と呼ばれ、そのソースファイル上で有効となります。
このエントリーをはてなブックマークに追加

↑このページのトップヘ