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

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

ただいまの
回答率

88.21%

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

受付中

回答 5

投稿

  • 評価
  • クリップ 2
  • VIEW 7,272
退会済みユーザー

退会済みユーザー

現在このような結果で表示されるプログラムの作成を試みています。
>0p↲
>p
>1d↲
>pd
>0i↲
>ipd
>2a↲
>ipad
というように、格納位置・格納データの順で入力すると、指示された格納位置にデータが入り表示されるというプログラムを作成したいです。
データサイズに合わせられるように動的メモリ(malloc)、リスト(構造体)を使用したいと考えています。
初心者のため、現在のところまったくといいほどわかりませんので、何かしらアドバイスをいただけたらと思っています。
よろしくお願いします。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    2017/01/27 12:37

    こちらの質問が他のユーザから「やってほしいことだけを記載した丸投げの質問」という指摘を受けました
    「質問を編集する」ボタンから編集を行い、調査したこと・試したことを記入していただくと、回答が得られやすくなります。

回答 5

+1

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


#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言語の作法や習慣から外れた書き方をしているかもしれません

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

#include <stdio.h>        //scanf, size_t使用するため
#include <stdlib.h>        //malloc, freeを使用するため
#include <string.h>        //memcpy, memmoveを使用するため
#include <ctype.h>         //isdigitを使用するため
#include <stdbool.h>        //bool型を使用するため


//////////////////////////////////////////////////////
//定数宣言
//////////////////////////////////////////////////////

const int DEF_BUF_SIZE = 256;    //入力データの最大サイズ
const char KEY_EXIT = 'y';        //プログラム終了キー

//////////////////////////////////////////////////////
//構造体定義
//////////////////////////////////////////////////////

//リストの構造体
typedef struct _Array
{
    char* data;    //配列
    int size;    //配列の要素数
}Array, Buffer;

//////////////////////////////////////////////////////
//プロトタイプ宣言
//////////////////////////////////////////////////////

//Array構造体初期化
void initArray(Array *array, int size);
//入力データ取得
bool inputData(Array *buf);
//入力データを分割して取得
bool devideData(Array *buf, int *index, char *data);
//配列のリサイズ
bool resizeArray(Array *array, int new_size, char init_value);
//配列の要素の出力
void showArray(Array *array);

//////////////////////////////////////////////////////
//関数
//////////////////////////////////////////////////////

int main(void)
{
    bool ret = false;    //入力データ判別に使用
    char key;            //キー取得に使用
    
    Buffer buf = {0};    //バッファ
    Array array = {0};    //出力用リスト
    
    char new_data;    //入力した文字
    int new_index;    //入力したインデックス
    
        
    //バッファ初期化
    initArray(&buf, DEF_BUF_SIZE);
    
    //終了キーが入力されるまで繰り返し
    while(key != KEY_EXIT)
    {
        while(ret == false)
        {
            //入力したデータを取得
            ret = inputData(&buf);
            if(ret == true)
            {
                //入力したデータを判別して分割
                ret = devideData(&buf, &new_index, &new_data);
            }
        }
        
        //現在の配列の要素数より大きい要素数を指定した場合、メモリサイズ変更
        if(array.size < new_index)
        { 
            //配列のリサイズ
            resizeArray(&array, new_index, ' ');
        }
        
        //データ代入
        array.data[(new_index - 1)] = new_data;
        
        //データ出力
        showArray(&array);
        
        printf("終了しますか?(y/n)\n");
        scanf("%c%*c", &key);

        //次の繰り返しで入力できるようにfalseを代入
        ret = false;
        
    }
    
    return 0;
}

//
//Array構造体内部の配列のメモリをsize数分確保
//
void initArray(Array *array, int size)
{
    array->data = (char *)malloc((sizeof(char) * size));
    array->size = size;
}

//
//入力したデータを取得
//取得できなかった場合falseを返す
//
bool inputData(Array *buf)
{
    printf("入力してください[要素番号|データ]\n");
    
    if(scanf("%s", buf->data) == EOF)
    {
        return false;
    }
    return true;
}

//
//入力したデータを数値と文字で分割して取得する
//フォーマットが違う場合、falseを返す
//
bool devideData(Array *buf, int *index, char *data)
{
    int i;
    
    for(i = 0; i < buf->size; i++)
    {
        //数字かどうか判断
        if(!isdigit(buf->data[i]))
        {
            //配列の初頭が数字ではない場合、フォーマットが違うと判断
            if(i == 0)
            {
                return false;
            }
            //数値ではない場合、文字列の位置を取得したとしてforを抜ける
            else
            {
                break;
            }
        }
    }
    
    //文字列内部の数値を取得
    *index = strtol(&buf->data[0],NULL, 10);
    
    //文字を取得
    *data = (char)buf->data[i];
    
    //インデックスの数値が0の場合はフォーマットが違うと判断
    if(index == 0)
    {
        return false;
    }
    
    return true;
}

//
//配列のリサイズ
//リサイズに失敗した場合、falseを返す
//第3引数には動的に取得したメモリの初期値に代入する文字を指定
bool resizeArray(Array *array, int new_size, char init_value)
{
    char *tmp;
    
    //メモリを確保
    tmp = (char *)malloc((sizeof(char) * new_size));
    
    //例外処理
    if(tmp == NULL)
    {
        printf("メモリの確保に失敗しました。");
        return false; 
    }
    
    //要素を初期化
    memset(&tmp[0], init_value, (sizeof(char) * new_size));
    
    //古い配列の要素を新しい配列にコピー
    memcpy(tmp, array->data, array->size);
    
    //古い配列のメモリを開放して廃棄
    free(array->data);
    
    //構造体の配列に新しい配列のポインタを代入して構造体の配列を更新
    array->data = tmp;
    
    //配列のサイズをコピー
    array->size = new_size;
    
    return true;
}


//
//配列を出力
//
void showArray(Array *array)
{
    int i;
    //配列のサイズ分文字を出力
    for(i = 0; i < array->size; i++)
    {
        printf("%c",array->data[i]);
    }
    printf("\n");
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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;
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

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

struct sl_list {
    struct sl_list* next;
    char c;
};

struct sl_list *sl_list_create();
void sl_list_destroy(struct sl_list *list);
struct sl_list *sl_list_insert(struct sl_list *list, int pos, char c);
int sl_list_fprint(FILE *stream, struct sl_list *list);

struct sl_list *sl_list_create()
{
    return NULL;
}

void sl_list_destroy(struct sl_list *list)
{
    if (list == NULL) return;
    struct sl_list *next_list = list->next;
    free(list);
    sl_list_destroy(next_list);
}

inline struct sl_list *_sl_list_insert_prev(struct sl_list *prev, int pos,
        struct sl_list *item)
{
    if (prev == NULL) {
        errno = ERANGE;
        return NULL;
    } else if (pos == 0) {
        item->next = prev->next;
        prev->next = item;
        return item;
    }
    return _sl_list_insert_prev(prev->next, --pos, item);
}

struct sl_list *sl_list_insert(struct sl_list *list, int pos, char c)
{
    struct sl_list *item = (struct sl_list *)malloc(sizeof(struct sl_list));
    if (item == NULL) return NULL;
    item->next = NULL;
    item->c = c;

    if (pos == 0) {
        item->next = list;
        list = item;
    } else {
        if (_sl_list_insert_prev(list, --pos, item) == NULL) {
            free(item);
            return NULL;
        }
    }
    return list;
}

inline int _sl_list_fprint(int n, FILE *stream, struct sl_list *list)
{
    if (list == NULL) return n;
    putc(list->c, stream);
    return _sl_list_fprint(++n, stream, list->next);
}

int sl_list_fprint(FILE *stream, struct sl_list *list)
{
    return _sl_list_fprint(0, stream, list);
}

bool input_data(FILE *stream, int *index, char *c);

int main(void)
{
    int n;
    char c;
    struct sl_list *list;
    list = sl_list_create();
    while (input_data(stdin, &n, &c)) {
        errno = 0;
        struct sl_list *next_list = sl_list_insert(list, n, c);
        if (next_list != NULL) {
            list = next_list;
            sl_list_fprint(stdout, list);
            printf("\n");
        } else {
            int errcode = errno;
            fprintf(stderr, "%s\n", strerror(errcode));
            break;
        }
    }
    sl_list_destroy(list);
    return 0;
}

bool input_data(FILE *stream, int *n, char *c)
{
    *n = 0;
    *c = '\0';
    char prev_c = '\0';
    for (;;) {
        int i = getc(stream);
        if (i == '\r') {
            i = '\n';
            int j = getc(stream);
            if (j != '\n' && j != EOF) {
                ungetc(j, stream);
            }
        }
        if (i == EOF) {
            return false;
        } else if ('0' <= i && i <= '9') {
            *n *= 10;
            *n += i - '0';
        } else if (i == '\n') {
            if (*c != '\0') {
                return true;
            } else if (prev_c != '\0') {
                *c = prev_c;
                *n /= 10;
                return true;
            } else {
                errno = EILSEQ;
                return false;
            }
        } else {
            if (*c == '\0') {
                *c = i;
            } else {
                errno = EILSEQ;
                return false;
            }
        }
        prev_c = i;
    }
}


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

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

これで表示されるはず

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る