LoginSignup
2
1

構造体のサイズを動的に変更したい

Last updated at Posted at 2023-12-28

やりたいこと

構造体に長さが不定の文字列を入れたい

案その1

構造体の中にポインタを入れておいて文字列の先頭ポインタを格納する。

メリット

  • 構造体の配列を作ることができる(管理しやすい)
  • 複数の可変長配列を管理できる

デメリット

  • 場合によっては構造体の本体をmalloc()、文字列のメモリをmalloc()と二度手間が発生する
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct {
    char* p_next;
    //ほかにもなんかいろいろデータが入ってる
    char* str;
}mozi;


int main(void) {
    //構造体を宣言
    mozi mozi1;
    char str[] = "文字列";//格納したい文字列(固定長じゃねえかってツッコミは無しで)

        //可変長文字列の長さ
        int str_size = strlen(str) + 1;

    //文字列のメモリを確保
    mozi1.str = (char*)malloc(str_size);
    if (!mozi1.str) {
        exit(1);
    }

    strcpy(mozi1.str, str);

    printf("%s\n", mozi.str);

    free(mozi1.str);
}

案その2(非推奨)

構造体のサイズを直接変える。

仕組み

構造体の一番後ろの要素に配列を入れることを考える。
構造体で宣言した配列の長さ以上の配列をそのまま入れると当然メモリ破壊を引きおこす。

image.png

じゃあ予め溢れる前提でメモリを確保すれば…?

image.png

問題ありませんね。
可変長にしたい要素をいちばん後ろに置くのがミソ。
構造体の要素は宣言したときの順にメモリに配置されるので、いちばん後ろの要素を可変長にしておけば構造体の他の要素を上書きする心配もありません。

メリット

  • 場合によってはmalloc()を一回で済ませることができる

デメリット

  • 可変長にできるのは1要素のみ
  • 変なバグを作りこみそう(主観)
  • 言語仕様的にはよろしくないっぽい
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct {
    char* p_next;
    //ほかにもなんかいろいろデータが入ってる
    char str;
}mozi;


int main(void) {

    char str[] = "文字列";//格納したい文字列(固定長じゃねえかってツッコミは無しで)

    mozi * p_mozi = malloc(sizeof(mozi) + strlen(str));//構造体の分と、文字列が構造体から溢れる分を確保

    if (!p_mozi) {
        exit(1);
    }

    strcpy(&p_mozi->str, str);

    printf("%s\n", &p_mozi->str);

    free(p_mozi);
    
}

案その3

コメントで教えてもらいました。
フレキシブル配列メンバという機能がありまして…

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

typedef struct {
    size_t p_num;
    char str[];
    //ほかにもなんかいろいろデータが入ってる
}mozi;

const int str_num = 2;//文字列の数

int main(void) {

    char str1[] = "文字列";//格納したい文字列(固定長じゃねえかってツッコミは無しで)
    char str2[] = "もじれつ";

    mozi* mozi1;

    //ポインタの数を入れるsize_t,文字列の位置を示すchar*,文字列本体
    mozi1 = (mozi*)calloc(1,sizeof(size_t) + (sizeof(char*) * str_num) + strlen(str1) + 1);
    
    if (!mozi1) {
        exit(1);
    }

    strcpy(mozi1->str, str1);
    printf("mozi1:%s\n", mozi1->str);

    free(mozi1);
}

メリット

  • malloc()を一回で済ませることができる

デメリット

  • 初見の人が混乱しそう

まとめ

デバッグの手間などを考えると素直に2回mallocかフレキシブル配列メンバでいいと思います。

2
1
5

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
2
1