[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]