C言語入門

1
C言語入門
第8週
プログラミング言語Ⅰ(実習を含む。),
計算機言語Ⅰ・計算機言語演習Ⅰ,
情報処理言語Ⅰ(実習を含む。)
2
関数定義と関数呼び出し
3
教科書 pp.149-160.
関数の宣言・定義・利用
• 書式:
関数の宣言
戻り値の型 関数名(引数の宣言, ...);
.h ファイルへ書き出す
関数の定義
戻り値の型 関数名(引数の宣言, ...)
{
// 関数に行わせる処理
// ...
return 戻り値; // 戻り値の型がvoidの場合は不要
}
.c ファイルへ書き出す
関数の利用
変数名 = 関数名(引数, ...);
適宜呼び出す
4
教科書 pp.149-160.
関数(サブルーチン)
• C 言語は処理を関数にしてまとめる
• main も関数
コマンドライン引数の数
c_template.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Here is a main routine.
コマンドライン引数を格納した
複数の文字列へのポインタ
関数名、引数、戻り値の定義
関数の本体
return EXIT_SUCCESS;
}
フルセットの main 関数
5
教科書 pp.149-160.
(ユーザー定義)関数
• 関数は自分で作ることが出来る
• 機能、分量等、ある程度まとまった処理は
関数にまとめる
• 可読性が向上する
• 再利用が容易になる
• 関数の定義の書式:
C言語の関数は
0個以上の引数と
0または1個の戻り値を持つ。
引数や戻り値が不要な場合は
戻り値の型 関数名(引数の宣言, ...)
void を宣言するよう
{
推奨されている。
// 関数に行わせる処理
// ...
return 戻り値; // 戻り値の型がvoidの場合は不要
}
return 文
return 戻り値;
• 関数から抜けて呼び出し元へ戻り値を渡す
return_ex1.c
int sub()
{
return 123;
printf("hello\n");
}
return文を実行すると
呼び出し元に戻るので
それ以降の文は
実行されない
void main()
{
int x = sub();
printf("%d\n", x);
}
return文に与えた値が
関数の戻り値になり
演算に使用される
mintty + bash + GNU C
$ gcc return_ex1.c && ./a
123
return 文
• 戻り値の型は必要に応じて適切な型を選ぶ
return_ex2.c
例えばもし
浮動小数点数の値を
戻したいなら
戻り値の型は
doubleでないといけない
int sub()
{
return 1.23;
}
この例では戻り値の型はintなので
return文にdoubleを与えても
戻り値はint型にキャストされるので
整数になる
void main()
{
double x = sub();
printf("%f\n", x);
}
訂正2015-06-26
誤: double = sub();
正: double x = sub();
mintty + bash + GNU C
$ gcc return_ex2.c && ./a
1.000000
関数のプロトタイプ宣言
• 関数は使う前に宣言されている必要がある
prototype_ex1_err.c
void main()
{
double x = sub();
printf("%f\n", x);
}
double sub()
{
return 1.23;
}
未宣言の関数の引数や戻り値は
int型を仮定して暗黙的に宣言される
異なる引数や戻り値で同名の関数は
関数名が競合するため利用出来ない
mintty + bash + GNU C
$ gcc prototype_ex1_err.c
prototype_ex1_err.c:9:8: エラー: ‘sub’ と型が競合しています
double sub()
^
prototype_ex1_err.c:5:14: 備考: 前の ‘sub’ の暗黙的な宣言はここです
double x = sub();
^
関数のプロトタイプ宣言
• 関数は使う前に宣言されている必要がある
prototype_ex1.c
double sub();
void main()
{
double x = sub();
printf("%f\n", x);
}
double sub()
{
return 1.23;
}
int型以外の引数や戻り値を持つ関数は
あらかじめプロトタイプ宣言されていなければならない
mintty + bash + GNU C
$ gcc prototype_ex1.c && ./a
1.230000
10
教科書 pp.149-160.
(ユーザー定義)関数の例
• 閏年の判定を関数にまとめてみる
leap_year_func_ex4_2.c
#include <stdio.h>
#include <stdlib.h>
int is_leap_year(int year)
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int main()
{
int year;
fprintf(stderr, "year = ?\b");
scanf("%d", &year);
関数の定義
自分で作成した関数を
サブルーチンとして呼ぶ
printf("%d is%s leap year.\n", year, is_leap_year(year) ? "" : " not");
return EXIT_SUCCESS;
}
11
教科書 pp.153-160.
(ユーザー定義)関数の例
• 関数を使う前にはパラメータの型と数が
分かっている必要がある(コンパイラの都合)
#include <stdio.h>
#include <stdlib.h>
int main()
{
int year;
fprintf(stderr, "year = ?\b");
scanf("%d", &year);
使う時点で不明な戻り値の型はintを仮定される。
引数については何も仮定しない。
もし、実際に定義されている関数の型が異なると
型が競合してエラーになる。
printf("%d is%s leap year.\n", year, is_leap_year(year) ? "" : " not");
return EXIT_SUCCESS;
}
型が不明なので、このタイミングで
int is_leap_year(int year)
int is_leap_year(int); が仮定される。
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
[1] pp.86-89.
12
教科書 pp.149-160.
関数のプロトタイプ宣言
• 関数は使う前にプロトタイプ宣言する
#include <stdio.h>
#include <stdlib.h>
int is_leap_year(int year);
関数のプロトタイプ宣言
戻り値の型、引数の数と型を宣言する
int main()
{
int year;
fprintf(stderr, "year = ?\b");
scanf("%d", &year);
関数のプロトタイプ宣言さえしておけば
関数の定義はどこにあっても良い。
例えば外部のファイルにあっても良い。
printf("%d is%s leap year.\n", year, is_leap_year(year) ? "" : " not");
return EXIT_SUCCESS;
}
13
教科書 pp.203-206.
関数をファイルに分離する
• 再利用性が高まる(コピペしなくて良くなる)
is_leap_year_func.h
ヘッダファイル
#ifndef IS_LEAP_YEAR_FUNC_H
#define IS_LEAP_YEAR_FUNC_H
include ガード(二重includeを防ぐ)
int is_leap_year(int year);
#endif
関数の宣言
他所から使う際に、
関数名、引数、戻り値の
情報を得るために必要
is_leap_year_func_ex4_2.c
関数本体のソースファイル
#include "is_leap_year_func.h"
関数本体の定義
int is_leap_year(int year)
{
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
14
教科書 pp.203-206.
ファイルに分離した関数の利用
• main 関数の部分
is_leap_year_main.c
#include <stdio.h>
#include <stdlib.h>
#include "is_leap_year_func.h"
int main()
{
int year;
fprintf(stderr, "year = ?\b");
scanf("%d", &year);
ヘッダファイルの include
外部ファイルで定義した
関数を呼び出し
printf("%d is%s leap year.\n", year, is_leap_year(year) ? "" : " not");
return EXIT_SUCCESS;
}
15
分割コンパイル
• コンパイルする複数のCのソースファイルを
コンパイラに与えれば良い
mintty + bash + GNU C
$ gcc
is_leap_year_main.c is_leap_year_func_ex4_2.c
コマンドプロンプト + Borland C++
>bcc32 is_leap_year_main.c is_leap_year_func_ex4_2.c
与えるのはCのソースファイルのみ
ヘッダファイルは #include 文により
自動的に取り込まれる
16
プリプロセッサ
• 条件付きコンパイル
#if
定数式
#elif 定数式
#else
#endif
#if 0
// ここはコンパイルされない
#endif
条件に合った箇所のみを
コンパイラに渡す構文
定数式の結果は0と非0の真偽値
#if defined(__BORLANDC__)
// __BORLANDC__ という名前のマクロが
// 定義されていた場合のみコンパイルされる
#endif
#if defined(__BORLANDC__) && 0x0551 <= __BORLANDC__
// ...
#endif
定数式なら
演算も可能
17
プリプロセッサ
• 条件付きコンパイル
defined( ) の短縮表記
#ifdef __BORLANDC__
// ...
#endif
#if
#ifndef __BORLANDC__
// ...
#endif
#if !defined( ) の短縮表記
18
プリプロセッサ
• 定義済みマクロ
__LINE__
__FILE__
__func__
__FUNC__
現在の行番号
現在のファイル名
現在の関数名(C99)
現在の関数名(Borland C++)
#ifdef __BORLANDC__
#define __func__ __FUNC__
#endif
Borland C++ で
C99互換の定義済みマクロが
使えるようにする
条件付きコンパイル
19
教科書 pp.203-206.
関数をファイルに分離する
• 作業用変数を用いる場合
is_leap_year_func_ex1_1.c
#include "is_leap_year_func.h"
int is_leap_year(int year)
{
int leap_year_flag;
}
ローカル変数の宣言
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
leap_year_flag = 1;
} else {
leap_year_flag = 0;
}
} else {
leap_year_flag = 1;
}
} else {
leap_year_flag = 0;
}
ここは
leap_year_practice_2.c
そのまま
return leap_year_flag;
結果を戻り値として返す
ローカル変数
ブロック内の作業用。
ブロック外に
同じ名前の変数があっても
競合せず安全に使える。
ブロック(複文)
{ } で囲まれた領域の事
20
関数の差し替え
• 関数のプロトタイプ宣言が同じなら関数は差
し替えて使える。
mintty + bash + GNU C
$ gcc
is_leap_year_main.c is_leap_year_func_ex4_2.c
mintty + bash + GNU C
$ gcc
実装は異なるが
プロトタイプ宣言が同じ関数を
定義したファイルで差し替え
is_leap_year_main.c is_leap_year_func_ex1_1.c
教科書 pp.161-169.
変数のスコープ(有効範囲)
• 大域(グローバル)変数
• プログラム全体からアクセス可能
• 局所(ローカル)変数
• ブロック内からのみアクセス可能
C言語では { } で
囲まれた部分がブロック
21
22
教科書 pp.161-169.
変数のスコープ(有効範囲)
• ローカル変数は{}の中のみで有効
scopetest.c
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int gl = 100;
関数外で宣言するとグローバル変数
void sub(int lo)
関数の引数はローカル変数
{
{
ブロックを作成するとローカル変数の有効範囲を制限出来る
int lo = 400;
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
}
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
}
int main()
{
int lo = 200;
sub(300);
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
return EXIT_SUCCESS;
}
23
教科書 pp.161-169.
変数のスコープ(有効範囲)
• ローカル変数は{}の外側に影響していない
scopetest.c
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int gl = 100;
void sub(int lo)
{
{
int lo = 400;
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
}
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
}
int main()
mintty + bash + GNU C
{
$ gcc scopetest.c && ./a
int lo = 200;
sub(300);
sub : 14: gl=101, lo=401
printf("%-4s : %3d: gl=%d, lo=%d\n", __func__, __LINE__, ++gl, ++lo);
return EXIT_SUCCESS;
}
sub :
main :
16: gl=102, lo=301
23: gl=103, lo=201
24
教科書 pp.203-206.
関数の演習
• is_leap_year_func_ex1_1.c、
is_leap_year_func_ex4_2.c を参考に以下のファイルの
処理を関数にしてファイルに分離してみましょう。
•
•
•
•
•
•
leap_year_ex1_2.c → is_leap_year_func_ex1_2.c
leap_year_ex2_1.c → is_leap_year_func_ex2_1.c
leap_year_ex2_2.c → is_leap_year_func_ex2_2.c
leap_year_ex3_1.c → is_leap_year_func_ex3_1.c
leap_year_ex3_2.c → is_leap_year_func_ex3_2.c
leap_year_ex4_1.c → is_leap_year_func_ex4_1.c
• is_leap_year_main.c と一緒にコンパイルしてみて動作
を確認してみましょう。
25
参考文献
• [1] B.W.カーニハン/D.M.リッチー著 石田晴久
訳、プログラミング言語C 第2版 ANSI 規格準
拠、共立出版(1989)