https://youtu.be/D1j6clkVCeU

\textcolor{pink}{四国めたん: }教師役ですわ

\textcolor{lime}{ずんだもん: }生徒役なのだ
\footnotesize \textcolor{pink}{四国めたん:} こんにちは。四国めたんです
\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は 配列リスト の続きについてお話ししますわ
\footnotesize \textcolor{lime}{ずんだもん:} 前回は 配列 へのデータの挿入や削除についての話しだったのだ
\footnotesize \textcolor{pink}{四国めたん:} 今回は、 配列 のサイズ変更についてお話ししますわ
\footnotesize \textcolor{lime}{ずんだもん:} 配列 のサイズ変更?
\footnotesize \textcolor{pink}{四国めたん:} はい、 配列 にデータを追加する際、 配列 がいっぱいで追加できない場合の対処ですわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
配列のサイズ変更
\footnotesize \textcolor{pink}{四国めたん:} 実は、 配列 のサイズを後から増やすことができればいいのですが、 配列 にはそのような機能は提供されていませんわ
\footnotesize \textcolor{lime}{ずんだもん:} ざんねんなのだ
\footnotesize \textcolor{pink}{四国めたん:} ですので、 配列 を宣言、初期化している場合は、 配列 のサイズを変更するのは難しいと思いますわ
\footnotesize \textcolor{lime}{ずんだもん:} 困ったのだ
\footnotesize \textcolor{pink}{四国めたん:} ただ、 配列 の代わりにmalloc
などを使ってメモリ領域を取得していれば、サイズを増やすことができますわ
\footnotesize \textcolor{lime}{ずんだもん:} あぁ、たしかmalloc
で取得したメモリ領域は 配列 のように使えたのだ
- サイズの大きなメモリ領域を新たに
malloc
などで取得
- 元のメモリ領域から新たなメモリ領域に、
memcpy_s
などを使ってデータをコピー
- 元のメモリ領域を破棄
- ポインタを新たなメモリ領域に入れ替え

\footnotesize \textcolor{pink}{四国めたん:} まずはサイズの大きなメモリ領域、例えば2倍のサイズのメモリ領域をmalloc
で取得しますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ
\footnotesize \textcolor{pink}{四国めたん:} 次に元のメモリ領域から新しいメモリ領域に`memcpy_s'などで全てのデータをコピーしますわ
\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ
\footnotesize \textcolor{pink}{四国めたん:} そして元のメモリ領域を破棄すると同時に、ポインタが新しいメモリ領域を指すようにしますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
\footnotesize \textcolor{lime}{ずんだもん:} ところでmalloc
の代わりにrealloc
を使ってはいけないのか?
\footnotesize \textcolor{pink}{四国めたん:} realloc
を使ってもOKですし、そのほうが簡単ですわね
\footnotesize \textcolor{pink}{四国めたん:} まぁ、今回はmalloc
を使ってみますわ
\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ
\footnotesize \textcolor{pink}{四国めたん:} とりあえず、サイズが5の配列に5人分の学生のデータを初期値として割り当てた後、データを追加してみましょう
\footnotesize \textcolor{lime}{ずんだもん:} おねがいするのだ
\footnotesize \textcolor{pink}{四国めたん:} ついでにデータの挿入や削除などをおこなう関数等も実装しておきましょう
students.h
#pragma once
#ifndef _INC_STUDENTS
#define _INC_STUDENTS
#include <stdio.h>
#define NAME_SIZE (20)
#define FIRST_LIST_SIZE (5)
typedef struct {
unsigned int id;
char name[NAME_SIZE];
} student;
typedef struct {
student* pstudents;
size_t num;
size_t size;
} student_list;
int init_student_list(student_list* plist);
void final_student_list(student_list* plist);
int insert(student_list* plist, size_t idx, unsigned int id, char name[]);
int delete(student_list* plist, size_t idx);
int push(student_list* plist, unsigned int id, char name[]);
int pop(student_list* plist);
#endif // _INC_STUDENTS
students.c
#include "students.h"
#include <stdlib.h>
#include <string.h>
#include <memory.h>
int init_student_list(student_list* plist) {
plist->pstudents = (student*)malloc(sizeof(student) * FIRST_LIST_SIZE);
plist->num = 0;
plist->size = FIRST_LIST_SIZE;
return (plist->pstudents == NULL) ? -1 : 0;
}
void final_student_list(student_list* plist) {
free(plist->pstudents);
plist->pstudents = NULL;
return;
}
int resize(student_list* plist) {
size_t old_size = sizeof(student) * plist->size;
size_t new_size = old_size * 2;
student* pnew_list = (student*)malloc(new_size);
if (pnew_list != NULL) {
memset(pnew_list, 0, new_size);
memcpy_s(pnew_list, new_size, plist->pstudents, old_size);
free(plist->pstudents);
plist->pstudents = pnew_list;
plist->size = plist->size * 2;
}
return (pnew_list == NULL) ? -1 : 0;
}
int insert(student_list* plist, size_t idx, unsigned int id, char name[]) {
int rc = 0;
if (plist->num >= plist->size) {
rc = resize(plist);
}
if (rc >= 0) {
idx = (idx > plist->num) ? plist->num : idx;
size_t dst_size = sizeof(student) * (plist->size - idx - 1);
size_t src_size = sizeof(student) * (plist->num - idx);
memmove_s((plist->pstudents + idx + 1), dst_size, (plist->pstudents + idx),
src_size);
(plist->pstudents + idx)->id = id;
strcpy_s((plist->pstudents + idx)->name, NAME_SIZE, name);
plist->num++;
}
return rc;
}
int delete(student_list* plist, size_t idx) {
int rc = -1;
if (idx < plist->num) {
size_t dst_size = sizeof(student) * (plist->size - idx);
size_t src_size = sizeof(student) * (plist->num - idx - 1);
memmove_s((plist->pstudents + idx), dst_size, (plist->pstudents + idx + 1),
src_size);
(plist->pstudents + plist->num - 1)->id = 0;
(plist->pstudents + plist->num - 1)->name[0] = 0;
plist->num--;
rc = 0;
}
return rc;
}
int push(student_list* plist, unsigned int id, char name[]) {
int rc = insert(plist, plist->num, id, name);
return rc;
}
int pop(student_list* plist) {
int rc = delete (plist, plist->num - 1);
return rc;
}
array_list.c
#include <stdio.h>
#include "students.h"
int main(int argc, char* argv[]) {
student_list list;
int rc = init_student_list(&list);
if (rc == 0) {
push(&list, 1, "佐藤紬");
push(&list, 2, "鈴木陽翔");
push(&list, 3, "高橋翠");
push(&list, 4, "田中朝陽");
push(&list, 5, "伊藤凛");
insert(&list, 2, 6, "渡辺暖");
}
for (size_t i = 0; i < list.num; i++) {
printf("学生番号: %d, 名前: %s\n", (list.pstudents + i)->id,
(list.pstudents + i)->name);
}
final_student_list(&list);
return 0;
}
\footnotesize \textcolor{pink}{四国めたん:} 今回は、配列リストや配列のサイズ、データの数を構造体student_list
にまとめていますわ
\footnotesize \textcolor{lime}{ずんだもん:} ふむ
\footnotesize \textcolor{pink}{四国めたん:} そして、配列リストの操作にかかわる関数と合わせて、"students.h"ヘッダーファイルに宣言していますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
\footnotesize \textcolor{pink}{四国めたん:} なお、各関数は"students.c"にまとめて定義していますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ
初期化関数
\footnotesize \textcolor{pink}{四国めたん:} まず、初期化の関数として、init_student_list
を定義しますわ
\footnotesize \textcolor{lime}{ずんだもん:} 初期化関数はどのようなことをおこなうのだ?
\footnotesize \textcolor{pink}{四国めたん:} この関数で配列リスト"pstudents"にstudent
構造体の配列を割り当てていますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ、初期のサイズを"FIRST_LIST_SIZE"として、malloc
関数を用いて配列のメモリ領域を作成しているのだ
\footnotesize \textcolor{pink}{四国めたん:} その他にデータのサイズ"num"を0として初期化していますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
終了関数
\footnotesize \textcolor{pink}{四国めたん:} 次に、終了処理の関数としてfinal_student_list
を定義していますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ
\footnotesize \textcolor{pink}{四国めたん:} この関数で、配列リスト"pstudents"に割り当てられているメモリ領域をfree
関数で破棄していますわ
\footnotesize \textcolor{lime}{ずんだもん:} 割り当てたメモリ領域を解放しないと大変なのだ
データ追加関数
\footnotesize \textcolor{pink}{四国めたん:} 次に、データを追加する関数insert
を定義していますわ
\footnotesize \textcolor{lime}{ずんだもん:} 基本は前回のサンプルプログラムのinsert
と概ね同じなのだ
\footnotesize \textcolor{pink}{四国めたん:} 追加でデータの数が配列のサイズと同じ場合にはresize
関数で配列のサイズを変更していますわ
\footnotesize \textcolor{lime}{ずんだもん:} このresize
関数が今回のキーなのだ
\footnotesize \textcolor{pink}{四国めたん:} なおresize
関数はinsert
関数からしか呼ばれないため"students.h"ファイルでの宣言はおこなっていませんわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} resize
関数では、現在の配列のサイズの2倍のメモリ領域をmalloc
関数で確保していますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ
\footnotesize \textcolor{pink}{四国めたん:} そして、新たなメモリ領域に元の配列のデータをmemcpy_s
でコピーしますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
\footnotesize \textcolor{pink}{四国めたん:} 最後に、元の配列のメモリ領域をfree
関数で破棄し、"pstudents"に新たなメモリ領域を割り当てますわ
\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ
\footnotesize \textcolor{pink}{四国めたん:} ちなみに、新たな配列のサイズは、元の配列のサイズの2倍にするのではなく、固定のサイズを増やすのでもOKですわ
\footnotesize \textcolor{lime}{ずんだもん:} このようにすれば、メモリの許す限り、いくらでもデータを追加することができるのだ
\footnotesize \textcolor{pink}{四国めたん:} ただデータをコピーする時間は必要ですわね
データ削除関数
\footnotesize \textcolor{pink}{四国めたん:} 次に、データを削除する関数delete
を定義していますわ
\footnotesize \textcolor{lime}{ずんだもん:} 基本的には、前回のサンプルプログラムのdelete
と同じなのだ
データの最後への追加 / 最後からの削除関数
\footnotesize \textcolor{pink}{四国めたん:} 最後は、データを配列の最後に追加する関数push
と、配列の最後から削除する関数pop
の定義ですわ
\footnotesize \textcolor{lime}{ずんだもん:} 別にinsert
関数やdelete
関数でも同じなのではないのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、いずれもインデックスを配列の最後尾を指定してinsert
関数やdelete
関数を呼び出しているので、同じと云えば同じですわね
\footnotesize \textcolor{lime}{ずんだもん:} なぜ関数を追加しているのだ?
\footnotesize \textcolor{pink}{四国めたん:} push
関数やpop
関数は意外と使うことが多いので、今回は特別に追加していますわ
\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ
メイン関数
\footnotesize \textcolor{pink}{四国めたん:} メイン関数では、まずstudent_list
構造体を宣言したあとに初期化関数init_student_list
を呼び出していますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ
\footnotesize \textcolor{pink}{四国めたん:} 初期化が成功すれば、push
関数で5人分のデータを追加していますわ
\footnotesize \textcolor{lime}{ずんだもん:} 初期化では5人分のメモリ領域を確保しているだけなので、これでデータは一杯なのだ
\footnotesize \textcolor{pink}{四国めたん:} はい、次に6人目のデータをインデックス2の部分に挿入していますわ
\footnotesize \textcolor{lime}{ずんだもん:} うむ、これでメモリ領域の拡張が必要になるのだ
\footnotesize \textcolor{pink}{四国めたん:} そして全てのデータを出力していますわ
\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ
\footnotesize \textcolor{pink}{四国めたん:} 最後にfinal_student_list
で終了処理をおこなっていますわね
\footnotesize \textcolor{lime}{ずんだもん:} 終了処理は必須なのだ
\footnotesize \textcolor{pink}{四国めたん:} それでは実行してみましょう

\footnotesize \textcolor{lime}{ずんだもん:} しっかりと挿入したデータも表示されているのだ
まとめ
\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした
\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ
\footnotesize \textcolor{pink}{四国めたん:} 以上で 配列リスト を終了しますわ
\footnotesize \textcolor{pink}{四国めたん:} 配列によるリストの操作は、意外と使う場面があったりしますわ
\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?
\footnotesize \textcolor{pink}{四国めたん:} はい、インデックスの指定の間違いによるバグなども多いので、結構、慣れが必要ですわ
\footnotesize \textcolor{lime}{ずんだもん:} しっかりと使いこなすのだ
Discussion