Skip to content

Instantly share code, notes, and snippets.

@nukopy
Created September 22, 2025 07:28
Show Gist options
  • Select an option

  • Save nukopy/641b4a4d2bb5faec52fdb4197f1b6939 to your computer and use it in GitHub Desktop.

Select an option

Save nukopy/641b4a4d2bb5faec52fdb4197f1b6939 to your computer and use it in GitHub Desktop.
C 言語の構造体変数と構造体ポインタの動作検証
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#ifndef DEVICE_NAME_LENGTH
#define DEVICE_NAME_LENGTH 32
#endif
#ifndef DEVICE_ADDRESS_LENGTH
#define DEVICE_ADDRESS_LENGTH 16
#endif
struct my_device {
char name[DEVICE_NAME_LENGTH];
unsigned int index;
uint16_t addr[DEVICE_ADDRESS_LENGTH];
};
// -------------------------------------------------------
// utils
// -------------------------------------------------------
bool size_t_to_int(size_t s, int *out) {
// size_t (unsigned long) が int に入るかどうかチェック
// INT_MAX は当然 unsigned long に入るのでキャストして OK
if (s > (size_t)INT_MAX) return false;
// キャスト
*out = (int)s;
return true;
}
void print_device_info(const char *device_name, struct my_device *dev) {
printf("%s: name=%s, index=%d\n", device_name, dev->name, dev->index);
// get the number of items
size_t n = sizeof dev->addr / sizeof dev->addr[0]; // 要素数
// print items in array
for (size_t i = 0; i < n; i++) {
uint16_t item = dev->addr[i];
printf("dev.addr[%zu] = %d\n", i, item);
/* この丁寧なキャスト全くいらんかった。size_t のためのフォーマット文字列があった。
int i_int;
if (size_t_to_int(i, &i_int)) {
printf("dev.addr[%d] = %d", i_int, item);
} else {
printf("Cannot cast i=%zu", i);
}
*/
}
printf("\n");
}
// -------------------------------------------------------
// グローバルな構造体変数、静的な構造体変数の初期化時の挙動確認
// -------------------------------------------------------
// グローバルな構造体変数
struct my_device global_dev; // 自動的にゼロ値で初期化される
// 静的な構造体変数
static struct my_device static_dev;
// 各フィールドの初期値
// global_dev.name[0] = '\0' (空文字列)
// global_dev.index = 0
// global_dev.addr[0] = 0, addr[1] = 0, ... (全て 0)
void print_global_static_dev() {
printf("----- グローバル変数、静的変数の初期化時の挙動確認 -----\n");
// global_dev
const char *name = "global_dev";
print_device_info(name, &global_dev);
// static_dev
print_device_info("static_dev", &static_dev);
printf("\n");
}
// -------------------------------------------------------
// グローバルな構造体ポインタ変数の挙動確認
// -------------------------------------------------------
// グローバルな構造体ポインタ変数
struct my_device *global_dev_ptr;
void print_global_dev_ptr() {
printf("----- グローバルな構造体ポインタ変数の挙動確認 -----\n");
// 文字列練習
// char name[] = "global_dev_ptr"; // 配列 + 初期値(サイズは自動、"abc\0")
// char name[16] = "global_dev_ptr"; // 16バイト確保、残りは '\0' で埋まる
// char name[16] = {0}; // 全ゼロ(空文字列)
// const char *name = "global_dev_ptr"; // 書き換え不可のリテラルを指すポインタ
// CAUTION: Segmentation fault
char name[] = "global_dev_ptr"; // 配列 + 初期値(サイズは自動、"abc\0")
// print_device_info(name, global_dev_ptr);
printf("\n");
}
// -------------------------------------------------------
// ローカルな構造体ポインタ変数の挙動確認
// -------------------------------------------------------
void print_local_dev_ptr() {
printf("----- ローカルな構造体ポインタ変数の挙動確認 -----\n");
// ローカルな構造体変数: ゼロ値による初期化はなく、ガベージ値が入る
struct my_device local_dev;
print_device_info("local_dev", &local_dev);
// ローカルな構造体変数: 明示的にゼロ値で初期化(グローバルな)
struct my_device local_dev_zero = {0};
print_device_info("local_dev_zero", &local_dev_zero);
// ローカルな構造体変数: 明示的に初期値を与える
struct my_device local_dev_init = {"fibonacci", 17, {
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610,
}};
print_device_info("local_dev_init", &local_dev_init);
// ローカルな構造体変数: 明示的に部分的な初期値を与える
struct my_device local_dev_init_partial = {"fibonacci"};
print_device_info("local_dev_init_partial", &local_dev_init_partial);
// ローカルな構造体ポインタ
// グローバルな構造体ポインタと異なり、構造体インスタンス分のメモリが確保され、ポインタ自体は有効になる
// 🚨 ただし、ポインタの値、構造体インスタンスのフィールドの値には不正な値が入っているため、プログラムが壊れる可能性がある。
/*
未初期化ポインタが指す先(ランダムなアドレス):
┌─────────────────────────────────┐
│ name[32] (32バイト) │ ← 不正アクセス!文字化けの原因
├─────────────────────────────────┤
│ index (4バイト) │ ← たまたま整数値として読める
├─────────────────────────────────┤
│ addr[16] (32バイト) │ ← たまたま数値として読める
└─────────────────────────────────┘
*/
// 現に print_device_info の出力が壊れている
/*
ev.addr[15] = 0 ← 前の出力の一部
rtial: name=fibonacci ← 別の出力の一部が混在
の挙動確認 ----- ← 日本語文字列の一部
, index=1667329647 ← 数値が単独で出力
dev.addr[0] = 29194
dev.addr[1] = 26996
dev.addr[2] = 27745
dev.addr[3] = 8250
dev.addr[4] = 24942
dev.addr[5] = 25965
dev.addr[6] = 26173
dev.addr[7] = 25193
dev.addr[8] = 28271
dev.addr[9] = 25441
dev.addr[10] = 26979
dev.addr[11] = 8236
dev.addr[12] = 28265
dev.addr[13] = 25956
dev.addr[14] = 15736
dev.addr[15] = 2608
*/
/*
# 何が起こっているか
## バッファオーバーフロー
- dev->nameが指すランダムなメモリにNULL文字 (`\0`) がない
- %s は NULL 文字まで読み続ける
- 結果:メモリ境界を越えて大量の文字を読み取り
## 出力バッファの破壊
- 長すぎる文字列が printf 内部のバッファを破壊
- 後続の出力が文字化けや欠損を起こす
- 文字列の途中で切れたり、他の出力と混在
## スタック破壊の可能性
これは単なる「おかしな出力」ではなく危険なコードになっている:
- メモリ破壊 - プログラムの安定性に影響
- 予期しない動作 - 他の部分にも影響する可能性
- セキュリティリスク - バッファオーバーフローは攻撃に利用される
*/
// 構造体ポインタの NG な使い方
struct my_device *local_dev_ptr;
print_device_info("local_dev_ptr", local_dev_ptr);
// ローカルな構造体ポインタ: NULL で初期化
// CAUTION: 明示的に NULL で初期化すると Segmentation fault になる
// struct my_device *local_dev_ptr_null = NULL;
// print_device_info("local_dev_ptr_null", local_dev_ptr_null);
// ローカルな構造体ポインタ: NULL で初期化し、メモリを確保する
// Segmentation fault にならない!
struct my_device *local_dev_ptr_null_malloc = NULL;
// ヒープに確保
local_dev_ptr_null_malloc = malloc(sizeof(struct my_device));
print_device_info("local_dev_ptr_null_malloc", local_dev_ptr_null_malloc);
free(local_dev_ptr_null_malloc);
// free(local_dev_ptr_null_malloc); // 同じポインタに対して 2 回 free は実行できない。crash する。
printf("\n");
}
// -------------------------------------------------------
// entrypoint
// -------------------------------------------------------
int main() {
print_global_static_dev();
print_global_dev_ptr();
print_local_dev_ptr();
return 0;
}
@nukopy
Copy link
Author

nukopy commented Sep 22, 2025

コマンド

gcc struct_pointer.c  && ./a.out

出力

----- グローバル変数、静的変数の初期化時の挙動確認 -----
global_dev: name=, index=0
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 0
dev.addr[3] = 0
dev.addr[4] = 0
dev.addr[5] = 0
dev.addr[6] = 0
dev.addr[7] = 0
dev.addr[8] = 0
dev.addr[9] = 0
dev.addr[10] = 0
dev.addr[11] = 0
dev.addr[12] = 0
dev.addr[13] = 0
dev.addr[14] = 0
dev.addr[15] = 0

static_dev: name=, index=0
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 0
dev.addr[3] = 0
dev.addr[4] = 0
dev.addr[5] = 0
dev.addr[6] = 0
dev.addr[7] = 0
dev.addr[8] = 0
dev.addr[9] = 0
dev.addr[10] = 0
dev.addr[11] = 0
dev.addr[12] = 0
dev.addr[13] = 0
dev.addr[14] = 0
dev.addr[15] = 0


----- グローバルな構造体ポインタ変数の挙動確認 -----

----- ローカルな構造体ポインタ変数の挙動確認 -----
local_dev: name=��a���, index=336
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 61976
dev.addr[3] = 60237
dev.addr[4] = 63039
dev.addr[5] = 0
dev.addr[6] = 53920
dev.addr[7] = 54881
dev.addr[8] = 45557
dev.addr[9] = 0
dev.addr[10] = 7056
dev.addr[11] = 50546
dev.addr[12] = 65535
dev.addr[13] = 0
dev.addr[14] = 51648
dev.addr[15] = 60218

local_dev_zero: name=, index=0
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 0
dev.addr[3] = 0
dev.addr[4] = 0
dev.addr[5] = 0
dev.addr[6] = 0
dev.addr[7] = 0
dev.addr[8] = 0
dev.addr[9] = 0
dev.addr[10] = 0
dev.addr[11] = 0
dev.addr[12] = 0
dev.addr[13] = 0
dev.addr[14] = 0
dev.addr[15] = 0

local_dev_init: name=fibonacci, index=17
dev.addr[0] = 0
dev.addr[1] = 1
dev.addr[2] = 1
dev.addr[3] = 2
dev.addr[4] = 3
dev.addr[5] = 5
dev.addr[6] = 8
dev.addr[7] = 13
dev.addr[8] = 21
dev.addr[9] = 34
dev.addr[10] = 55
dev.addr[11] = 89
dev.addr[12] = 144
dev.addr[13] = 233
dev.addr[14] = 377
dev.addr[15] = 610

local_dev_init_partial: name=fibonacci, index=0
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 0
dev.addr[3] = 0
dev.addr[4] = 0
dev.addr[5] = 0
dev.addr[6] = 0
dev.addr[7] = 0
dev.addr[8] = 0
dev.addr[9] = 0
dev.addr[10] = 0
dev.addr[11] = 0
dev.addr[12] = 0
dev.addr[13] = 0
dev.addr[14] = 0
dev.addr[15] = 0

local_dev_ptr: name=
ev.addr[15] = 0
rtial: name=fibonacci, index=0
の挙動確認 -----
--
-----
, index=1667329647
dev.addr[0] = 29194
dev.addr[1] = 26996
dev.addr[2] = 27745
dev.addr[3] = 8250
dev.addr[4] = 24942
dev.addr[5] = 25965
dev.addr[6] = 26173
dev.addr[7] = 25193
dev.addr[8] = 28271
dev.addr[9] = 25441
dev.addr[10] = 26979
dev.addr[11] = 8236
dev.addr[12] = 28265
dev.addr[13] = 25956
dev.addr[14] = 15736
dev.addr[15] = 2608

local_dev_ptr_null_malloc: name=, index=0
dev.addr[0] = 0
dev.addr[1] = 0
dev.addr[2] = 0
dev.addr[3] = 0
dev.addr[4] = 0
dev.addr[5] = 0
dev.addr[6] = 0
dev.addr[7] = 0
dev.addr[8] = 0
dev.addr[9] = 0
dev.addr[10] = 0
dev.addr[11] = 0
dev.addr[12] = 0
dev.addr[13] = 0
dev.addr[14] = 0
dev.addr[15] = 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment