[an error occurred while processing this directive] [an error occurred while processing this directive]

構造体とmalloc()

構造体の中身が勝手に壊れる! なんか知らないけどSegmentation Faultする! Windowsが「申し訳ございません」とか謝る! という方のために。

基本

変数をまとるのに、構造体が使えます。
例えば、学生の名前とIDを保持するのにstudentという構造体を作ってみます。
 struct student {
    int id;
    char *name;
 };
idとnameは構造体のメンバ(member)と呼ばれます。これはmain()の中ではなくて、#include とかの下に書きます。
使ってみましょう。
#include 
 
struct student {
  int id;
  char *name;
};

int main(void){
  struct student hoge;
  hoge.id = 0;
  hoge.name = "Kei";
  printf("%d: %s", hoge.id, hoge.name);
}
hogeという実体を作って、これに"."で区切って値を代入しています。
構造体は参照(ポインタ)を使ってアクセスすることもできます。
int main(void){
  struct student hoge, *hogep;
  hogep = &hoge; // hogepにhogeのアドレスを代入
  hogep->id = 0;
  hogep->name = "Kei";
  printf("%d: %s", hoge->id, hoge->name);
}
hogepを使ってアクセスする時は、"->"を使ってアクセスします。
hogepはポインタなので、ポインタの実体が無いとアクセスできません。 (ここではhogep = &hoge の行で参照を代入しています)

Typedefで名前を付ける

変数の型は(struct なんとか)という形になります。 でも一々structって書くのは面倒…というときのために、typedefという機能があります。 これを使うと、(なんとか)だけでアクセスできます。
struct student {
   int id;
   char *name;
};
のかわりに
typedef struct {
   int id;
   char *name;
} student, *student_t;
と書くと、
struct student hoge;
struct student *hogep;
と書く代わりに
student hoge;
student_t hogep;
とかけるようになります。

構造体を返す関数(1)

struct studentという型の変数を作って、これを返す関数を作ってみます。
struct student {
  int id;
  char *name;
};
一番基本は、こんな感じです。
struct student mk_student(int id, char *name){
  struct student s;
  s.id = id;
  s.name = name;
  return s;
}
こういう関数を作ると、別の関数(例えばmain())で次のような呼び出しが出来ます。
int main(void){
  struct student s0;
  ...
  s0 = mk_student(1, "kei");
こうすると、s0が(1, "kei")というデータが入った構造体になります。
しかし、この方法の難点は、 s0 = mk_student(1, "kei"); という一文を実行するときに処理が重いことです。このプログラムでは、mk_student()の中のsとmain()の中のs0は別のものです。なので、この文では s0.id = s.id; s0.name = s.name; という二つの処理が行われています。構造体のメンバが100個あれば、100回の代入が行われます。本来なら、関数の中で作った構造体をそのまま使いたいところです。

「間違った」構造体を返す関数(2)

そこで、次のコードを考えてみます。
struct student *mk_student(int id, char *name){
  struct student s;
  s.id = id;
  s.name = name;
  return &s;
}
&s は、student構造体のsへのポインタ(アドレス)です。これで、
int main(void){
  struct student *sp0;
  ...
  sp0 = mk_student(1, "kei");
  printf("id = %d, name =  %s\n", sp0->id, sp0->name);
と書いてはどうでしょうか?
結論を言うと、これは間違っています。実行すると成功したり失敗したりします。 mk_student()の中のstruct student sは、mk_student()という関数の中でのみ使える変数で、そこを抜けると使えなくなります。使えなくなるというのは、やがて他の変数で上書きされてしまいます。上書きされるとうまく動かないし、上書きされなければ動く、という仕組みです。

構造体を返す関数(3)

関数を出ても使えるメモリを確保するには、malloc()を使う方法があります。
  char *cp = malloc(サイズ);
で、サイズ分のメモリが返ってきます。intの長さ10の配列が欲しければ、
   int *A;
   A = (int *)malloc(sizeof(int) * 10);
と書くとOKです。
これを使うと、コピーせずに、かつ安全に「構造体を作る関数」を書けます。
struct student *mk_student(int id, char *name){
  struct student *sp;
  sp = (struct student*)malloc(sizeof(struct student));
  sp->id = id;
  sp->name = name;
  return sp;
}
こう書いた上で、
int main(void){
  struct student *sp0;
  ...
  sp0 = mk_student(1, "kei");
  printf("id = %d, name =  %s\n", sp0->id, sp0->name);
とすると、今度はいつも正しくsp0に構造体へのポインタが返ってきます。
[an error occurred while processing this directive]