このページをはてなブックマークに追加このページを含むはてなブックマーク このページをlivedoor クリップに追加このページを含むlivedoor クリップ

  • 追加された行はこの色です。
  • 削除された行はこの色です。
*ポインタを介した構造体の利用 [#na3041a2]

+メリット
--realloc時のコピーを最小限にする。
--構造体をソートするときにアドレスの入れ替えだけで済む。
+デメリット
--配列の各要素に対してmallocを行う必要があったり、構造体のデータが必要なくなった時点でfreeする必要があったり処理が複雑になる。


**qsortで構造体のソート [#pf5b40f0]

 標準関数で用意されているqsort関数を用いることで構造体をソートすることができる。その際構造体の比較関数を用意する必要がある。

例:

#code(c){{
#include <stdio.h>
#include <string.h>

struct person{
	char name[20];
	char address[40];
	char tel[20];
};

int main(void){
	i =;						/* for文用のカウンタ */
	n = 2;
	struct person **persons;

	/* ポインタ配列と構造体をmallocする */
	persons = malloc(sizeof(struct person*) * n);
	persons[0] = malloc(sizeof(struct person));
	strcpy(persons[0]->name, "IPUSIRON");
	strcpy(persons[0]->address, "Security Akademeia");
	strcpy(persons[0]->tel, "090-XXXX-XXXX");
	persons[1] = malloc(sizeof(struct person));
	strcpy(persons[1]->name, "John");
	strcpy(persons[1]->address, "Japan");
	strcpy(persons[1]->tel, "090-YYYY-YYYY");
	/* ソートを実行する */
	sort(persons, n, sizeof(struct person*), person_cmp);
	/* ソート結果を表示する */
	for (i = 0; i < n; i++) {
		printf("%s %s\n", persons[i]->name, persons->[i].tel);
	}
	return 0;
}

int person_cmp(const void *a, const void *b){
	struct person *pa = *(struct person**)a;
	struct person *pb = *(struct person**)b;

	/* 構造体のメンバtelでソートする */
	return strcmp(pa->tel, pb->tel);
}
}}


*構造体のテクニック [#y6618248]

-構造体タグ名を他の関数でも使いたいなら、構造体タグの定義をすべての関数より外で記述しておく。
-別の関数で初期化した構造体変数を、別の関数で間接参照したければ、ポインタを渡す。ただし、呼び出される関数側は引数に構造体の型を指定する。
--さらに、通常の変数の間接参照では「*ポインタ変数」としてアクセスしていたが、構造体の場合はアロー演算子を利用して「ポインタ変数->構造体メンバ」としてアクセスする。


**memsetを利用した構造体変数の初期化 [#he0192e4]

 構造体変数を初期化する再に、構造体の各面罵をいちいち初期化するのではなく、memsetを利用してまとめて初期化することができる。

 memset関数の第1引数はアドレス、第2引数は初期化する値、第3引数は初期化するサイズを指定する。

#code(c){{
#include <stdio.h>
#include <string.h>

struct p_person{
	char *name;
	char *address;
	char *tel;
};

int main(void){
	struct p_person person;
	memset(&person, '\0', sizeof(person));
	return 0;
}
}}

[補講]memsetを使った初期化の方法は構造体だけでなく、配列のような連続するメモリ空間ならば利用できる。 ◇

 ヌルポインタはアドレス0を指すポインタである。しかし、ヌルポインタのアドレスが内部的に0ではない処理系も存在する。

 memsetはデータを単なるバイト列として扱う。構造体や配列を初期化するためにmemsetで初期化を行う場合でも、メンバという概念は無視され、バイト単位で0に初期化していく。~
 ポインタが内部的に0でない処理系では、コンパイラがアドレスとして0の場合に限り何らかの変換を行う。しかし、memsetにはこうした変換を行うことができない。なぜならば、アドレスとしての0を代入することにならないからである。

 同様の問題は浮動小数点型でも起こる。ビットがすべて0のデータが数値として0.0でない処理系が存在するのである。

 以上より、移植を優先したいならば、memsetを使った構造体変数の初期化は止めておいた方がよい。

**strdup [#yc63d210]

-引数に与えられた文字列のコピーをmallocした領域に作成して、そのアドレスを返す関数である。
-標準ライブラリの関数で多くの処理系ではstrcpyなどと一緒に含まれているが、ANSいでは規定されていない。
-strdupをうまく利用することで、ポインタの構造体メンバに値を入れることができる。

例1:

#code(c){{
#include <stdio.h>
#include <string.h>

struct p_person{
	char *name;
	char *address;
	char *tel;
};

int main(void){
	struct p_person person;
	person.name = strdup("IPUSIRON");
	person.address = strdup("Security Akademeia");
	person.tel = strdup("090-XXXX-XXXX");
	return 0;
}
}}

例2:構造体オブジェクトへのポインタからメンバにアクセスする

#code(c){{
#include <stdio.h>
#include <string.h>

struct p_person{
	char *name;
	char *address;
	char *tel;
};

int main(void){
	struct p_person *person;
	person = malloc(sizeof(struct p_person);
	person->name = strdup("IPUSIRON");
	person->address = strdup("Security Akademeia");
	person->tel = strdup("090-XXXX-XXXX");
	return 0;
}
}}

[補講]mallocの戻り値はvoid*型なので、構造体へのポインタに代入することができる。 ◇

***strdupのコード [#u116d7d5]

 strdupのコードは次の通りである。

#code(c){{
#include <stdio.h>
#include <stdlib.h>

char *strdup(const char *src){
	char *p;
	if (src == NULL) {
		return NULL;
	}
	if ((p = malloc((strlen(src) + 1) * sizeof(char)) != NULL) {
		strcpy(p, src);
	}
	return p;
}
}}

*参考文献 [#ua941bf0]

-『C言語ポインタが理解できない理由』