プログラミング〈新〉作法 2. C言語からはじめよう

2.2K Views

March 10, 25

スライド概要

- C言語とは
- 入力・演算・出力
- 制御構造
- 関数
- ポインタ
- 構造体と共用体
- リソース管理

profile-image

機械学習や音声認識に関する書籍を執筆しています。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

プログラミング 新 作法 これからプログラムを書く のために ~ 言語からはじめよう 2. C 言語とは 入力・演算・出力 制御構造 関数 ポインタ 構造体と共用体 リソース管理 人 〉 〈 ~ C 1

2.

言語とは 2.1 C プログラミング言語の中でも最も基本的な言語の1つ C言語の歴史 ANSI C ベル研究所で のライブラリ作成 ⽤⾔語として開発 AT&T OS 後にUNIXの記 述⾔語となる コンパイラ 1972 C99 C11 ‧最初の標準化 ‧新しい型 ‧Unicode対応 ‧C++の取り込み ‧マルチスレッド C++11 ‧スマートポインタ C++20 ‧オブジェクト指向 GCC Clang ‧多⾔語、フリーソフト ‧LLVMによる最適化 1983 1989 2007 2020 2

3.

言語とは 2.1 C 言語の特徴 条件分岐・繰り返し・配列・構造体・関数定義など,高水準言語で取り入れ られている基本的な要素を持つ ポインタの使用やビット演算機能など,低水準言語である機械語に近い記述 も可能 基本ソフトウェアのような移植性が求められる対象のみならず,実行速度が 要求される組み込み分野等において現在でも広く使われている C 3

4.

2.2 入力・演算・出力 あらゆるプログラミング言語の基本的機能 出⼒ ⼊⼒ 演算 キーボード 記憶 ディスプレイ 4

5.
[beta]
2.2

入力・演算・出力

入力・演算・出力の例 (code2-1.c)
#include <stdio.h>
int main(void)
{
int amount;
const int price = 150;
printf("How many do you need?: ");
scanf("%d", &amount);
printf("Total : %d yen\n", price * amount);
return 0;
}
How many do you need?: 2
Total : 300 yen
5

6.

2.2 入力・演算・出力 変数 記憶装置(メモリ)に格納されたデータを参照するために,識別子(変数 名)によって区別されるもの 変数は格納されるデータの種類に応じた型を持つ 基本的には整数型か浮動小数点型のいずれか 定数 const を付けて宣言し,一度値が設定されると以降変更されない変数 C言語ではオブジェクト形式マクロとして定義することもある リテラル プログラム中に直接記述されるデータ 6

7.
[beta]
2.2

入力・演算・出力
言語の変数

C

整数型
値の範囲
short
無指定

long
long long

負号の有無

ポインタ
int

signed
unsigned

char

バイト

char c = 'A';

enum

浮動⼩数点型

int *p = &a;

unsigned long int a;
1

enum color {R, G,
B};

float

有効桁数:約7double pi =
有効桁数:約153.14;
double

long double

int *

〜 バイト
例)0以上の値のみで、広い範囲が必要なとき
2 8

有効桁数:処理系依存

char *

⽂字列の先頭を指す

char *str =
"ABC";

複数の変数をまとめたもの

配列 個々の変数には添字でアクセス
int ary[5];

構造体 個々の変数にはメンバー名
でアクセス
struct list {
int key;
double data;
struct list
*next;
};

7

8.

2.2 入力・演算・出力 言語の演算子 C 符号を反転 の値を1増や す &i // iのアドレス sizeof i // iのバイト数 ⼆項演算⼦ x + y // 算術演算 x < y // ⽐較演算 x > 0 && y > 0 // 論理演算 x = y // 代⼊演算 単項演算⼦ -i ++i // // i 三項演算⼦ x > y ? x : y // 果 ⼤きいほうが演算結 優先順位 ⾼ 処理 単項 算術 ⽐較 論理 低 代⼊ 左 右 結合性 7 - 3 - 1 x = y = 優先 0 優先 8

9.

2.2 入力・演算・出力 入出力関数 書式指定 変数のアドレス 書式指定 scanf("%d", &x) 変数‧式 printf("%d\n", x) 変数 キーボード ⼊⼒ x 出⼒ コンピュータ内部 ディスプレイ 9

10.
[beta]
2.2

入力・演算・出力

数値計算を行うプログラム例 : ニュートン法で平方根を求める (code2-2.c)
答えのありそうな範囲を繰り返し処理によって狭めていくことで,答えの近
似値を得る
参考 : [喜多他 23] プログラミング演習 Python 2023
http://hdl.handle.net/2433/285599 p.67-68

int main(void)
{
int x;
double r1, r2, rnew;
printf("Enter a positive integer: "); scanf("%d", &x);
rnew = x;

10

11.
[beta]
2.2

入力・演算・出力
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
printf("%7.5f < %7.5f < %7.5f\n", r2, rnew, r1);
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
printf("%7.5f < %7.5f < %7.5f\n", r2, rnew, r1);
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
printf("%7.5f < %7.5f < %7.5f\n", r2, rnew, r1);
return 0;

}
Enter a positive integer: 2
1.00000 < 1.50000 < 2.00000
1.33333 < 1.41667 < 1.50000
1.41176 < 1.41422 < 1.41667
11

12.

2.2 入力・演算・出力 配列 同じ型の多数の要素について,それらを順序よく並べた全体を1つの変数名で 宣言したもの 80 int score[5]; score[0] 70 65 score[1] score[2] 93 score[3] 77 score[4] 12

13.
[beta]
2.2

入力・演算・出力

配列変数の使用例

int main(void)
{
int no;
int score[5] = {80, 65, 70, 93, 77};
printf("Student number? (0-4): ");
scanf("%d", &no);
printf("Score: %d\n", score[no]);
return 0;
}
Student number? (0-4): 3
Score: 93
13

14.
[beta]
2.2

入力・演算・出力

文字と文字列
C言語では文字は1バイトの整数型として扱われる
文字列は文字の配列として表現され,終端はヌル文字(\0)で示される
#define STR_LEN 15
int main(void)
{
char str[STR_LEN] = "example string";
str[0] = 'E';
str[8] = 'S';
printf("%s\n", str);
return 0;
}
Example String
14

15.
[beta]
2.2

入力・演算・出力

文字配列の使用例

#define NUM 5
#define NAME_LEN 9
int main(void)
{
int no;
char name[NUM][NAME_LEN] = {"Alice", "Bob", "Caroline", "David", "Eve"};
printf("Student number? (0-%d): ", NUM-1);
scanf("%d", &no);
printf("Name: %s\n", name[no]);
printf("The first character of this Name is: %c\n", name[no][0]);
return 0;
}

Student number? (0-4): 2
Name: Caroline
The first character of this Name is: C

15

16.

2.3 制御構造 制御構造 : プログラムの流れを制御するための構造 条件分岐 : 条件に応じて実行する命令を変える if文,switch文 繰り返し : 命令を繰り返し実行する for文,while文,do-while文 構造化プログラミング 順次実行・条件分岐・繰り返しを組み合わせることで,任意の計算手順を記 述できる 16

17.

2.3 if 制御構造 文 分岐の条件を制御式で示し,その条件に応じて実行する命令を変える 条件) 条件成⽴時の処理 if ( 条件) 条件成⽴時の処理 if ( Start Start else 条件不成⽴時の処理 条件 条件成⽴時の処理 false 条件成⽴時の処理 条件不成⽴時の処理 End End 単純なif ⽂ false true true (a) 条件 (b) if 〜 else ⽂ 17

18.
[beta]
2.3

制御構造

単純なif文の使用例
int main(void)
{
int x;
printf("Enter a positive integer: "); scanf("%d", &x);
if (x <= 0) {
printf("Input error!\n");
return -1;
}
printf("You entered %d\n", x);
return 0;
}
Enter a positive integer: -3
Input error!
18

19.
[beta]
2.3

制御構造

switch

文: 分岐の条件を整数値で示し,その条件に応じて実行する命令を変える

int main(void)
{
int a;
printf("Enter integer (0-2): "); scanf("%d", &a);
switch (a) {
case 0: printf("small size\n"); break;
case 1: printf("medium size\n"); break;
case 2: printf("large size\n"); break;
default: printf("error!\n"); break;
}
return 0;
}
Enter integer (0-2): 1
medium size

19

20.

2.3 制御構造 for 文 ループ変数に対して,初期化・繰り返し条件,更新処理を指定する 本体は繰り返し処理の対象となる文またはブロックを記述する Start 初期化; 繰り返し条件; 更新) 繰り返し対象の処理 for ( 初期化 繰り返し条件 true false 繰り返し対象の処理 End 更新 20

21.
[beta]
2.3

制御構造

for

文の使用例

int main(void)
{
int x;
double r1, r2, rnew;
printf("Enter a positive integer: "); scanf("%d", &x);
if (x <= 0) {
printf("Input error!\n");
return -1;
}
rnew = x;

21

22.
[beta]
2.3

制御構造
for (int i=0; i<3; i++) {
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
printf("%7.5f < %7.5f < %7.5f\n", r2, rnew, r1);
}
return 0;

}
Enter a positive integer: 3
1.00000 < 2.00000 < 3.00000
1.50000 < 1.75000 < 2.00000
1.71429 < 1.73214 < 1.75000

22

23.

2.3 制御構造 文(前判定),do-while文(後判定) 条件が真の間,繰り返し処理を行う while 継続条件) 繰り返し対象の処理 do while ( 繰り返し対象の処理 while (継続条件); Start 繰り返し対象の処理 継続条件 true 繰り返し対象の処理 (a) 前判定の繰り返し(whileループ) Start true 継続条件 false false End End (b) 後判定の繰り返し(do-whileループ) 23

24.
[beta]
2.3

制御構造

while

文の使用例

int main(void)
{
int x;
double r1, r2, rnew, diff;
printf("Enter a positive integer: "); scanf("%d", &x);
if (x <= 0) {
printf("Input error!\n");
return -1;
}
rnew = x;

24

25.
[beta]
2.3

制御構造
diff = rnew - x/rnew;
while (diff > 1.0E-5) {
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
diff = r1 > r2 ? r1 - r2 : r2 - r1;
printf("%7.5f < %7.5f < %7.5f\n", r2, rnew, r1);
}
return 0;

}
Enter a positive integer: 5
1.00000 < 3.00000 < 5.00000
1.66667 < 2.33333 < 3.00000
2.14286 < 2.23810 < 2.33333
2.23404 < 2.23607 < 2.23810
2.23607 < 2.23607 < 2.23607
25

26.
[beta]
2.3

制御構造

文の使用例

do-while

int main(void)
{
int x;
do {
printf("Enter a positive integer: "); scanf("%d", &x);
if (x <= 0) {
printf("Input error!\n");
continue;
}
break;
} while(1);
printf("You entered %d\n", x);
return 0;
}
26

27.

2.3 制御構造 Enter a positive integer: -5 Input error! Enter a positive integer: 0 Input error! Enter a positive integer: 2 You entered 2 文 : 繰り返し処理を中断し,ループを抜ける continue文 : 繰り返し処理を中断し,次の繰り返し処理に移る break 27

28.

2.3 制御構造 構造化プログラミング 順次実行・条件分岐・繰り返しの組み合わせでプログラムを書く方法 goto文を極力使わず,ブロック単位で変数のスコープが限定できるので,プ ログラムの理解や保守が容易になる 条件 処理1 true 処理2 順次実⾏ 繰り返し条件 false 処理1 処理2 条件分岐 true 処理 false 繰り返し 28

29.

2.4 関数 言語の関数 C 呼び出し 実⾏開始 main 関数 ライブラリ 関数 終了 呼び出し 標準 ライブラリ ⼊出⼒ 数値計算 ⽂字列操作 ユーザ定義 呼び出し 終了 関数 終了 ライブラリ 関数 29

30.

2.4 関数 関数 C言語のプログラムには必ず1つの main 関数が必要 処理は main 関数から始まる 返却値(整数型)は, return 文でプロセス終了コードとして OS に返され る 0 は正常終了,それ以外(例 : 定数 EXIT_FAILURE )は異常終了 引数は,なし (void) の場合と,コマンドライン引数を受け取る場合がある なしの場合 : int main(void) または int main() コマンドライン引数を受け取る場合 : int main(int argc, char main *argv[]) 内容はプログラム全体の手順を簡潔に記述することが多い 30

31.

2.4 関数 ライブラリ関数 : 主要な機能は標準ライブラリとして提供されている プリプロセッサ指令 #include で各ライブラリのヘッダファイルを読み込む ヘッダファイル 機能 関数の例 printf , scanf stdio.h 標準入出力 stdlib.h 一般ユーティリティ malloc , free strnlen , strncpy string.h 文字列操作 math.h 数学関数・数学定数 sqrt , M_PI time , localtime time.h 時刻・日付 ctype.h 文字分類 isalpha , isdigit pthread_create , pthread_join pthread.h 並行処理 外部ライブラリには,数値計算や GUI などの機能を提供するものもある 31

32.

2.4 関数 ユーザ定義関数 大きな問題を,小さな関数に分割して組み合わせる(段階的詳細化) ユーザ関数を定義する方法 返却値型 関数名 (仮引数宣⾔並び) 関数頭部(ヘッダ) { 実⾏するコード 関数本体 (ボディ) } 関数が受け取るものが仮引数,関数呼び出し時に渡すものが実引数 32

33.

2.4 関数 スコープと記憶域期間 関数内で定義した変数は外部から参照・変更ができない 変数の有効範囲をスコープとよぶ ローカル変数は関数・ブロック内でのみ有効 グローバル変数はファイル内の全関数で有効 変数が値を保持している期間を記憶域期間とよぶ 自動変数は関数が実行されている間のみ値を保持 静的変数はプログラムの開始から終了まで値を保持 動的変数は必要な時に確保し,不要になったら解放 33

34.
[beta]
2.4

関数

関数を用いたプログラム例 (code2-2.c)
#include <stdio.h>
int input_number(void)
{
int x;
do {
printf("Enter a positive integer: "); scanf("%d", &x);
if (x <= 0) {
printf("Input error!\n");
continue;
}
break;
} while(1);
return x;
}
34

35.
[beta]
2.4

関数

double calculate_squareroot(double x)
{
double r1, r2;
double rnew = x;
double diff = rnew - x/rnew;
while (diff > 1.0E-5) {
r1 = rnew; r2 = x/r1; rnew = (r1 + r2)/2;
diff = r1 > r2? r1 - r2: r2 - r1;
}
return rnew;
}

35

36.
[beta]
2.4

関数

int main(void)
{
int x = input_number();
double sq = calculate_squareroot((double) x);
printf("Square root of %d is %7.5f\n", x, sq);
return 0;
}
Enter a positive integer: 2
Square root of 2 is 1.41421

36

37.

2.5 ポインタ ポインタとは メモリ上のアドレスを格納する変数 変数の型ごとに対応するポインタ型が存在する ポインタの型は,指すデータのサイズ情報を持つ 参照外しを使うと,ポインタ経由で値の取得や操作が可能 変数 のアドレスをポインタpに格納する // a int a = 10; int *p; p = &a; // int b = *p; 参照外しでポインタpが指す値を取得する 37

38.

2.5 ポインタ ポインタと関数呼び出し 関数呼び出し時にわたす実引数は,値渡しが原則 ポインタによる参照渡しで,関数内で引数の値を変更することが可能 変数 に整数を入力する // a int a; scanf("%d", &a); 38

39.
[beta]
2.5

ポインタ

ポインタと配列
配列名は,配列の先頭要素のアドレスを示すポインタ定数
配列の要素にアクセスする際には,添字演算子を使う
ポインタに添字の数値を加えてアクセスすることも可能
以下のコードでは b+2 は b のアドレスから 2 * sizeof(int) だけず
らしたアドレスを示す
配列の要素を参照する

//
int b[5] = {1, 2, 3, 4, 5};
printf("b[2]:%d
*(b+2):%d\n", b[2], *(b+2));
b[2]: 3

*(b+2): 3
39

40.

2.5 ポインタ 関数ポインタ 関数の実体はメモリ上に配置された命令列なので,その実体をポインタで指 すことができる 関数ポインタを宣言し,その値を切り替えることで実質的に関数に関数をわ たすことができる Listing 2.13 int add(int a, int b) { return a + b; } int multiply(int a, int b) { return a * b; } ... int x = 2, y = 3; int (*functionPtr)(int, int) = add; printf("%d + %d = %d\n", x, y, (*functionPtr)(x, y)); ... 40

41.

2.6 構造体と共用体 配列変数と構造体 配列変数は同じ種類のデータを複数個格納するもの 複数の種類の変数がひとつの関係でまとめられる場合の扱い方が必要 0 index int score[5]; 1 2 3 4 80 65 70 93 77 Alice Bob Caroline David Eve struct student { int score; char name[9]; }; 80 65 70 93 77 struct student table[5]; Alice Bob Caroline David Eve char name[5][9]; 41

42.

2.6 構造体と共用体 構造体 複数の変数をまとめて新たな型として定義したもの 構造体の名前を構造体タグとよぶ 各変数をメンバとよぶ 構造体の宣言 typedef を使うと宣言した構造体を型名として使える struct student { int score; char name[9]; }; 42

43.

2.6 構造体と共用体 構造体の配列の使用例 #include <stdio.h> #define NUM 5 #define NAME_LEN 9 typedef struct { int score; char name[NAME_LEN]; } Student; 43

44.
[beta]
2.6

構造体と共用体

int main(void)
{
Student table[NUM] = {
{80, "Alice"}, {65, "Bob"}, {70, "Caroline"}, {93, "David"}, {77, "Eve"}
};
for(int i=0; i<NUM; i++)
printf("name: %-9s score: %d\n", table[i].name, table[i].score);
return 0;
}

name: Alice
name: Bob
...

score: 80
score: 65

44

45.

2.6 構造体と共用体 共用体 同じメモリ領域を複数の変数で共有する あるメンバの値を変更すると,他のメンバの値も変更される 共用体の宣言の例 Listing 2-15 メモリの同じ領域を符号なし32ビット整数と符号なし8ビット整数4つの 配列として宣言し,この処理系のエンディアンを調べる union EndianTest { uint32_t i; uint8_t bytes[4]; }; 45

46.

2.7 リソース管理 ファイル入出力 ファイルを含めた外部との入出力をストリームという概念で扱う プログラム開始時に自動的に開かれるストリームには標準入力 (stdin)・ 標準出力 (stdout)・標準エラー出力(stderr) がある ファイルを扱うときは,ストリームを明示的に作成する stdin キーボード stdout stderr ディスプレイ ⼊⼒ file出⼒ コンピュータ内部 file 46

47.

2.7 リソース管理 ファイル入出力に用いる関数 ファイルを開く : fopen() 引数はファイル名と読み書きのモード,返却値はファイルポインタ ファイルから読み込む : fscanf() - 第1引数がファイルポインタ,あとは scanf() と同じ ファイルに書き込む : fprintf() 第1引数がファイルポインタ,あとは printf() と同じ ファイルを閉じる : fclose() ファイル読み込みの例 Listing 2-17 47

48.

2.7 リソース管理 動的メモリ確保 プログラム実行中に必要なメモリを確保する メモリの確保 : malloc() 引数は確保したいメモリのバイト数,返却値は確保したメモリの先頭ア ドレス メモリの解放 : free() 引数は解放したいメモリの先頭アドレス 適切にメモリ管理を行わないと,メモリリーク(メモリ使用量が増加し続け る)やダングリングポインタ(解放済みのメモリを参照する)などの問題 が発生する 48

49.

2.7 リソース管理 動的メモリ確保による柔軟なデータ構造 リスト : 要素の挿入や削除をポインタのつなぎ替えで行うことができる head 80 65 70 93 77 Alice Bob Caroline David Eve next next next next next NULL 49

50.
[beta]
2.7

リソース管理

リストの実装例(一部) Listing 2-18 (Code2-3.c)
typedef struct student {
int score;
char name[NAME_LEN];
struct student *next;
} Student;
Student* add_data(int sc, char *nm) {
Student *s = malloc(sizeof(Student));
s->score = sc;
strncpy(s->name, nm, NAME_LEN-1);
s->next = NULL;
return s;
}

50

51.

2.7 リソース管理 並行・分散処理 並行処理 複数の処理を同時に実行すること 高速化やリアルタイム処理を実現する手段 分散処理 データや処理を複数のコンピュータに分散して処理すること 並行・分散処理を行うためには,スレッドやプロセスを生成し,それらの間 でデータを共有する必要がある Listing 2-19 pthread ライブラリでは,スレッドを生成する関数が提供されている 複数のスレッドが同時に共有データにアクセスしないように工夫が必要 51

52.

2.8 まとめ 言語のパラダイム(手続き型プログラミング) C 段階的詳細化 問題 関数 関数 関数 関数 構造化プログラミング 順次実⾏ 条件分岐 関数 繰り返し データ構造 配列 リスト 52