C言語を１ヶ月勉強したノート

# はじめに
C言語は大学1年生の時に軽く触れたことあるよ

情報の授業はいつのまにか fortran になっていましたね

C言語なんて基本構文は特殊じゃないからいまさら学ぶことあるんですかね

ぽいんたー？ってなに？

古い教材で学び直した私のノートをここに残します


## 教材と参考文献
* [初心者のためのポイント学習Ｃ言語](http://www9.plala.or.jp/sgwr-t/)<br>
* [初心者のためのポイント学習Ｃ言語 目次](http://www9.plala.or.jp/sgwr-t/c/index.html)<br>
* [visual studio shortcuts](https://visualstudio.microsoft.com/keyboard-shortcuts.pdf)<br>
* [ASCIIコード一覧表](https://e-words.jp/p/r-ascii.html)<br>
* [printf()](http://www9.plala.or.jp/sgwr-t/c/sec05.html#s5-1)<br>
* [関数library](www9.plala.or.jp/sgwr-t/lib/lib.html)<br>
* [pointer](https://www.cc.kyoto-su.ac.jp/~yamada/programming/pointer.html)<br>
* [記憶クラス１](https://www.ibe.kagoshima-u.ac.jp/static/www1/edu/gengo0/p11.html)<br>
* [記憶クラス２](https://www.geeksforgeeks.org/storage-classes-in-c/)<br>
* [ビット演算子１](https://www.geeksforgeeks.org/bitwise-operators-in-c-cpp/)<br>
* [ビット演算子２](https://en.wikipedia.org/wiki/Bitwise_operations_in_C)<br>
* [コンパイラとプリプロセッサ](https://www.kushiro-ct.ac.jp/yanagawa/C-2017/21-0704/index.html)<br>

***
## ◆ 第２章データの扱い
### ◎ 定数

```mermaid
graph LR
    定数 --> 数値
    定数 --> 文字
    定数 --> 文字列
    数値 --> 整数
    数値 --> 実数:浮動少数
    整数 --> 8進数
    整数 --> 10進数
    整数 --> 16進数
```

### ◎ データ型
|型|型名|バイト幅|
|---|---|---|
|char|文字|1|
|int|整数|2|
|long|倍精度整数|4|
|float|少数|4|
|double|倍精度少数|8|


### ◎ 配列
型　配列名　要素数
```c
int num[9];
```
宣言時に（型のバイト幅）×（要素数）のメモリを確保する

#### ※文字列は文字の配列
```c
char gender = 'M'; // シングルクォーテーション
char str[] = "Hello"; // ダブルクォーテーションマーク
```

### ◎ ASCIIコード
`char` は整数を扱う文字型、つまり文字を計算できる

### ◎ 型変換
```
int sum, n;
doble average;
average = (double)sum / n;
```

### ３－３．インクリメント（++）・デクリメント（--）演算子
代入演算子（=）と共に用いると、結果が違ってきます
```c
n = 2;
a = ++n; // a=3, n=3
b = n++; // b=3, n=4
// 後置演算の場合、計算より先に代入される
```


***
## ◆ 第５章
### ◎ printf() と scanf()
```c
printf("こたえは %d です。\n", ans);
printf("整数を入力 > ");
scanf("%d", &input); // アドレス指定
```

|特殊文字|型|　｜　|特殊文字|型|
|---|---|---|---|---|
|%c|１文字|　｜　|%5d|スペース含め5文字|
|%d|10進数|　｜　|%010d|0詰め10文字|
|%x|16進数|　｜　|%12f|小数点含め12桁|
|%o|8進数|　｜　|%9.2f|小数点以下2桁|
|%f|少数|　｜　|%ld|long|
|%e|指数|　｜　|%lf|double|
|%s|文字列|　｜　|%-d|左詰め|

### ◎ visual studio 2019 セキュリティ問題
`scanf` → `scanf_s`
`strcpy` → `strcpy_s`
`strcat` → `strcat_s`
`gets` → `gets_s`

`scanf_s` で `%s` を読み込む際 `&` が不要だが、
第3引数(直後の引数)に最大文字数が必要


***
## ◆ 第７章 標準ライブラリ関数
```c
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
```


### ◎ 文字列比較
```c
if(strcmp(title, input_title) == 0){...}
```



***
## ◆ 第１０章 pointer
*months_p[0]
```c
int a, b;
int *p; // pointerの宣言（変数と同じ型）

p = &a; // a のアドレスを代入
*p = 100; // a の値に100を代入
b = *p + 1; // b の値に101を代入
```

<table><tr><td>

```c
char c;
char *p;

c = 'A';
p = &c;
```

</td><td>

|→|アドレス|メモリ|←|
|---:|:---:|:---:|:---|
|&c|1000|A|c|
|p|^|^|*p|
|||||
||2000|1000|p|

</td></tr></table>

```c
int *p, *q, *r;   /* p, q, r はint型ポインタ変数*/
int* p, q, r;     /* ポインタ変数なのは p だけ */
```


### ◎ 配列とpointerと文字列
<table><tr><td>

```c
char st[10] = "ABC";
char *p1, *p2;

p1 = &st[0];
p2 = st;
// st = &st[0] = &st[0][0]

printf("%c", *(p1+2)); // C
++p2;
printf("%c", *p2); // B
```

```c
char *p = "XYZ";
```
```c
char *months_p[3] = {
    "January",
    "February",
    "March"
}
```
</td><td>

||アドレス|メモリ|.|
|---:|:---:|:---:|:---|
|p|1234|X|*p|
||1235|Y|.|
||1236|Z|.|
||1237|\0|.|
|||||
||1501|J|*months_p[0]|
||1502|a|.|
|||||
||1508|\0|.|
||1509|F|*months_p[1]|
|||||
|||||
||5000|1234|p|
|||||
||5555|1501|months_p[0]|
||5556|1509|months_p[1]|
||5557|1518|months_p[2]|
</td></tr></table>


***
## ◆ 第１１章 function
### ◎ 関数間のデータ授受
<table><tr><td>

```c
int main(void){
    int a = 100;
    int sum = wa(a, 299);
}
// 実引数を仮引数に渡す
int wa(int x, int y){
    return x+y;
}
```
</td><td>

```c
int main(void){
    int a[10] = { 1, 2, 5, 6, -1 };
    int sum = goukei(a);
}
// 配列の先頭アドレスを渡す
int goukei(int *dt){
    int kei = 0;
    while(*dt != -1){
        kei += *dt;
        dt++;
    }
    return kei;
}
```
</td></tr></table>

### ◎ コマンドライン引数
```c
int main(int argc, char *argv[]) {
    // 引数の個数をチェック
    if (argc != 3) {
        puts("引数の個数が違います");
        return 0;
    }

    // 出力
    printf("合計 = %d\n", atoi(argv[1]) + atoi(argv[2]));

    return 0;
}
```
`argc` : 引数の総個数（プログラム名も含む）
`argv[]` : 引数の文字列を指すポインタの配列

[compile実行方法](https://www.javadrive.jp/cstart/install/index5.html#google_vignette)


***
## ◆ 第１２章 記憶クラス
### ◎ 有効範囲
* ローカル変数
* グローバル変数

### ◎ 記憶クラスの種類
* <span style="color:green">自動変数</span>
    ```c
    auto int num1;
    ```
    通常 `auto` は省略され、初期値は不定値
    関数が呼ばれるたびに初期化される
* <span style="color:green">外部変数</span>（＝グローバル変数）
    関数外で定義される変数
* <span style="color:green">静的変数</span>
    ```c
    static int num2;
    ```
    コンパイル時に一度だけ初期化される

### ◎ メモリ領域
Cで使うメモリ領域は４つに大別できる。
|領域名|配置されるもの|
|---|---|
|プログラム領域|プログラムコード|
|静的領域|外部変数と静的変数|
|スタック領域|自動変数、関数の引数、関数の戻り値、長い計算式の一時変数など|
|ヒープ領域|メモリ割り当て関数などで動的に確保|


***
## ◆ 第１３章 データ型の修飾
`short` や `long` は `int` を省略している
その他修飾子も普段省略してデフォルトを使用している

##### 表現範囲
データ型ごとの表現範囲はビット数で決まる
ビット数は処理系によって異なるが、以下が広く普及している
|data|bit|
|---|---|
|char|1|
|||
|short int|2*8|
|int|4*8|
|long int|8*8|
|||
|float|4*8|
|double|8*8|
|long double|16*8|

##### 符号ビット
デフォルトで先頭１ビットは符号ビット
符号なしに修飾して倍の正数を表現できる
```c
signed int num1; // =default
unsigned int num2; // 符号なし

auto signed short int // すべて省略なしで記述した場合
```

***
## ◆ 第１４章 複雑な演算子
### ◎ ビット演算子
|演算子|説明|
|-|-|
|&|AND|
|\||OR|
|\^|XOR|
|~|反転|
|<<|左シフト|
|>>|右シフト|

* 算術シフト : 符号ビットを保存する
* 論理シフト : 符号ビットを考慮しない

### ◎ 条件演算子（三項演算子）
```c
// 条件式?式１:式２
// 真なら式１、偽なら式２
int a,b,max;
max = (a >= b) ? a : b;
```

```c
// 引数のバイト数を求める
sizeof(char); // 1
sizeof(char[20]); // 20
sizeof(int[50] / int); // 50
```


***
## ◆ 第１５章 構造体
複数のデータをまとめて扱うには配列を用いますが、
異なる型のデータをまとめて扱いたいとき用いるのが「構造体」。

### ◎ structの宣言と初期化、入出力
メンバの参照は `.` を用いる。
```c
struct profile {
    int no;
    char name[20];
    double weight;
};

int main(void){
    struct profile my_info = {14105046, "TAKEDA", 59.9};
    struct profile students_list[20] = {
        {15095001, "AIBA", 48.7},
        {16135101, "MIYAZAKI", 76.5},
    };

    // 入力
    puts("学籍番号、名字、体重の入力");
    scanf_s("%d %19s %lf",
        &students_list[2].no,
        students_list[2].name, sizeof(students_list[2].name)
        &students_list[2].weight);

    // 一括代入
    student_list[4] = my_info;

    // 出力
    for(int i = 0; i < 4; i++) {
        printf("%d %s %5.1f\n",
            students_list[i].no,
            students_list[i].name,
            students_list[i].weight);
    }

    return 0;
}
```

### ◎ structの入れ子
初期値はすべてのメンバを順に並べる。参照は `.` を２つ用いる。
<table><tr><td>

```c
struct date {
    int year;
    int month;
    int day;
};
struct purchase {
    int id;
    char product_name[20];
    struct date purchase_date;
};
```
</td><td>

```c
struct purchase {
    int id;
    char product_name[20];
    struct date {
        int year;
        int month;
        int day;
    }
};
```
</td></tr><tr><td colspan="2">

```c
struct purchase purchase_info = {1, "book", 2024, 4, 1};
purchase_info.date.day = 22;
```
</td></tr></table>


### ◎ struct_pointer
構造体へのポインタを参照する際は、`構造体名 -> メンバ名`
```c
struct score {
    int id;
    char name[20];
    double average;
};

int main(void){
    struct score student_scores = {
        {1, "AOKI", 78.6},
        {2, "WADA", 57.1},
    };
    struct score *p = student_scores;

    for(int i=0; i<2; i++){
        printf("%d %lf", (p+i)->id, (p+i)->average);
    }

    return 0;
}
```

##### 入れ子の場合
`p` はポインタなので `->` 、 `date` はポインタではないので `.`
```c
printf("%d月分", p->date.month);
```

### ◎ struct_function
##### 関数に構造体を渡す場合
構造体全体をコピーするのは重いので、ポインタを使うほうが良い

##### 関数から構造体を受け取る場合
```c
// typedef
struct analyzed_score data = {
    int max;
    int min;
    double average;
};
// function
struct analyzed_score calc_data(int *p){
    strict analyzed_score data;
    int sum = 0, max = 0, min = 100;
    int i = 0;
    while(*(p+i)!=-1){
        sum += *(p+i);
        if(max < *(p+i)) max = *(p+i);
        if(min < *(p+i)) min = *(p+i);
        i++;
    }
    data.max = max;
    data.min = min;
    data.sum = (double)sum/i;
    return data;
}
```

### ◎ struct_self_ref
自己参照構造体
```c
struct list {
    char name[20];
    struct list *next;
}
```
リスト処理で用いられる
```mermaid
graph LR
    subgraph g1["list[1]"]
        direction LR
        11[データ部] ---
        12[ポインタ]
    end
    subgraph g2["list[2]"]
        direction LR
        21[データ部] ---
        22[ポインタ]
    end
    subgraph g3["list[3]"]
        direction LR
        31[データ部] ---
        32[ポインタ]
    end
    12 --> g2
    22 --> g3
```

##### ヒープ領域の利用
* 確保 : `malloc`
* 開放 : `free`


```c
// typedef
struct list {
    int key;
    char name[20];
    struct list *next;
};

// function
/** リストにデータを登録 */
struct list * add_list(int key, char *name, struct list *head) {
    // キーが重複する場合登録しない
    struct list* check_p = head;
    while (check_p != NULL) {
        if (check_p->key == key) {
            puts("キーが重複しています");
            return head;
        }
        check_p = check_p->next;
    }    
    
    struct list *p;
    // 記憶領域の確保
    if ((p = (struct list*)malloc(sizeof(struct list))) == NULL) {
        puts("malloc error");
        exit(EXIT_FAILURE);
    }

    // リストにデータを登録
    p->key = key;
    strcpy_s(p->name, name);

    // ポインタの繋ぎ換え
    p->next = head;
    head = p;

    return head;
}

/**
    リストのデータを削除
    リストの先頭のデータを削除する場合は head が変動する
*/
struct list * delete_list(int key, struct list *head) {
    // リストが空の場合
    if (head == NULL) return head;

    // 探索
    struct list *p = head;
    struct list *pre_p = head->next; // 先頭削除の場合headを開放するために一時格納
    while (p != NULL) {
        if (p->key == key) break;
        pre_p = p;
        p = p->next;
    }
    // not found
    if (p == NULL) {
        puts("キーが見つかりませんでした");
        return head;
    }

    // 削除
    // 先頭の場合
    if (p == head){
        free(p);
        return pre_p;
    }
    // それ以外の場合
    pre_p->next = p->next;
    free(p);
    return head;
}

/** リストの表示 */
void show_list(struct list *p) {
    while (p != NULL) {
        printf("%3d %s\n", p->key, p->name);
        p = p->next;
    }
}
/** リストの解放 */
void free_list(struct list *p) {
    struct list* p2;
    while (p != NULL) {
        p2 = p->next;
        free(p);
        p = p2;
    }
}


// main
int main(void) {

    struct list* head;
    char name[20];
    int key = 0;

    head = NULL;

    while (1) {
        puts("キーと名前（MAX：19文字）を入力。（終了条件 : Ctrl+Z）");
        // 終了条件
        if (scanf_s("%d %s", &key, name, 19) == EOF) break;
        // リストにデータを登録
        head = add_list(key, name, head);
    }

    // リストの表示
    show_list(head);

    // リストの解放
    free_list(head);

    return 0;
}
```

|address|memory|
|---|---|
|1234|1|
|1235|matsu|
|1236|NULL|
|||
|3157|2|
|3158|take|
|3159|1234|
|||
|4321|3|
|4322|ume|
|4323|3157|

```mermaid
graph LR
    subgraph head
        4321
    end
    subgraph g1[4321 ~ 4323]
        3 --- ume --- 3157
    end
    subgraph g2[3157 ~ 3159]
        2 --- take --- 1234
    end
    subgraph g3[1234 ~ 1236]
        1 --- matsu --- NULL
    end

    4321 --> g1
    3157 --> g2
    1234 --> g3
```


***
## ◆ 第１６章 その他の型
### ◎ typedef
既にある型に新しい名前を宣言する。
型名が長くなるビット列や構造体などの宣言を簡潔にするためによく使われる。
`#define` はプリプロセッサで解釈され、`typedef` はコンパイラで解釈される。
```c
typedef unsigned char BYTE;
BYTE data; // unsigned char data;
```
```c
#define NINZU 3 // 学生の人数
typedef struct {
	int no;	// 学生番号
	int ten[KAMOKU]; // 点数
	int goukei;	// 合計点
} SEISEKI; // 成績データ

int main(void){
	SEISEKI mycls[NINZU] = {
		{1001,85,74,63,90,0},
		{1002,78,65,70,62,0},
		{1003,89,92,88,76,0},
	};
}
```

### ◎ union(共用体)
各メンバがすべて同じアドレスから割り振られている
宣言が `struct` から `union` になるだけで、使い方は構造体と全く同じ
##### 共用体を含む構造体
ある条件のときのみデータの一部を変更したい場合、構造体を２つ作る必要がなくなる
```c
struct kamoku1 {
	int kokugo;
	int eigo;
	int syakai;
};
struct kamoku2 {
	int suugaku;
	int eigo;
};
struct seiseki {
	int no;
	char name[20];
	int sentaku;
	union kamoku {
		struct kamoku1 k1;
		struct kamoku2 k2;
	}ka;
};

int main(void){
    struct seiseki mycls[20] = {
        { 1001, "小柳幸江", 1, 76, 87, 69 },
        { 1002, "大場優", 2, 79, 48, 0 },
        { 1003, "新庄あやか", 2, 85, 98, 0 },
        { 1004, "野崎栄一", 1, 43, 76, 56 },
        { 1005, "本多健也", 1, 69, 91, 69 },
        { 9999, "" , 0, 0, 0, 0 },
    }
}
```

<table><tr><td>

```mermaid
erDiagram
    seiseki1{
        int no
        char name[20]
        int sentaku
        int kokugo
        int eigo
        int syakai
    }
    seiseki2{
        int no
        char name[20]
        int sentaku
        int suugaku
        int eigo
    }
```
</td><td>

```mermaid
erDiagram
    seiseki{
        int no
        char name[20]
        int sentaku
        union kamoku "ka"
    }
    kamoku{
        int kokugo "suugaku"
        int eigo "eigo"
        int syakai "0"
    }

    seiseki ||--o{ kamoku : ""
```
</td></tr></table>


### ◎ enum(列挙型)
int型整数値の並びに特定の名前を与える
```c
#define	SUN 0
#define	MON 1
#define	TUE 2
#define	WED 3
#define	THU 4
#define	FRI 5
#define	SAT 6
```
```c
// 上記宣言と同じく、0から順に定数に整数値を与える
enum week { SUN, MON, TUE, WED, THU, FRI, SAT };
```
```c
// 特定の値を与える場合
enum kabu { SUN=100, MON=96, TUE=90, WED=110, THU=130 };
// 列挙定数は一つ前に指定された値＋１を持つ
enum number { ONE=1, TWO, THREE, FOUR };
```
```c
/**
2024-04-26
演習16-3
takeda
*/
#include <stdio.h>

enum week { SUN, MON, TUE, WED, THU, FRI, SAT };

// function
/**
    曜日を取得する関数（Zellerの公式）
    @param y 西暦
    @param m 月
    @param d 日
    @return 曜日（0:日, 1:月, 2:火, 3:水, 4:木, 5:金, 6:土）
*/
int youbi(int y, int m, int d) {
    int w;
    if (m == 1 || m == 2) {
        m += 12;
        y--;
    }
    w = (5*y/4 - y/100 + y/400 + (26*m + 16)/10 + d) % 7;
    return w;
}

// main
int main(void) {

    int year, month, day;
    int yo;

    // input
    printf("西暦を入力 > ");
    scanf_s("%d", &year);
    printf("月を入力 > ");
    scanf_s("%d", &month);
    printf("日を入力 > ");
    scanf_s("%d", &day);

    // 曜日を取得
    yo = youbi(year, month, day);

    // 出力
    printf("%4d年%d月%d日は", year, month, day);
    switch (yo){
        case SUN:
            printf("日曜日です\n");
            break;
        case MON:
            printf("月曜日です\n");
            break;
        case TUE:
            printf("火曜日です\n");
            break;
        case WED:
            printf("水曜日です\n");
            break;
        case THU:
            printf("木曜日です\n");
            break;
        case FRI:
            printf("金曜日です\n");
            break;
        case SAT:
            printf("土曜日です\n");
            break;
    default:
        break;
    }

    return 0;
}
```

## ◆ 第１７章 ファイル入出力
|関数|書式|説明|
|---|---|---|
|~~`fopen()`~~<br>ｾｷｭﾘﾃｨ↓|FILE* fopen(char *filename, char *mode);|エラー時<br>`NULL`を返却|
|`fopen_s()`|int fopen_s(FILE **fp, char *filename, char *mode);|正常時`0`を返却|
|`fclose()`|int fclose(FILE *fp);|エラー時`EOF`を返却|
|`fgets()`|char *fgets(char *s, int n, FILE *fp);|1行取得し`s`に格納<br>最大`n-1`文字<br>終了時`NULL`を返却|
|`fputs()`|int fputs(char *s, FILE *fp);|`s`を書き込み|
|`getc()`|int getc(FILE *fp)|1文字取得<br>終了時`EOF`を返却|
|`putc()`|int putc(int c, FILE *fp)|1文字書き込み|
|~~`fscanf()`~~<br>`fscanf_s()`|>|ファイルから書式付き入力<br>fpを指定する以外は`scanf()`と同じ|
|`fprintf()`|>|ファイルへ書式付き出力<br>fpを指定する以外は`printf()`と同じ|

|mode|discription|ファイルがないとき|
|---|---|---|
|"r"|読み込み専用|エラー|
|"w"|書き込み専用|新規作成|
|"a"|追記専用|新規作成|
|"r+"|読み込み＆書き込み|エラー|
|"w+"|書き込み＆読み込み|新規作成|
|"a+"|読み込み＆追記|新規作成|


```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    FILE* fp;
    char s[256];
    char c;

    // file open
//    if ((fp = fopen("smpl.txt", "r")) == NULL){
    if ((fopen_s(&fp, "smpl.txt", "r")) != 0){
        printf("file open error!!\n");
        exit(EXIT_FAILURE);
    }

    // file read write
    // 1行単位で読み込み
    while (fgets(s, 256, fp) != NULL){
        printf("%s", s);
    }

//    // 1文字単位で読み込み
//    while ((c = getc(fp)) != EOF){
//        printf("%c", c);
//    }
    printf("\n");

    // file close
    fclose(fp);


    char* tp;
    char filename[256];
    char *ext_len;
    // file open
    if ((fopen_s(&fp, "BKCP.BAT", "w")) != 0) {
        printf("file open error!!\n");
        exit(EXIT_FAILURE);
    }
    // スペース区切りで抽出
    tp = strtok(s, " ");
    while (tp != NULL) {
        // remove extension
        ext_len = strrchr(tp, '.');
        strncpy_s(filename, tp, ext_len-tp);
        // file read write
        fprintf(fp, "COPY %s %s.BAK\n", tp, filename);
        tp = strtok(NULL, " ");
    }
    // file close
    fclose(fp);

    return 0;
}
```