質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

2419閲覧

自己参照構造体で、構造体の後ろに順に追加していく方法がわかりません

zigzagsv

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2021/06/10 07:22

編集2021/06/10 08:04

前提・実現したいこと

C言語で自己参照構造体を作成しています。標準入力で入力したものをリストに追加していくのですが、新しく追加したものが構造体の先頭に来てしまいます。構造体の後ろに順に追加していく方法を教えてください。

該当のソースコード

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct TelephoneBook{
char name[16];
char number[16];
struct TelephoneBook *next;
};

struct TelephoneBook *add_book(char *name, char *number,struct TelephoneBook *head);
void print_book(struct TelephoneBook *p);
void free_book(struct TelephoneBook *p);
int check_name(char *na);
int check_number(char *num);

int main(void){
struct TelephoneBook *head;
char name_number[33];
char name[16]; //入力した名前を格納する
char number[16]; //入力した電話番号を格納する
int check_na; //仕様通りに入力されてなければ1,\qが入力されていれば2が代入される
int check_nu; //仕様通りに入力されてなければ1が代入される
head = NULL;

while(1){ printf("input>"); fgets(name_number,33,stdin); sscanf(name_number,"%s %s", name, number); check_na = check_name(name); if(check_na == 2){ print_book(head); free_book(head); return EXIT_SUCCESS; } if (check_na == 1){ printf("error"); return EXIT_FAILURE; } check_nu = check_number(number); if (check_nu == 1){ printf("error"); return EXIT_FAILURE; } if(check_na == 0 && check_nu == 0){ printf("Ok\n"); } head = add_book(name,number,head); }

}

int check_name(char *na){
char name[16] = " ";
strcpy(name, na);
char *chr1 = "\q";
char *chr2 = "\Q";
if(strcmp(name,chr1) == 0 || strcmp(name,chr2) == 0){
return 2;
}
int len = strlen(name);
if(name[0] >= 'A' && name[0] <= 'Z'){

}else{ return 1; } for(int i = 1; i <= len; i++){ if(name[i] >= 'a' || name[i] <= 'z'){ }else{ return 1; } } return 0;

}

int check_number(char *num){
char number[16];
strcpy(number, num);
for(int i = 0; i <= 2; i++){
if(number[i] >= '0' && number[i] <= '9'){

}else{ return 1; } } for(int i = 4; i <= 7; i++){ if(number[i] >= '0' && number[i] <= '9'){ }else{ return 1; } } for(int i = 9; i <= 12; i++){ if(number[i] >= '0' && number[i] <= '9'){ }else{ return 1; } } if(number[3] != '-' || number[8] != '-'){ return 1; } return 0;

}

struct TelephoneBook *add_book(char *name, char *number,struct TelephoneBook *head){
struct TelephoneBook *p = head;
while ( p->next != NULL ) {
p = p->next;
}
p = (struct TelephoneBook *) malloc(sizeof(struct TelephoneBook));
strcpy(p->name, name);
strcpy(p->number, number);
p->next = head;
head = p;
return head;
}

void print_book(struct TelephoneBook *p){
while (p != NULL) {
printf("%s : %s\n", p->name, p->number);
p = p->next;
}
}

void free_book(struct TelephoneBook *p){
struct TelephoneBook p2;
while (p != NULL) { /
次ポインタがNULLまで処理 */
p2 = p->next;
free(p);
p = p2;
}
}

試したこと

p->next = head; head = p;

の前にfor文で構造体の最後を探してみようとしてみましたが上手くいきませんでした。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2021/06/10 08:21

このままではコードが見づらいので、質門を編集し、<code>ボタンで、出てくる’’’の枠の中にコードを貼り付けてください
guest

回答2

0

struct TelephoneBook *p = head; while ( p->next != NULL ) { p = p->next; }

とかやれば末尾要素(へのポインタ)がpに得られるので、
そいつの後ろに繋げばいいのでは?

投稿2021/06/10 07:31

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zigzagsv

2021/06/10 08:04

追加してみましたが上手く動作しません。以下がコード全体です。 #include <stdio.h> #include <string.h> #include <stdlib.h> struct TelephoneBook{ char name[16]; char number[16]; struct TelephoneBook *next; }; struct TelephoneBook *add_book(char *name, char *number,struct TelephoneBook *head); void print_book(struct TelephoneBook *p); void free_book(struct TelephoneBook *p); int check_name(char *na); int check_number(char *num); int main(void){ struct TelephoneBook *head; char name_number[33]; char name[16]; //入力した名前を格納する char number[16]; //入力した電話番号を格納する int check_na; //仕様通りに入力されてなければ1,\qが入力されていれば2が代入される int check_nu; //仕様通りに入力されてなければ1が代入される head = NULL; while(1){ printf("input>"); fgets(name_number,33,stdin); sscanf(name_number,"%s %s", name, number); check_na = check_name(name); if(check_na == 2){ print_book(head); free_book(head); return EXIT_SUCCESS; } if (check_na == 1){ printf("error"); return EXIT_FAILURE; } check_nu = check_number(number); if (check_nu == 1){ printf("error"); return EXIT_FAILURE; } if(check_na == 0 && check_nu == 0){ printf("Ok\n"); } head = add_book(name,number,head); } } int check_name(char *na){ char name[16] = " "; strcpy(name, na); char *chr1 = "\q"; char *chr2 = "\Q"; if(strcmp(name,chr1) == 0 || strcmp(name,chr2) == 0){ return 2; } int len = strlen(name); if(name[0] >= 'A' && name[0] <= 'Z'){ }else{ return 1; } for(int i = 1; i <= len; i++){ if(name[i] >= 'a' || name[i] <= 'z'){ }else{ return 1; } } return 0; } int check_number(char *num){ char number[16]; strcpy(number, num); for(int i = 0; i <= 2; i++){ if(number[i] >= '0' && number[i] <= '9'){ }else{ return 1; } } for(int i = 4; i <= 7; i++){ if(number[i] >= '0' && number[i] <= '9'){ }else{ return 1; } } for(int i = 9; i <= 12; i++){ if(number[i] >= '0' && number[i] <= '9'){ }else{ return 1; } } if(number[3] != '-' || number[8] != '-'){ return 1; } return 0; } struct TelephoneBook *add_book(char *name, char *number,struct TelephoneBook *head){ struct TelephoneBook *p = head; while ( p->next != NULL ) { p = p->next; } p = (struct TelephoneBook *) malloc(sizeof(struct TelephoneBook)); strcpy(p->name, name); strcpy(p->number, number); p->next = head; head = p; return head; } void print_book(struct TelephoneBook *p){ while (p != NULL) { printf("%s : %s\n", p->name, p->number); p = p->next; } } void free_book(struct TelephoneBook *p){ struct TelephoneBook *p2; while (p != NULL) { /* 次ポインタがNULLまで処理 */ p2 = p->next; free(p); p = p2; } }
episteme

2021/06/10 09:08 編集

デバッグを丸投げするおつもりか? - 空(head == NULL) のとき、正しく動作するか? - 要素がひとつ入っているとき、正しく動作するか? を調べたか?
guest

0

ベストアンサー

新しく追加したものが構造体の先頭に来てしまいます

だって、そうなるようにプログラムしてるもの。リストのつなぎがどう変化するか、図に描いてみましたか?

そもそも「構造体の先頭」という言い方はおかしい。リストの先頭とか言うんじゃないだろうか。

構造体の後ろに順に追加していく方法を教えてください

いくら払う?w

C

1struct TelephoneBook * add_book(char *name, char *number, struct TelephoneBook *head) 2{ 3 struct TelephoneBook *p = head; 4 5 while (p->next) { 6 p = p->next; 7 } 8 // ループから抜けた時点で p はリストの最後を指している 9#ifdef ORG 10 // せっかくリストの最後まで辿ったのに、その p の値を捨ててしまう。 11 // 追加する新たな構造体メモリを獲得し、メンバを初期化する 12 p = (struct TelephoneBook*) malloc(sizeof(struct TelephoneBook)); 13 strcpy(p->name, name); 14 strcpy(p->number, number); 15 16 // これまでのリストを p の後ろにつなぐ。即ち p は先頭になる 17 p->next = head; 18 head = p; 19#else 20 struct TelephoneBook *tp; // 追加する構造体を指す一時ポインタ 21 22 // 追加する構造体メモリを獲得し、メンバを初期化する 23 tp = (struct TelephoneBook*) malloc(sizeof(struct TelephoneBook)); 24 tp->next = NULL; 25 strcpy(tp->name, name); 26 strcpy(tp->number, number); 27 28 // 今作った構造体をリストの最後に追加する。めでたしめでたし 29 p->next = tp; 30#endif 31 return head; 32}

質問者のコードは head = NULL; でスタートしており、それだとORGコードが動作するが、私のコードではSegmentation Fault を起こし動作しない。そこで例えば次のようにダミーエントリを作っておく手がある。当座をしのいで動作確認はできるはず。

C

1int main(void) 2{ 3#ifdef ORG 4 struct TelephoneBook *head = NULL; 5#else 6 struct TelephoneBook dummy = { "Dummy", "000-0000-0000", NULL }; 7 struct TelephoneBook *head = &dummy; // ダミーエントリをポイントして開始 8#endif

P.S.
それにしてもヘンな判定をしてますね。リスト操作をしようという段階になって、isdigit(), isupper(), islower() といった標準ライブラリ関数を知らないの?
それに、いちいちローカルな配列にコピーしなくても、そのまま判定できますよ。
for (int i = 1; i <= len; i++) { これはバグ
for (int i = 1; i < len; i++) {  これが正しい

C

1#include <ctype.h> 2int check_name(char *name) 3{ 4 char *chr1 = "\q"; 5 char *chr2 = "\Q"; 6 7 if (strcmp(name, chr1) == 0 || strcmp(name, chr2) == 0) 8 return 2; 9 10 int len = strlen(name); 11 if (!isupper(name[0])) // 先頭は英大文字であるべし 12 return 1; 13 14 for (int i = 1; i < len; i++) { // !!条件を修正した!! 15 if (!islower(name[i])) // 残りは英小文字であるべし 16 return 1; 17 } 18 return 0; // OK 19} 20 21int check_number(char *number) 22{ 23 /* number[] format: 090-1234-5678 */ 24 for (int i = 0; i <= 2; i++) { // 0, 1, 2 が 25 if (!isdigit(number[i])) // 数字でないなら 26 return 1; 27 } 28 for (int i = 4; i <= 7; i++) { // 4, 5, 6, 7 が 29 if (!isdigit(number[i])) // 数字でないなら 30 return 1; 31 } 32 for (int i = 9; i <= 12; i++) { // 9, 10, 11, 12 が 33 if (!isdigit(number[i])) // 数字でないなら 34 return 1; 35 } 36 if (number[3] != '-' || number[8] != '-') { 37 return 1; 38 } 39 return 0; 40}

サービス。私の手元で動作確認した完全版。

C

1#include <stdio.h> 2#include <string.h> 3#include <stdlib.h> 4#include <ctype.h> 5 6typedef struct TelephoneBook { 7 char name[16]; 8 char number[16]; 9 struct TelephoneBook *next; 10} TPBook; 11 12TPBook * add_book(char *name, char *num, TPBook *head); 13void print_book(TPBook *p); 14void free_book(TPBook *p); 15int check_name(char *name); 16int check_number(char *number); 17 18int main(void) 19{ 20 TPBook *head = NULL; 21 char name_number[33]; 22 char name[16]; // 入力した名前を格納する 23 char number[16]; // 入力した電話番号を格納する 24 int check_na; // 仕様通りに入力されてなければ1,\qが入力されていれば2が代入される 25 int check_nu; // 仕様通りに入力されてなければ1が代入される 26 27 while (1) { 28 printf("input>"); 29 fgets(name_number, sizeof(name_number), stdin); 30 sscanf(name_number, "%s %s", name, number); 31 check_na = check_name(name); 32 33 if (check_na == 2) { 34 print_book(head); 35 free_book(head); 36 return EXIT_SUCCESS; 37 } 38 if (check_na == 1) { 39 printf("error"); 40 return EXIT_FAILURE; 41 } 42 check_nu = check_number(number); 43 if (check_nu == 1) { 44 printf("error"); 45 return EXIT_FAILURE; 46 } 47 if (check_na == 0 && check_nu == 0) { 48 printf("Ok\n"); 49 } 50 head = add_book(name, number, head); 51 print_book(head); // show result 52 } 53} 54 55int check_name(char *name) 56{ 57 char *chr1 = "\q"; 58 char *chr2 = "\Q"; 59 60 if (strcmp(name, chr1) == 0 || strcmp(name, chr2) == 0) 61 return 2; 62 63 int len = strlen(name); 64 if (!isupper(name[0])) // 先頭は英大文字であるべし 65 return 1; 66 67 for (int i = 1; i < len; i++) { 68 if (!islower(name[i])) // 残りは英小文字であるべし 69 return 1; 70 } 71 return 0; // ok 72} 73 74int check_number(char *number) 75{ 76 /* format: 090-1234-5678 */ 77 for (int i = 0; i <= 2; i++) { // 0, 1, 2 が 78 if (!isdigit(number[i])) // 数字でないなら 79 return 1; 80 } 81 for (int i = 4; i <= 7; i++) { // 4, 5, 6, 7 が 82 if (!isdigit(number[i])) // 数字でないなら 83 return 1; 84 } 85 for (int i = 9; i <= 12; i++) { // 9, 10, 11, 12 が 86 if (!isdigit(number[i])) // 数字でないなら 87 return 1; 88 } 89 if (number[3] != '-' || number[8] != '-') { 90 return 1; 91 } 92 return 0; // ok 93} 94 95TPBook * add_book(char *name, char *number, TPBook *head) // 修正版 96{ 97 // 追加する構造体メモリを獲得 98 TPBook * tp = malloc(sizeof(TPBook)); 99 100 // メンバを初期化する 101 tp->next = NULL; 102 strcpy(tp->name, name); 103 strcpy(tp->number, number); 104 105 if (head == NULL) // 一回目は tp 自身が先頭になる 106 return tp; 107 108 TPBook *p = head; 109 110 while (p->next) 111 p = p->next; 112 113 // この時点で p はリストの最後を指している 114 // 今作った構造体 tp をリストの最後に追加する。 115 p->next = tp; // 新しい構造体をリストの最後に 116 return head; // 二回目以降は head そのまま 117} 118 119void print_book(TPBook *p) 120{ 121 while (p) { 122 printf("%s : %s\n", p->name, p->number); 123 p = p->next; 124 } 125} 126 127void free_book(TPBook *p) 128{ 129 while (p) { /* 次ポインタがNULLまで処理 */ 130 TPBook *p2 = p->next; 131 free(p); 132 p = p2; 133 } 134}

投稿2021/06/10 09:32

編集2021/06/11 08:58
rubato6809

総合スコア1382

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubato6809

2021/06/10 12:22

ああ、なるほど。 これだと最初にadd_book()でセグフォするわけだ。
rubato6809

2021/06/10 12:40

mainIO の方も修正した。headポインタがNULLで始まると具合が悪いので。
rubato6809

2021/06/10 12:51

for 文の条件のバグもあった。
rubato6809

2021/06/11 08:59

昨日のコードは急いでアップしたもの。 add_book() 関数を改善した。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問