ポインタ演算子のイメージ +0 +1 +2 +3 +4 +5 +6 +7 +8 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ char型 [] [] [] [] [] [] [] [] [] +0 +1 +2 ↓ ↓ ↓ long型 [] [] [] [] [] [] [] []|[] |← [0] →|← [1] →|← ポインタ演算の秘密 ・同じ1加算でもデータ型によって加算される量は異なる ・ポインタにおける1加算は実際にはデータ型で使用するバイト数だけ加算される ・配列変数の添え字([]演算子)も同様である ポインタ演算の秘密の補足 ・ただしプログラムを組む人は『実際の加算量』について意識する必要はない ・1加算すれば次のデータに到達できると考えてよい ・意識させないための仕組みがポインタである ポインタ変数と配列変数の違い ポインタ変数 配列変数 ---------------------------------------------------- 宣言 int *piPointer; int piArray[10]; 代入 できる できない 領域確保 1つ 指定個数 格納する値 ポインタ 各要素に通常の値 (アドレス) <ポインタ変数> piPointer = &iValue; <配列変数> piArray = &iValue; 代入はできない。 C言語研修(基礎編) 〜関数〜 関数とは ・ブラックボックスのことである A→[?]→B ・数学における関数と意味的には一緒 ・英語ではfunction(機能・作用)という C言語における関数とは ・基本的な関数(標準関数)は用意されている −C言語の構文だけでは何もできない ・自分で新しく関数を作ることもできる −関数を作成する場合は自分で定義する ・C言語にはサブルーチン(Sub Routine)という呼称はない −全ての関数という呼称で統一されている C言語における関数とは ・関数は関数から呼ばれていて初めて実行される −呼び出しされない関数は何もしない ・main関数のみプログラム実行時に自動的によばれる 関数作成する目的 ・1つの関数を小さくする −制御文などが深くならない様に ・共通部分を1つにまとめる −同じような処理を行っている部分を関数に −プログラムが小さくなる −修正量が減る 関数の宣言 戻り値の型 関数名 (引数リスト){ 関数の動作を実現する文 } 機能名 A → [ ? ] → B 関数名について ・関数が実現している機能を表す名称 ・半角の英数字と一部の記号が使用できる −全角文字や半角カタカナは使用できない −C言語全体を通したルールである ・関数名のネーミングルールはプロジェクト毎に異なる 引数リストについて ・引数は複数渡せる −それぞれの引数をカンマ(,)で区切って記述する ・引数にはそれぞれデータ型がある −引数名の前にデータ型名をつける −引数毎にデータ型が異なっても良い ・引数のない関数も定義できる −引数リストにvoidと記述する 引数の名称 仮引数 関数を定義する時に使用する引数 実引数 関数を呼び出し時に指定する引数 ※仮引数に何も入れない時は定義するときはvoidを入れる 実引数にはなにも入れない #includeint power(int iBase); : void main(void){ : a=power(i) ← iが実引数 } int power(int iBase){ ←int iBaseが仮引数 : } 戻り値について ・戻り値は1つだけしか返さない −複数の戻り値を返すことはできない ・戻り値にはデータ型がある −関数名にはデータ型がある −関数名の前にデータ型名をつける −『戻り値の型』=『関数の型』と考えてよい ・戻り値のない関数も定義できる −関数名の前にvoidと記述する 戻り値について ・戻り値がreturn文で返す −戻り値は自動的には返らない −return文は関数定義の本文中で使用する ・return文が使われると関数の途中でも関数から抜ける −return文はbreak文の関数版だと思ってよい −必要なら一つの関数にいくつ書いても良い が、変わりづらくなるので濫用はしない 関数定義の本文について ・他の関数で使用している変数名も変数宣言で使用できる −その場合は別の変数として扱われる ・return文により処理を途中で止められる −戻り値のある関数の場合は戻り値も指定する −わかりづらくなるので濫用はしない 関数プロトタイプの存在意義 ・以下の目的を実現するために関数プロトタイプは存在する −関数が存在することをコンパイラに伝える −引数の有無とデータ型をコンパイラに伝える −戻り値の有無とデータ型をコンパイラに伝える #include int power(int iBase); ← プロトタイプ宣言 : void main(void){ : a=power(i) ← iが実引数 } int power(int iBase){ ←int iBaseが仮引数 : } 関数プロトタイプの宣言 ・引数リストの)の後ろに";"を記述するだけ 引数の名前(iBase,iExp)は省略可能 int power(int iBase, int iExp); ~~~~~ ~~~~ ~ ・関数を使用するより前にプロトタイプ宣言を行う −通常はプロトタイプソースの先頭付近(もしくはヘッダ ファイル内)にまとめて記述する 引数の渡し方 値渡し 引数として値そのものを渡す (Call by Value) 関数側で仮引数の値を操作しても 呼び出し側の実引数には影響しない 参照渡し 引数として変数のアドレス(ポインタ)を渡す (Call by Reference) 関数側で引数に対して行った操作が 呼び出し側の実引数にも反映される 値渡しのイメージ ・関数側に新しく用意した変数に値をコピーするイメージ 呼び出し側 | 関数側 ⇒ [ ○ ] | [ ○ ] 値渡しのメモリイメージ 値渡しのメモリイメージ 呼び出し側 [ 10 ]----------- : | : |関数側に新しく用意した : |変数に値をコピーする 関数側 [ 10 ]←--------- 値渡しの関数 宣言 引数のデータ型を通常のデータ型にする int power(int iBase, int iExp); 使用 値を関数に渡す power(2, i); 備考 定数も実引数として指定できる 関数の独立性が保たれる 参照渡しのイメージ ・呼び出し側の変数を共有するイメージ 呼び出し側 | 関数側 ⇒[ ○ ] | 参照渡しのメモリイメージ 呼び出し側 [ 10 ]1000番地--- : | : |変数のアドレス(=1000番地) : |を関数側に渡す 関数側 [ 10 ]←--------- 参照渡しの関数 宣言 引数のデータ型をポインタ型にする void swap(int *iVal1, int *iVal2); 使用 変数のアドレスを関数に渡す swap(&iValue1, &iValue2); 備考 定数は実引数として指定できない −定数にはアドレスがないため 引数の参照渡しの注意点 ・関数の独立性を保つため、特定の場合を除いて使用しない ・原則として以下の場合に使用する −関数の処理結果(戻り値)を複数戻したい場合 −ポインタを引数として渡す場合 −配列変数を引数として渡す場合 文字列を関数に渡す ・文字列は文字(char)型の配列変数である ・配列変数はポインタ変数とほぼ同じ ・ポインタを引数に渡すには参照渡しを使用 −ポインタで受け取る −配列として受け取る 文字列を受け取る関数 宣言 引数をポインタ型もしくは配列にする void PutString(char *pszSrc); void PutString(char pszSrc[]); 使用 文字列の先頭アドレスへのポインタを渡す putString(pszBuffer); 備考 文字列定数も指定できる −コンパイル時にワーニングが出る場合あり 文字列を受け取る関数の補足 ・参照渡しで値が関数に渡すということは関数内で配列変数の値が 書き換えられてしまう可能性があるということである。 ・関数内で配列変数の値を書き換えないと言うことを明示するには const指定を引数指定に追加する void PutString(const char *pszSrc)