C言語　文字列のポインタ、構造体[備忘録]

#文字列とポインタ
配列による文字列と
ポインタによる文字列は密接な関係があります。

・配列による文字列
 char  str [ ] = "ABC";
strはchar[ 4 ]型の配列です。　
A ⇨ str[0]
B ⇨ str[1]
C ⇨ str[2]
\0 ⇨ str[3]

・ポインタによる文字列
  char ＊ptr = "123";
ptrはcharのポインタである。
ptr[0] = 1
ptr[1] = 2
ptr[2] = 3
ptr[3] = \0

上記の二つは類似していると思います。ではどこが相違点なのか下記をご覧ください。
・配列による文字列

```c
/*配列による文字列の書き換え*/

#include<stdio.h>

int main(void){

char s[] = "ABC";

printf("s = \"%s\"\n", s);

s = "DEF";   /*エラー*/

printf("s = \"%s\"\n", s);

  return 0;
}
```
・ポインタによる文字列

```c
/*ポインタによる文字列の書き換え*/

#include<stdio.h>

int main(void){

char *p = "123";

printf("p = \"%s\"\n", p);

p = "456";   /*OK!!*/

printf("p = \"%s\"\n", p);

  return 0;
}
```
実行結果
p = "123"
p = "456"
ご覧の通りポインタによる配列は文字列の書き換えができることがわかりました。

----
ポインタによる文字数のプログラム
関数str_lengthで文字数を測るプログラムです。

```c
#include<stdio.h>

int str_length(const char *s){

  int len = 0;

  while (*s++)
    len++;
  return len;
}

int main(void){

  char str[128];

  printf("文字列を入力してください : ");
  scanf("%s", str);
  
  printf("文字列\"%s\"の長さは%dです。\n", str, str_length(str));

  return 0;
}
```
実行結果
文字列を入力してください : five
文字列"five"の長さは4です。

関数str_lengthの部分を走査していきます。

```c
  while (*s++)
    len++;
  return len;
}
```

この部分が非常にややこしかったと思います。
まず１行目while文の繰り返しの終了条件は0になれば終了します。fiveと入力しましたがこの中でいう0はfive\0になります。
このポインタ*sの中身はstr[0] = fになります。
それを踏まえ(＊s++)の処理は配列の要素を指すポインタのインクリメントは
s＋＋ は s = s + 1となります。1は要素数になるのでs[1]になります。
(＊s++)
s[0] = f
s[1] = i
s[2] = v
s[3] = e
s[4] = \0   ⇦ここでwhile文の繰り返し終了。

またここでは```len++;```の部分がありました。while文と一緒にカウントされていたので要素の部分と同じカウントがされたと思うので ```len = 4```となります。
文字数が計算できました。
#構造体
構造体...データをひとまとめにした宣言

```c
struct student{

  char  name[64];
  int   height;
  float weight;
  long  schols;

};
```

１行目structは構造体の宣言、studentは構造体タグ
2~5行目メンバ
このような宣言を4個のデータを集めてstruct studentという型を作るという。
では学生のデータ構造体のプログラムを実装します。

```c
#include<stdio.h>
#define NAME_LEN  64

struct student{
  char  name[NAME_LEN];
  int   height;
  float weight;
  long  schols;
};


int main(void){

  struct student takao = {"Takao", 173, 86.2};

  printf("氏名 = %s\n",     takao.name);
  printf("身長 = %d\n",     takao.height);
  printf("体重 = %.1f\n",   takao.weight);
  printf("奨学金 = %ld\n",  takao.schols);
  
  return 0;
}
```

実行結果
氏名 = Takao
身長 = 173
体重 = 86.2
奨学金 = 0

構造体オブジェクトの個々のメンバにアクセスに利用するのがドット演算子になります。
takao.name高尾という構造体の名前を引っ張ってくる。

takaoの配列は3つで構造体は４つメンバがあるが、エラーは起こさない。
最後の奨学金のscholsは初期化子されていないので0となる。

###ポインタとアロー演算子
```c
#include<stdio.h>
#define NAME_LEN  64

struct student{
  char  name[NAME_LEN];
  int   height;
  float weight;
  long  schols;
};

void hiroko (struct student *std){
  if (std -> height < 180)
    std -> height = 180;
  if (std -> weight < 80)
    std -> weight = 80;
}


int main(void){

  struct student sanaka = {"Sanaka", 175, 62.5, 73000};

  hiroko(&sanaka);

  printf("氏名 = %s\n",     sanaka.name);
  printf("身長 = %d\n",     sanaka.height);
  printf("体重 = %.1f\n",   sanaka.weight);
  printf("奨学金 = %ld\n",  sanaka.schols);
  
  return 0;
}
```

実行結果
氏名 = Sanaka
身長 = 180
体重 = 80.0
奨学金 = 73000
```hiroko(&sanaka);```でsanakaの値を変えてください！っとしてます。
ポインタで構造体をアクセスするときにアロー演算子を使います。
```std -> height```,```std -> weight```
元はこれ
```(＊std).height```,```(＊std).weight```
明らかにアロー演算子の方が簡潔かつ見やすいのでこれをつかっていきます。

###typedef宣言
typedef宣言をうまく利用するとstruct studentという型名を簡潔に表記できます。

```c
#include<stdio.h>
#define NAME_LEN  64

typedef struct student{
  char  name[NAME_LEN];
  int   height;
  float weight;
  long  schols;
}Student;

void hiroko (Student *std){
  if (std -> height < 180)
    std -> height = 180;
  if (std -> weight < 80)
    std -> weight = 80;
}


int main(void){

  Student sanaka = {"Sanaka", 175, 62.5, 73000};

  hiroko(&sanaka);

  printf("氏名 = %s\n",     sanaka.name);
  printf("身長 = %d\n",     sanaka.height);
  printf("体重 = %.1f\n",   sanaka.weight);
  printf("奨学金 = %ld\n",  sanaka.schols);
  
  return 0;
}
```

```c
typedef struct student{
  char  name[NAME_LEN];
  int   height;
  float weight;
  long  schols;
}Student;
```

このように構造体をStudentというtypedef名にしてあげれば
呼び出す際もStudentだけで呼び出すことができる。※変数ではない。