配列・・・メモリが連続していないとダメ。配列に要素を追加する際には配列自体の大きさを変更しないといけない。配列全体をコピー、新しいメモリを用意という作業が必要になる。
以下のような配列は未初期化の領域(arr[1]やarr[3])にアクセスしてしまい、予期しない値が表示される可能性がある。
#include <stdio.h>
int main() {
// int型の要素を持つ配列の宣言
int arr[5];
// 配列に値を代入
arr[0] = 10;
arr[2] = 30;
arr[4] = 50;
// 配列の要素を表示
printf("配列の要素:\n");
for (int i = 0; i < 5; ++i) {
printf("%d\n", arr[i]);
}
return 0;
}
struct・・・独自のデータ構造を定義できる。
.
ドット演算子・・・構造体の特定の変数の値を取得する。
*
星型演算子・・・間接参照演算子。特定のアドレスを見にいく。
->
・・・アロー演算子。.
と*
を合わせたもの。
連結リスト
ノードと呼ばれる要素が連結されたデータ構造
ノード・・・実際の値と次の要素のポインタを格納する。
連結リストを使うことで、値をメモリの異なる部分に格納することができる。値が連続している必要がない。ポインタ変数は必ず初期値を設定する、予期しないメモリアドレスを参照しないようにするため。
連結リスト(ポインタ変数)は1つのアドレス、最初のノードを指すアドレスだけを表している。
malloc・・・常にアドレスを返す。『heap』領域からメモリを取得する。mallocを使用して作成した変数は常にNULLチェックをする。free()を使いメモリの解放も行う。
#include <stdio.h>
#include <stdlib.h>
int main() {
// メモリの割り当て
int *ptr = malloc(sizeof(int));
// NULLチェック
if (ptr == NULL) {
printf("メモリの割り当てに失敗しました。\n");
return 1; // エラーコードを返して終了
}
// メモリを使用した処理。。。
free(ptr);
}
realloc・・・既存のメモリブロックのサイズを変更し、新しいサイズに合わせて再割り当てを行う。
#include <stdio.h>
#include <stdlib.h>
int main() {
// 初期のメモリ割り当て
int *ptr = (int *)malloc(3 * sizeof(int));
// NULLチェック
if (ptr == NULL) {
printf("メモリの割り当てに失敗しました。\n");
return 1;
}
// メモリの使用
// 省略
// メモリサイズを変更
ptr = (int *)realloc(ptr, 5 * sizeof(int));
// メモリの使用
// 省略
// メモリの解放
free(ptr);
return 0;
}
木構造
通常、1つのrootを持っている。そこから親、子、孫のように枝分かれしていく。各ノードが2つのポインタを持っている。2つの値が比較可能な場合小さいほうが左、大きいほうが右に振り分けられる。
ハッシュテーブル
連結リストの配列。
抽象化
キュー Queue・・・最初に入力された値が最初に取り出される値。先入先出(FIFO)。値の追加をenqueue
、値の取り出しをdequeue
と言う。
trieと呼ばれる別のデータ構造もある。配列をノードとするツリー。
スタック Stack・・・最初に入力された値が最後に取り出される値。先入先出(FILO)。値の追加をpush
、値の取り出しをpop
と言う。
抽象化にはdictionariesっていうのもあるらしい。