LoginSignup
1
0

More than 5 years have passed since last update.

C言語でコマンドラインから入力し->ヒープに保持->そこから参照->最後にfreeまでの習作。

Last updated at Posted at 2018-09-13

C言語の習作のためやってみた。

・構造体を新たに定義
・コマンドラインから任意の文字列を入力
・数字の入力を促し、プログラム内でキャスト
・任意のサイズの構造体を確保
・構造体の配列にCLIからの入力内容を保持させていく。
・再度、その構造体配列から任意のものを参照してくる。
・好みのタイミングでプログラムを終了させる。
・終了直前にプログラム内でヒープに確保されたメモリをすべて解放する。

以上の処理を含めた簡易なCLIアプリケーション。



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




char* get_string_from_cli () {
    // 戻り値用文字列
    char *input = NULL;
    // 関数内でのメモリ確保用一時文字列
    char *temp = NULL;
    char c = 0;
    int size = 1;
    int index = 0;
    // cliからの入力を取得する前に 1文字分確保< 1 * sizeof(char) バイト>
    input = (char *)malloc(size * sizeof(char));
    if (input == NULL) {
        printf("メモリ確保に失敗");
        return "";
    }
    while(c = getchar()) {
        if (c == '\n'){
            input[index] = 0;
            break;
        }
        input[index] = c;
        size++;
        index++;
        temp = (char *)realloc(input, size * sizeof(char));
        if (temp == NULL) {
            return "";
        }
        if (temp != input) {
            input = temp;
        }
    }
    return input;
}

typedef struct employee {
    char *name;
    char *address;
    int age;
    int salary;
} Employee;

int main()
{
    // 会社の人数を保持
    int number_of_empoyees = 0;
    // 非雇用者の配列を生成
    Employee* staff_list = NULL;
    // コマンドラインからの入力
    char *got_info = NULL;

    printf("会社の社員数を入力 >>>");
    got_info = get_string_from_cli();
    // コマンドラインから取得した文字列を10進基数に変換
    number_of_empoyees = strtol(got_info, NULL, 10);
    free(got_info);
    got_info = NULL;
    if (number_of_empoyees < 1)
    {
        printf("不正な人数でした");
        exit(255);
    }
    // 入力した人数分の非雇用者構造体の配列を作成
    staff_list = (Employee *)malloc(number_of_empoyees * sizeof(Employee));
    if (staff_list == NULL) {
        printf("被雇用者構造体のメモリ確保ができませんでした ");
        exit(255);
    }
    int age = 0;
    int salary = 0;
    int i;
    for (i = 0; i < number_of_empoyees; i++)
    {
        printf("%d番目のスタッフ:名前を入力してください >>>", i);
        got_info = get_string_from_cli();
        staff_list[i].name = got_info;
        printf("%d番目のスタッフ:住所を入力してください >>>", i);
        got_info = get_string_from_cli();
        staff_list[i].address = got_info;
        printf("%d番目のスタッフ:年齢を入力してください >>>", i);
        got_info = get_string_from_cli();
        age = strtol(got_info, NULL, 10);
        if (age > 17 && age < 61) {
            staff_list[i].age = age;
        } else {
            printf("採用条件を満たしておりません。");
            printf("代替値を入力します");
            staff_list[i].age = 18;
        }
        printf("%d番目のスタッフ:給与を入力してください >>>", i);
        got_info = get_string_from_cli();
        salary = strtol(got_info, NULL, 10);
        if (salary < 1000000) {
            staff_list[i].salary = salary;
        } else {
            printf("それ以上は支給できません");
            printf("代替値を入力します");
            staff_list[i].salary = 30;
        }
    }
    printf("閲覧したいスタッフの番号(%d ~ %d)を入力 >>>", 0, number_of_empoyees - 1);
    int staff_number = 0;
    while (1)
    {
        printf("スタッフの番号 >>>");
        got_info = get_string_from_cli();
        if (strcmp(got_info, "exit") == 0) {
            printf("終了します");
            // ヒープにあるメモリをすべて解放する
            free(got_info);
            got_info = NULL;
            for (i = 0; i < number_of_empoyees; i++) {
                free((staff_list + i)->name);
                free((staff_list + i)->address);
            }
            exit(255);
        }
        staff_number = strtol(got_info, NULL, 10);
        if (staff_number >= 0 && staff_number < number_of_empoyees) {
            printf("お名前:%s", staff_list[staff_number].name);
            printf("住所:%s", staff_list[staff_number].address);
            printf("年齢:%d", staff_list[staff_number].age);
            printf("給与:%d円/月", staff_list[staff_number].salary);
        } else {
            printf("存在しないスタッフの番号です");
        }
    }
}

※追記

コメントで reallocを多用するべきではないとの指摘があったので
動的確保の頻度を128byteずつ拡充するように変更

char* get_string_from_cli () {
    // 戻り値用文字列
    char *input = NULL;
    // 関数内でのメモリ確保用一時文字列
    char *temp = NULL;
    char c = 0;

    // 事前にchar型のメモリを128byte分確保しておく
    int size = S;
    input = (char *)malloc(size * sizeof(char));
    if (input == NULL) {
        printf("メモリ確保に失敗");
        return "";
    }

    int index = 0;
    while(c = getchar()) {
        // indexがメモリの範囲を超えるようであればreallco();
        if (index == size) {
            size = size + S;
            // 規定バイトである128byteずつ大きくしていく
            temp = (char *)realloc(input, size * sizeof(char));
            if (temp == NULL) {
                printf("メモリの拡充に失敗しました");
                exit(255);
            }
            if (temp != input ) {
                //ポインタ変数を入れ直す
                input = temp;
            }
        }
        // 改行文字が来たら文字入力終了
        if (c == '\n'){
            input[index] = 0;
            break;
        }
        input[index] = c;
        // 書き込み対象を1byteずつシフト
        index++;
    }
    return input;
}

また、Employeeという構造体を修正

// 雇用者構造体
typedef struct employee {
    char *name;
    char *address;
    int age;
    int salary;
} Employee;

// 雇用者オブジェクトを作成するための関数
Employee* make_employee(char* name, char* address, int age, int salary ) {
    // 関数内で Employee型オブジェクトを作成
    Employee *e = (Employee *)malloc(1 * sizeof(Employee));
    e->name = name;
    e->address = address;
    e->age = age;
    e->salary = salary;
    // Employee型のアドレスを返却
    return e;
}

// Employee型を解放する
void free_employee(Employee* e) {
    free(e->name);
    free(e->address);
    free(e);
}

以下、改修後の全コード


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

#define S 128


char* get_string_from_cli () {
    // 戻り値用文字列
    char *input = NULL;
    // 関数内でのメモリ確保用一時文字列
    char *temp = NULL;
    char c = 0;

    // 事前にchar型のメモリを128byte分確保しておく
    int size = S;
    input = (char *)malloc(size * sizeof(char));
    if (input == NULL) {
        printf("メモリ確保に失敗");
        return "";
    }

    int index = 0;
    while(c = getchar()) {
        // indexがメモリの範囲を超えるようであればreallco();
        if (index == size) {
            size = size + S;
            // 規定バイトである128byteずつ大きくしていく
            temp = (char *)realloc(input, size * sizeof(char));
            if (temp == NULL) {
                printf("メモリの拡充に失敗しました");
                exit(255);
            }
            if (temp != input ) {
                //ポインタ変数を入れ直す
                input = temp;
            }
        }
        // 改行文字が来たら文字入力終了
        if (c == '\n'){
            input[index] = 0;
            break;
        }
        input[index] = c;
        // 書き込み対象を1byteずつシフト
        index++;
    }
    return input;
}

// 雇用者構造体
typedef struct employee {
    char *name;
    char *address;
    int age;
    int salary;
} Employee;

// 雇用者オブジェクトを作成するための関数
Employee* make_employee(char* name, char* address, int age, int salary ) {
    // 関数内で Employee型オブジェクトを作成
    Employee *e = (Employee *)malloc(1 * sizeof(Employee));
    e->name = name;
    e->address = address;
    e->age = age;
    e->salary = salary;
    // Employee型のアドレスを返却
    return e;
}

// Employee型を解放する
void free_employee(Employee* e) {
    free(e->name);
    free(e->address);
    free(e);
}

int main()
{
    // 会社の人数を保持
    int number_of_empoyees = 0;
    // 非雇用者の配列を生成
    Employee **staff_list = NULL;
    // コマンドラインからの入力
    char *got_info = NULL;

    printf("会社の社員数を入力 >>>");
    got_info = get_string_from_cli();
    // コマンドラインから取得した文字列を10進基数に変換
    number_of_empoyees = strtol(got_info, NULL, 10);
    free(got_info);
    got_info = NULL;
    if (number_of_empoyees < 1)
    {
        printf("不正な人数でした");
        exit(255);
    }
    // 入力した人数分の非雇用者構造体の配列を作成
    // 入力した人数分の雇用者構造体型のポインタ配列を作成
    staff_list = (Employee **)malloc(number_of_empoyees * sizeof(Employee *));
    if (staff_list == NULL) {
        printf("被雇用者構造体のメモリ確保ができませんでした ");
        exit(255);
    }

    // コマンドラインからの入力を確保 & 社員情報の登録
    char *ensure_name;
    char *ensure_address;
    long *ensure_age = (long*)malloc(1 * sizeof(long));
    if (ensure_age == NULL) {
        printf("年齢確保用のメモリ確保に失敗しました.");
        exit(255);
    }
    long *ensure_salary = (long *)malloc(1 * sizeof(long));
    if (ensure_salary == NULL) {
        printf("年収確保用のメモリ確保に失敗しました.");
        exit(255);
    }
    int number;
    int i;
    for (i = 0; i < number_of_empoyees; i++)
    {
        printf("%d番目のスタッフ:名前を入力してください\r\n>>>", i);
        ensure_name = get_string_from_cli();
        printf("%d番目のスタッフ:住所を入力してください\r\n>>>", i);
        ensure_address = get_string_from_cli();
        printf("%d番目のスタッフ:年齢を入力してください\r\n>>>", i);
        got_info = get_string_from_cli();
        *ensure_age = strtol(got_info, NULL, 10);
        if (*ensure_age > 17 && *ensure_age < 61)
        {
        } else {
            printf("採用条件を満たしておりません。");
            printf("代替値を入力します");
            *ensure_age = 18;
        }
        printf("%d番目のスタッフ:給与を入力してください\r\n>>>", i);
        got_info = get_string_from_cli();
        *ensure_salary = strtol(got_info, NULL, 10);
        if (*ensure_salary < 1000000) {
        } else {
            printf("それ以上は支給できません");
            printf("代替値を入力します");
            *ensure_salary = 300000;
        }
        // 入力した内容をもって社員オブジェクトを作成
        staff_list[i] = make_employee(ensure_name, ensure_address, *ensure_age, *ensure_salary);
    }
    // 社員情報の登録終了 & 社員情報の参照を行う
    printf("閲覧したいスタッフの番号(%dから %dまでの間)を入力\r\n>>>", 0, number_of_empoyees - 1);
    int staff_number = 0;
    while (1)
    {
        printf("スタッフの番号 >>>");
        got_info = get_string_from_cli();
        // 入力した内容がexitであればアプリケーションの終了
        if (strcmp(got_info, "exit") == 0) {
            printf("終了します");
            // ヒープにあるメモリをすべて解放する
            free(got_info);
            got_info = NULL;
            for (i = 0; i < number_of_empoyees; i++) {
                free_employee(staff_list[i]);
            }
            exit(255);
        }
        staff_number = strtol(got_info, NULL, 10);
        if (staff_number >= 0 && staff_number < number_of_empoyees) {
            printf("お名前:%s", staff_list[staff_number]->name);
            printf("住所:%s", staff_list[staff_number]->address);
            printf("年齢:%d", staff_list[staff_number]->age);
            printf("給与:%d円/月", staff_list[staff_number]->salary);
        } else {
            printf("存在しないスタッフの番号です");
        }
    }
}

構造体を動的に作る際、関数の呼び出し側からすでにmallocで確保した変数を渡してやるのか、関数内でmallocして呼び出し元に返却するのか、どちらがグッドパターンなのかわからずに、とりあえず、関数内でmallocして返却するという形をとった。

1
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0