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して返却するという形をとった。