Cの基本12 ~配列・文字列~ 備忘録

### はじめに
私は初心者である。この備忘録を参考にされようとしている他の初心者の方は、ここに書かれていることを鵜呑みになさらぬようお願いしたい。何か間違いを見つけた方はコメント欄でご報告頂きたい。

___

### 配列
配列は、同じ型の変数が一列に並んだ大きな変数だ。
宣言は以下のように
```c
int a[12];
```
`int`型の変数が12個、メモリ内に並んで作られる。それぞれの変数は`a[1]`のようにしてアクセスできる。
一つ一つの変数は配列の要素と呼ぶ。インデックスはゼロから始まる。`[]`内の値は添え字という。

毎月の食費を格納し、呼び出せる例文だ。ちゃんと動いた。
```c
#include <stdio.h>

int main(void){
    int a[12];
    int i;

    for (i=0; i<12; i++){
        printf("Month %d: ", i+1);
        scanf("%d", &a[i]);
    }
    for (;;){
        printf("Month? ");
        scanf("%d", &i);
        if (i<1 || 12<i)
            break;
        printf("Month %d: %d\n", i, a[i-1]);
    }
    return 0;
}
```
添え字を`-1`にしても、最後の配列にある値にアクセスできるといった機能はCにはなさそうだ。勝手に新しい要素に代入みたいなこともできなかった。
```c
printf("%d\n", a[-1]);    // 動かん
```
```c
int a[2];
a[2] = 3;    // 動かん
```


##### 配列の初期化
要素数が初期値より多いとエラーになり、少ないと残りの要素がゼロになる。
```c
#include <stdio.h>

int main(void){
    int days[12] = {31,28};
    
    for (int i=0; i<12; i++)
        printf("%d\n", days[i]);
    return 0;
}    // 31と28が出力された後は0が１０個
```
下記のような初期化もできる。
```c
int days[] = {31,28};    // 自動で要素数が2になる
```
あくまで初期化のみであって下記のような代入はエラーが出る
```c
int days[];
    days = {31,28};    // エラーになる
```

##### 初期化の応用
`int`型の要素を1000個もつ配列`a`を宣言し、要素を全てゼロで初期化するなら以下のようにかける。
```c
int a[1000] = {0};
    printf("%d\n", a[500]);    //　出力は0
```
もしこれが、外部変数や`static`変数ならば明示的に初期化しなければ、すべての要素がゼロになる。
```c
static int a[1000];
    printf("%d\n", a[500]);    // 出力は0
```



##### 多次元配列の基本
まぁ想像がつくから、はしょる。
P391

___
([苦しんで覚えるC言語](https://9cguide.appspot.com/13-02.html)参照)
##### 要素数を求める

これは面白いな。
配列全体のサイズを求め、それを要素1つのサイズで割れば要素の数がわかる。
```c
static int a[1000];
    int len;
    len = sizeof(a) / sizeof(a[0]);
    printf("%d\n", len);    // 出力は1000
```

*[要コメント確認](https://qiita.com/yuto1009/items/5a2a5d37c568a9c3c834#comment-78ecb9da2369625e9829)*

##### memcpy関数

配列をコピーするための関数だ。

`memcpy(コピー先配列名､コピー元配列名、配列全体のサイズ)`の形の関数だ。
`memory.h`ファイルを`#include`して使う。

```c
#include <memory.h>
#include <stdio.h>

int main(void)
{
    int array1[] = { 42, 79, 13, 19, 41 };
    int array2[] = { 1, 2, 3, 4, 5 };
    int i;

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    memcpy(array2, array1, sizeof(array1)); /* array1 の全要素を array2 にコピー */

    for (i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("array2[%d] = %d\n", i, array2[i]);
    }

    return 0;
}
```
関数の戻り値はなく？(*あるそうだ*)これだけでコピーが完了する。

この関数はコピー元の配列がコピー先のものより長かったとしても、無理やりコピーして
バッファオーバーランとかいうタチの悪いエラーを引き起こすらしいから注意しよう。

___

### 文字を扱う
文字変数として`char`型がある。
`printf`関数で表示するには`%c`指定子を使う。
```c
#include <stdio.h>

int main(void){
    char c = 'A';
    printf("%c\n", c);    // 出力はA
    return 0;
}
```
文字はUnicodeがベースになってるから下記のコードでも出力は`A`
*コメント参照　環境によるそうだ*
```c
#include <stdio.h>

int main(void){
    char c = 65;
    printf("%c\n", c);
    return 0;
}
```
下記のコードの出力は`J`
```c
#include <stdio.h>

int main(void){
    char c = 'A' + 9;
    printf("%c\n", c);
    return 0;
}
```

___

### 文字列を扱う
Cでは文字変数の配列を使うことで文字列になるらしい。
文字列の最後にはEOS(End of String)を置いて文字列の終わりを表す。
文字列の指定子は`%s`

```c
#include <stdio.h>

int main(void){
    // char str[6] = {"M", "A", "R", "I", "O", "/0"};  // ダブルクオーテーションは動かないから注意
    char str[6] = {'M', 'A', 'R', 'I', 'O', '\0'};
    printf("%s\n", str);
    return 0;
}

```

なお、配列の初期化の時に要素の省略をした場合は、法則に従い、残りにはゼロが格納される。

##### 文字列の初期化
C言語にはもっと簡単に文字列を初期化できる方法がある。
それが文字列リテラル。
文字を一つ一つ区切らずに、`ダブルクオーテーション""`で囲うことで初期化に使える。
以下が例だ。
```c
#include <stdio.h>

int main(void){
    char str[] = "MARIO";
    printf("%s\n", str);
    return 0;
}    // MARIOと出力される
```
初期化の時しかまとめて代入できず、その後は一文字ずつ代入しなければならないのは変わらない。

##### 文字列を扱う関数

[このサイトを見た方が早いのでリンクだけ載せておく。](https://9cguide.appspot.com/14-03.html)

##### 文字列の入力

文字列を入力するときは以下のようにする。
```c
#include <stdio.h>

int main(void){
    char str[32];
    scanf("%s", str);  // 注目
    printf("%s\n", str);
    return 0;
}
```
`scanf`関数のときに`&`がない。`&`を使わないのはポインタと深く関わりがあるらしい。次章で扱うから今は保留。

*追記　下記のコード
文字列は初期化の時しかまとめて代入できないと書いたが、下記のコードのようにインプットがある場合は一度に入るようだ。*
```c
#include <stdio.h>

int main(void){
    char str[3];
    scanf("%s", str);
    printf("%s\n", str);
    return 0;
}
```

`memcpy`関数のところで書いたような、配列の大きさを超えて代入されるようなことを防ぐには下記のようなコードが使える。
```c
#include <stdio.h>

int main(void){
    char str[32];
    scanf("%32s", str);    // 32文字を超えると切り捨てられる。
    printf("%s\n", str);
    return 0;
}
```

ここに載せた以外の関数も先ほど示したリンクに記載されている。

___

> 今回の内容は富永和人氏の[新しいC言語の教科書](https://www.amazon.co.jp/%E6%96%B0%E3%81%97%E3%81%84C%E8%A8%80%E8%AA%9E%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8-%E5%86%A8%E6%B0%B8%E5%92%8C%E4%BA%BA-ebook/dp/B07P1TC98P)を参考に書き留めてます。