C言語ひさしぶり過ぎる

すっかりC言語に時間を取られてしまった2月。

もう、しばらくは結構です(^^;
普段使いなら、Perlで十分過ぎますので。。。



すっかり色々忘れてて、凹んだ。

初歩的なんだけど、malloc()の使い方も完璧忘れてました。

せっかくなので備忘録として。malloc()で双方向リストを操作するサンプル。
//**********************************************************
// メモリ操作のテスト
// 両方向リストの作成
//**********************************************************

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

#define NAME_LENGTH 100

typedef struct NAMELIST {
    struct NAMELIST *back;
    int number;
    unsigned char name[NAME_LENGTH];
    struct NAMELIST *next;
}_NAMELIST;

struct NAMELIST *add(unsigned char *name, struct NAMELIST *name_last);
struct NAMELIST *getStart(struct NAMELIST *p);
struct NAMELIST *getLast(struct NAMELIST *p);
void print_list(struct NAMELIST *namelist);
void free_list(struct NAMELIST *p);

//=============================================================================
// メイン
//=============================================================================
int main(int argc, char *argv[]) {
    int i = 0;
    for(i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    unsigned char buf[NAME_LENGTH];
    struct NAMELIST *p = NULL;

    printf("input, added string.(end = EOF)\n");
    while(scanf("%s", buf) != EOF) {
        if(!strcmp(buf, "q")) {
            break;
        }
        p = add(buf, p);
    }
    print_list(p);
    free_list(p);

    return 0;
}

//=============================================================================
// データの追加
//   戻り値=struct NAMELIST *追加したデータへのポインタ
//=============================================================================
struct NAMELIST *add(unsigned char *name, struct NAMELIST *name_last) {
    struct NAMELIST *name_new;

    if((name_new = (struct NAMELIST *)malloc(sizeof(struct NAMELIST))) == NULL) {
        printf("memory allocation failed. = malloc() error\n");
        exit(EXIT_FAILURE);
    }

    // 確保したメモリに入力値を保存する
    strncpy(name_new->name, name, (size_t)NAME_LENGTH);

    // 今回のポインタのbackを前回のポインタにつなぐ
    name_new->back = name_last;

    // 今回のポインタのnextをNULLに
    name_new->next = NULL;

    // 前回のポインタのnextを今作成したメモリにつなぐ(NULLの時を除く)
    if(name_last != NULL) {
        name_last->next = name_new;
    };

    return name_new;
}


//=============================================================================
// 最初のデータへのポインタを求める
//   戻り値=struct NAMELIST *最初のデータへのポインタ
//=============================================================================
struct NAMELIST *getStart(struct NAMELIST *p) {
    struct NAME *q;
    if(p == NULL) {
        return (struct NAMELIST *)NULL;
    }
    while(p->back != NULL) {
        p = p->back;
    }
    return p;
}


//=============================================================================
// 最後のデータへのポインタを求める
//   戻り値=struct NAMELIST *最後のデータへのポインタ
//=============================================================================
struct NAMELIST *getLast(struct NAMELIST *p) {
    struct NAME *q;
    while(p->next != NULL) {
        p = p->next;
    }
    return p;
}


//=============================================================================
// データをすべて表示する
//=============================================================================
void print_list(struct NAMELIST *namelist) {
    namelist = getStart(namelist);
    while(namelist != NULL) {
        printf("%s\n", namelist->name);
        namelist = namelist->next;
    }
}


//=============================================================================
// データ用として確保したメモリをすべて解放する
//=============================================================================
void free_list(struct NAMELIST *p) {
    struct NAMELIST *q;
    while (p != NULL) {
        q = p->back;
        free(p);
        p = q;
    }
}
実行するととにかく文字列(半角99文字まで)の文字列をひたすら入力する。
データが入力される度にmalloc()でメモリを確保して、データを格納する。
qが入力されるとそれまで入力したデータを表示する・・・という単純なもの。

データの形はstruct NAMELISTで定義している。
最初はデータを示すポインタがNULLをさしている。
データを作ると、ポインタはデータの位置になる。

データの形式は
・次のデータを指し示すポインタ
・実際の格納されるデータ
・前のデータを指し示すポインタ
から成り立っていて、

新たにデータを作るとき、、、
・次のデータを指し示すポインタはNULLにする
・実際のデータを格納する
・前のデータを指し示すポインタは前の構造体を示す
というデータにする。

次のデータも前のデータも(データを見ることで)つながりが分かるので双方向リストと言うらしい。

malloc()を使う意味というか、、、メモリがある限り、動的にデータの保管場所を増やせるのがメリットになる。逆に解放忘れがあると、いわゆるメモリリークの元になるので注意が必要だが。
逆にmalloc()を使う必要が無い例としては、それほど巨大なメモリを確保する必要が無い場合、あらかじめ必要になるデータ量が決まっている場合・・・などはmalloc()を使うより、配列を使った方が安全。

とな。


完全に「復習」の備忘録。



こんな事(データの格納方法)を考えなくても、push()、pop()でリスト(スタック)操作ができたり、、hash変数が使えたり、SORT()も関数一発だったり・・・と、perlの便利さが身に染みてます。。。