①構造体の使い方
struct p_kouzoutai { char *a; char *b; char *c; }
と
struct a_kouzoutai
{
char a;
char b;
char c[];
}
の違いは何だろうか?関数とポインタの項でやったが大きさ分のメモリを取るのが配列でアドレス分だけを取るのがポインタである。
早速見てみると、
#include<stdio.h> main(){ typedef struct p_kouzoutai { char *a; char *b; char *c; }POINT; typedef struct a_kouzoutai { char a[10]; char b[10]; char c[10]; }ARRAY; printf("p_kouzoutai=%d\n",sizeof(POINT)); printf("a_kouzoutai=%d\n",sizeof(ARRAY)); } 結果 p_kouzoutai=12 a_kouzoutai=30
見て分かるとおりポインタの方は4バイト*3で12バイト。配列は10バイト*3で30バイト。
①メンバに文字列をコピー
メンバに文字列をコピーする。なお上で作成したヘッダファイルkouzoutai.hを使う。
#include<stdio.h> #include<string.h> #include "kouzoutai.h" int main(void){ struct p_kouzoutai copy; strcpy(copy.a,"testcopy"); printf("%s\n",copy.a); } 結果 異常終了 <|| 結果うまくいかない。 今度は配列のほうでやってみる。 >|| #include<stdio.h> #include<string.h> #include "kouzoutai.h" int main(void){ ARRAY copy; strcpy(copy.a,"testcopy"); printf("%s\n",copy.a); } 結果 testcopy
ついでにせっかく宣言したtypedefの方を使った。
それはともかくうまくいった。
文字列操作の項と上で散々やったように配列は配列で取った分の領域を確保しているのに対してポインタはアドレスである。
ポインタのときはわざとtypedefの型を使わなかったのはp_kouzoutaiをローカル変数で宣言しているということを伝えたかった。ローカル変数は初期化しない限り値は不定。そのアドレスに文字列をコピーしていたので結果異常終了を起こした。
ではポインタは使えないかというともちろんそんなことはない。
この場合strdup関数を使う。(まだANSII Cでは標準関数として認められていないが一般的に使われている。)strdup関数は引数に与えられた文字列のコピーをmallocした領域に作成し、そのアドレスを返す。
#include<stdio.h> #include<string.h> #include "kouzoutai.h" int main(void){ POINT copy; copy.a=strdup("testcopy"); printf("%s\n",copy.a); } 結果 testcopy
なるたけ同じコードを流用する。
今度はうまく言った。
strdupの仕様はchar *strdup(const char *s);でmallocもししてくれているので非常に使い勝手がいい。
strdupに別のメンバ名を入れることもよく行われる
exp; copy.a=strdup(copy.b)
ついでにstrdupの中身を記述すると
#include<stdio.h> #include<stdlib.h> #include<string.h> char *strdup(const char *src) { char *p; if(src==NULL) return NULL; p=malloc(strlen(src)+1); if(p) {strcpy(p,src);} return p; }
こんな感じである。
②関数のポインタと構造体を使ったプログラム
少し難しいが非常に有用なプログラム。
#include<stdio.h> #include<string.h> int list(void) { printf("func list\n"); return 1; } int show(void) { printf("fanc show\n"); return 1; } int quit(void) { exit(0); } struct command{ char *com_str; int (*com_func)(void); }; struct command coms[]={ {"ls",list},{"dir",list}, {"cat",show},{"type",show}, {"quit",quit},{"exit",quit}, {NULL,NULL} }; int do_command(char *command) { struct command *p; for(p=coms;p->com_str!=NULL;p++){ if(strcmp(p->com_str,command)==0){ return p->com_func(); } } printf("command not found\n"); return 0; } int main() { char command[80]; char *p; while(1) { printf(">"); fgets(command,8,stdin); if((p=strrchr(command,'\n'))!=NULL) *p='\0'; do_command(command); } return 0; }
結果として>というプロンプトの後に好きな文字を打ち込み、
ls,dirならlist。catならshow・・・という感じでコマンド処理を行える。
mainのargvですんでしまうこともある。