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

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

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

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

Q&A

5回答

8737閲覧

C言語 動的メモリ確保とリスト(構造体)を利用したプログラム

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

2クリップ

投稿2015/01/21 01:32

現在このような結果で表示されるプログラムの作成を試みています。

0p↲
p
1d↲
pd
0i↲
ipd
2a↲
ipad

というように、格納位置・格納データの順で入力すると、指示された格納位置にデータが入り表示されるというプログラムを作成したいです。
データサイズに合わせられるように動的メモリ(malloc)、リスト(構造体)を使用したいと考えています。
初心者のため、現在のところまったくといいほどわかりませんので、何かしらアドバイスをいただけたらと思っています。
よろしくお願いします。

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

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

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

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

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

guest

回答5

0

適当にパパッと実装してみました。

#include <stdio.h> // scanf, putchar, size_t のためにインクルード
#include <stdlib.h> // malloc, free のためにインクルード
#include <string.h> // strlen, strcmp のためにインクルード

#define LIST_BUF_SIZE 256 // リストのために確保するバッファサイズ
#define INPUT_BUF_SIZE 16 // 入力文字列のために確保するバッファサイズ
#define INPUT_END_WORD "exit" // メインループの終了条件となる入力

/* 構造体定義 */
// リストの要素となる構造体
typedef struct s_ListNode ListNode;
struct s_ListNode
{
char value; // 格納される値
ListNode *next; // 次の要素
ListNode *previous; // 前の要素
};
// リストにアクセするための構造体
typedef struct s_List
{
ListNode *head; // 先頭要素を示す
ListNode *tail; // 末尾要素を示す
ListNode *nextWrite; // 次に書き込むノードの位置、バッファが一杯になったら NULL が入る
size_t count; // 格納されている要素数

void *buf;

} List;
// 入力データに関する構造体
typedef struct s_InputData
{
size_t index; // 挿入位置
char value; // 挿入文字
} InputData;

/* プロトタイプ宣言、詳細は定義の所に書いてあります */
void initializeList(List *list);
ListNode *getNodeFromList(List *list);
int insertItemForList(List *list, size_t index, char value);
char getItemFromList(const List *list, size_t index);
void disposeList(List *list);
void input(char *buf, size_t bufSize);
int analyzeInput(const char *inputStr, InputData *data);
void showList(const List *list);

// リストを初期化する関数
void initializeList(List *list)
{
// 自由に使えるメモリ領域を確保
list->buf = malloc(LIST_BUF_SIZE);
// 書き込み位置を登録
list->nextWrite = (ListNode *)list->buf;
// 先頭と末尾の生成
list->head = getNodeFromList(list);
list->tail = getNodeFromList(list);
// 先頭と末尾の参照セット
list->head->next = list->tail;
list->tail->previous = list->head;

list->count = 0;

}
// リストから新しいノードを得る関数、バッファが一杯であれば NULL を返す
ListNode *getNodeFromList(List *list)
{
ListNode *ret;
ret = list->nextWrite;
// バッファが一杯の場合は次の書き込み位置が NULL ポインタとなっている約束
if(ret != NULL)
{
// 次の書き込み位置を暫定でセット
list->nextWrite += sizeof(ListNode);
// 次に書き込んだら確保したバッファから出てしまう場合
if((void *)(list->nextWrite + sizeof(ListNode)) > (void *)(list + LIST_BUF_SIZE))
{
// 一杯になった目印に NULL ポインタを次の書き込み位置にする
list->nextWrite = NULL;
}
list->count++;
}
return ret;
}

int insertItemForList(List *list, size_t index, char value)
{
ListNode *insertNode;
ListNode *targetNode;
size_t listIndex;

// 挿入位置が現在の要素数よりも大きい if(index > list->count) { return 0; } // バッファが一杯で挿入できない if((insertNode = getNodeFromList(list)) == NULL) { return 0; } insertNode->value = value; // 指定位置までリストを回す targetNode = list->head->next; for(listIndex = 0; listIndex < index; listIndex++) { targetNode = targetNode->next; } // リストの参照に挿入したい要素を割り込ませる insertNode->next = targetNode; insertNode->previous = targetNode->previous; insertNode->next->previous = insertNode; insertNode->previous->next = insertNode; return 1;

}
// リストから指定インデックスの文字を得る関数
// 要素数より大きなインデックスが指定された場合は '\0' を返す
char getItemFromList(const List *list, size_t index)
{
ListNode *currentNode;
size_t listIndex;

// 要素数よりも大きなインデックスを指定されたら '\0' を返す if(index > list->count) { return '\0'; } // 参照中のノードにリストの先頭をセット currentNode = list->head->next; // リストを指定インデックスまで順番にたどる for(listIndex = 0; listIndex < index; listIndex++) { currentNode = currentNode->next; } return currentNode->value;

}
// リストを破棄する関数
void disposeList(List *list)
{
free(list->buf);
list->nextWrite = NULL; // 破棄後に要素の確保処理が行われた場合でも失敗させる
}

// 入力を行う関数
// 入力周りは今回の論点で無い為実装は適当。使うならもっとセキュアにしてください
void input(char *buf, size_t bufSize)
{
scanf("%s", buf);
}
// 入力された文字列を解析して挿入先インデックスと挿入する文字を取得する関数
// 解析のエラーが適当なので、エラーの種類に合わせてメッセージを表示したい場合などは
// エラーコードを割り当ててください
int analyzeInput(const char *inputStr, InputData *data)
{
size_t inputIndex;
// 挿入位置の解析
inputIndex = 0;
data->index = 0;
while(inputStr[inputIndex] >= '0' && inputStr[inputIndex] <= '9')
{
// 先頭から10進数を走査しているため、1つのインデックス差で10倍
data->index *= 10;
// ascii で数字 n は 0x3n なので先頭4bitを落とす
data->index += (inputStr[inputIndex] & 0x0f);
inputIndex++;
}
// インデックス指定が無い為入力フォーマットエラー
if(inputIndex < 1)
{
return 0;
}
// 挿入文字が無い為入力フォーマットエラー
if(strlen(inputStr) < inputIndex + 1)
{
return 0;
}
// 挿入文字が1文字で無い為フォーマットエラー
if(strlen(inputStr) > inputIndex + 1)
{
return 0;
}

data->value = inputStr[inputIndex]; return 1;

}

// リストの中身を表示する関数
void showList(const List *list)
{
size_t listIndex;
char value;

listIndex = 0; while((value = getItemFromList(list, listIndex)) != '\0') { putchar(value); listIndex++; } putchar('\n');

}

int main(int argc, char **argv)
{
List list;
InputData data;
char inputBuf[INPUT_BUF_SIZE];

// リストの初期化 initializeList(&list); while(1) { input(inputBuf, INPUT_BUF_SIZE); // 入力された文字列が終了を表す文字列であれば終了 if(!strncmp(inputBuf, INPUT_END_WORD, INPUT_BUF_SIZE)) { break; } // 入力結果を解析し、正しいフォーマットであれば追加 if(analyzeInput(inputBuf, &data)) { if(insertItemForList(&list, data.index, data.value)) { showList(&list); } } } // リストの破棄 disposeList(&list); return 0;

}

 こんな感じのことがやりたいのでしょうか?
この要件であれば単方向リストで十分でしたが後から気づいたのでそのまま双方向リストで。
処理は効率が悪いものとなっていますが、リスト構造の実装について基本に忠実に書いたつもりです。
この構造が理解できたら、今度は要素の削除や高速化の方法などを考えてみてください。
ただし、"動的メモリ確保"がこれでできているとは言えません。
結局最初に確保したメモリを使い切ったら、それ以上文字を追加することができません。
ですが、初心者とおっしゃっていたため malloc でメモリを確保し、確保したメモリを
自分で自由に扱う感覚をまずつかんでほしいと思いこのような実装となりました。
動的にメモリを確保するのは、まずこれが理解できてからになると思います。
わからない箇所や、今回のソースに動的なメモリ確保を実装したいなどありましたら返信ください。

注意:C#を普段書いているのでC言語の作法や習慣から外れた書き方をしているかもしれません

投稿2015/02/01 10:51

KazuhisaIda

総合スコア11

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

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

0

片方向リストで実装してみました。

C

1#include <stdio.h> 2#include <errno.h> 3#include <stdbool.h> 4#include <string.h> 5#include <stdlib.h> 6 7struct sl_list { 8 struct sl_list* next; 9 char c; 10}; 11 12struct sl_list *sl_list_create(); 13void sl_list_destroy(struct sl_list *list); 14struct sl_list *sl_list_insert(struct sl_list *list, int pos, char c); 15int sl_list_fprint(FILE *stream, struct sl_list *list); 16 17struct sl_list *sl_list_create() 18{ 19 return NULL; 20} 21 22void sl_list_destroy(struct sl_list *list) 23{ 24 if (list == NULL) return; 25 struct sl_list *next_list = list->next; 26 free(list); 27 sl_list_destroy(next_list); 28} 29 30inline struct sl_list *_sl_list_insert_prev(struct sl_list *prev, int pos, 31 struct sl_list *item) 32{ 33 if (prev == NULL) { 34 errno = ERANGE; 35 return NULL; 36 } else if (pos == 0) { 37 item->next = prev->next; 38 prev->next = item; 39 return item; 40 } 41 return _sl_list_insert_prev(prev->next, --pos, item); 42} 43 44struct sl_list *sl_list_insert(struct sl_list *list, int pos, char c) 45{ 46 struct sl_list *item = (struct sl_list *)malloc(sizeof(struct sl_list)); 47 if (item == NULL) return NULL; 48 item->next = NULL; 49 item->c = c; 50 51 if (pos == 0) { 52 item->next = list; 53 list = item; 54 } else { 55 if (_sl_list_insert_prev(list, --pos, item) == NULL) { 56 free(item); 57 return NULL; 58 } 59 } 60 return list; 61} 62 63inline int _sl_list_fprint(int n, FILE *stream, struct sl_list *list) 64{ 65 if (list == NULL) return n; 66 putc(list->c, stream); 67 return _sl_list_fprint(++n, stream, list->next); 68} 69 70int sl_list_fprint(FILE *stream, struct sl_list *list) 71{ 72 return _sl_list_fprint(0, stream, list); 73} 74 75bool input_data(FILE *stream, int *index, char *c); 76 77int main(void) 78{ 79 int n; 80 char c; 81 struct sl_list *list; 82 list = sl_list_create(); 83 while (input_data(stdin, &n, &c)) { 84 errno = 0; 85 struct sl_list *next_list = sl_list_insert(list, n, c); 86 if (next_list != NULL) { 87 list = next_list; 88 sl_list_fprint(stdout, list); 89 printf("\n"); 90 } else { 91 int errcode = errno; 92 fprintf(stderr, "%s\n", strerror(errcode)); 93 break; 94 } 95 } 96 sl_list_destroy(list); 97 return 0; 98} 99 100bool input_data(FILE *stream, int *n, char *c) 101{ 102 *n = 0; 103 *c = '\0'; 104 char prev_c = '\0'; 105 for (;;) { 106 int i = getc(stream); 107 if (i == '\r') { 108 i = '\n'; 109 int j = getc(stream); 110 if (j != '\n' && j != EOF) { 111 ungetc(j, stream); 112 } 113 } 114 if (i == EOF) { 115 return false; 116 } else if ('0' <= i && i <= '9') { 117 *n *= 10; 118 *n += i - '0'; 119 } else if (i == '\n') { 120 if (*c != '\0') { 121 return true; 122 } else if (prev_c != '\0') { 123 *c = prev_c; 124 *n /= 10; 125 return true; 126 } else { 127 errno = EILSEQ; 128 return false; 129 } 130 } else { 131 if (*c == '\0') { 132 *c = i; 133 } else { 134 errno = EILSEQ; 135 return false; 136 } 137 } 138 prev_c = i; 139 } 140}

sl_list...ってなっているのが構造体とそれに関わる関数です。C99以上必須です。末尾再帰を使っているので、末尾再帰最適化に対応したコンパイラ+最適化オプションが必須です。メモリが許す限りINT_MAXまでは対応しています。エラー処理も入れてますけど、完全じゃ無いです(posが負の場合とか)。input_dataは適当な実装なのであまり見ないでください。

投稿2016/01/02 04:14

raccy

総合スコア21733

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

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

0

構造体を使っていないので要求を満たしていませんが、参考までに

#include <stdio.h> #include <string.h> #include <malloc.h> #define INPUT_LEN_MAX 100 static void *MemAlloc(size_t size) { void *p = malloc(size); if(!p) abort(); return p; } static char *InputLine(void) { char *line = MemAlloc(INPUT_LEN_MAX + 1); int i = 0; int c; for(; ; ) { c = getc(stdin); if(c == 0x0a) /* enter */ break; if(0x20 <= c && c <= 0x7e) /* ascii character */ { if(INPUT_LEN_MAX <= i) { printf("Error: Input line is too long!\n"); i = 0; break; } line[i] = c; i++; } } line[i] = '\0'; return line; } int main() { char *str; str = MemAlloc(1); str[0] = 0x00; for(; ; ) { char *line = InputLine(); char *p; int i; int c; p = strchr(line, 0x00); if(p < line + 2) /* strlen(line) < 2 */ { free(line); break; } c = p[-1]; p[-1] = 0x00; i = atoi(line); free(line); if(i < 0 || strlen(str) < i) { printf("Error: Index out of range!\n"); break; } p = MemAlloc(strlen(str) + 2); strncpy(p, str, i); p[i] = c; strcpy(p + i + 1, str + i); free(str); str = p; printf("%s\n", str); } free(str); return 0; }

投稿2016/01/01 21:36

IchigoTaruto

総合スコア159

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

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

0

シンプルにとりあえずそれっぽく動くのをパパッと実装してみました。
mallocを使いたいとのことなのでメモリ確保にはmallocのみを使ってます。
まぁ、ちょっとした参考までに。

lang

1#include <stdio.h> //scanf, size_t使用するため 2#include <stdlib.h> //malloc, freeを使用するため 3#include <string.h> //memcpy, memmoveを使用するため 4#include <ctype.h> //isdigitを使用するため 5#include <stdbool.h> //bool型を使用するため 6 7 8////////////////////////////////////////////////////// 9//定数宣言 10////////////////////////////////////////////////////// 11 12const int DEF_BUF_SIZE = 256; //入力データの最大サイズ 13const char KEY_EXIT = 'y'; //プログラム終了キー 14 15////////////////////////////////////////////////////// 16//構造体定義 17////////////////////////////////////////////////////// 18 19//リストの構造体 20typedef struct _Array 21{ 22 char* data; //配列 23 int size; //配列の要素数 24}Array, Buffer; 25 26////////////////////////////////////////////////////// 27//プロトタイプ宣言 28////////////////////////////////////////////////////// 29 30//Array構造体初期化 31void initArray(Array *array, int size); 32//入力データ取得 33bool inputData(Array *buf); 34//入力データを分割して取得 35bool devideData(Array *buf, int *index, char *data); 36//配列のリサイズ 37bool resizeArray(Array *array, int new_size, char init_value); 38//配列の要素の出力 39void showArray(Array *array); 40 41////////////////////////////////////////////////////// 42//関数 43////////////////////////////////////////////////////// 44 45int main(void) 46{ 47 bool ret = false; //入力データ判別に使用 48 char key; //キー取得に使用 49 50 Buffer buf = {0}; //バッファ 51 Array array = {0}; //出力用リスト 52 53 char new_data; //入力した文字 54 int new_index; //入力したインデックス 55 56 57 //バッファ初期化 58 initArray(&buf, DEF_BUF_SIZE); 59 60 //終了キーが入力されるまで繰り返し 61 while(key != KEY_EXIT) 62 { 63 while(ret == false) 64 { 65 //入力したデータを取得 66 ret = inputData(&buf); 67 if(ret == true) 68 { 69 //入力したデータを判別して分割 70 ret = devideData(&buf, &new_index, &new_data); 71 } 72 } 73 74 //現在の配列の要素数より大きい要素数を指定した場合、メモリサイズ変更 75 if(array.size < new_index) 76 { 77 //配列のリサイズ 78 resizeArray(&array, new_index, ' '); 79 } 80 81 //データ代入 82 array.data[(new_index - 1)] = new_data; 83 84 //データ出力 85 showArray(&array); 86 87 printf("終了しますか?(y/n)\n"); 88 scanf("%c%*c", &key); 89 90 //次の繰り返しで入力できるようにfalseを代入 91 ret = false; 92 93 } 94 95 return 0; 96} 97 98// 99//Array構造体内部の配列のメモリをsize数分確保 100// 101void initArray(Array *array, int size) 102{ 103 array->data = (char *)malloc((sizeof(char) * size)); 104 array->size = size; 105} 106 107// 108//入力したデータを取得 109//取得できなかった場合falseを返す 110// 111bool inputData(Array *buf) 112{ 113 printf("入力してください[要素番号|データ]\n"); 114 115 if(scanf("%s", buf->data) == EOF) 116 { 117 return false; 118 } 119 return true; 120} 121 122// 123//入力したデータを数値と文字で分割して取得する 124//フォーマットが違う場合、falseを返す 125// 126bool devideData(Array *buf, int *index, char *data) 127{ 128 int i; 129 130 for(i = 0; i < buf->size; i++) 131 { 132 //数字かどうか判断 133 if(!isdigit(buf->data[i])) 134 { 135 //配列の初頭が数字ではない場合、フォーマットが違うと判断 136 if(i == 0) 137 { 138 return false; 139 } 140 //数値ではない場合、文字列の位置を取得したとしてforを抜ける 141 else 142 { 143 break; 144 } 145 } 146 } 147 148 //文字列内部の数値を取得 149 *index = strtol(&buf->data[0],NULL, 10); 150 151 //文字を取得 152 *data = (char)buf->data[i]; 153 154 //インデックスの数値が0の場合はフォーマットが違うと判断 155 if(index == 0) 156 { 157 return false; 158 } 159 160 return true; 161} 162 163// 164//配列のリサイズ 165//リサイズに失敗した場合、falseを返す 166//第3引数には動的に取得したメモリの初期値に代入する文字を指定 167bool resizeArray(Array *array, int new_size, char init_value) 168{ 169 char *tmp; 170 171 //メモリを確保 172 tmp = (char *)malloc((sizeof(char) * new_size)); 173 174 //例外処理 175 if(tmp == NULL) 176 { 177 printf("メモリの確保に失敗しました。"); 178 return false; 179 } 180 181 //要素を初期化 182 memset(&tmp[0], init_value, (sizeof(char) * new_size)); 183 184 //古い配列の要素を新しい配列にコピー 185 memcpy(tmp, array->data, array->size); 186 187 //古い配列のメモリを開放して廃棄 188 free(array->data); 189 190 //構造体の配列に新しい配列のポインタを代入して構造体の配列を更新 191 array->data = tmp; 192 193 //配列のサイズをコピー 194 array->size = new_size; 195 196 return true; 197} 198 199 200// 201//配列を出力 202// 203void showArray(Array *array) 204{ 205 int i; 206 //配列のサイズ分文字を出力 207 for(i = 0; i < array->size; i++) 208 { 209 printf("%c",array->data[i]); 210 } 211 printf("\n"); 212} 213 214

投稿2015/02/07 12:56

MiyukiAizawa

総合スコア41

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

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

0

一番の荒技は
#include <stdio.h>
int main(void){
printf("Op\np\n1d\npd\nOiipd\n2a\nipad");
}

これで表示されるはず

まあ、ASCIIコードが関係してると思います

投稿2015/01/29 13:21

kome

総合スコア11

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問