質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.00%

  • Linux

    3201questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

  • C

    3080questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • Ubuntu

    1090questions

    Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

  • Vagrant

    1050questions

    Vagrantは、VirtualBox上の仮想マシンを コマンドラインから作成してくれるソフトウェアです。 ビルド環境など容易に構築が可能です。

  • GCC

    125questions

    GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

sizeof()のキャストについてのアンチパターン

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 232

imamoto_browser

score 1064

qiitaの記事を読んでC言語のプログラミングでのアンチパターンを学習しているのですが、

void func() {
  int loop = 0;
  struct aaa *aaa = NULL;
  aaa = (struct aaa *) malloc(sizeof(struct aaa *));
  if( NULL == aaa ) {
    return RET_NG;
  }
  // 何か処理
  free(aaa);
  return RET_OK;
}

サイトの解説によると、malloc時に指定したサイズに構造体の型ではなくポインタ型を指定すると、intサイズ分しかメモリを確保できないので
意図した動作にならないとあります。
なぜ、

aaa = malloc(sizeof(struct aaa));  // もしくはsizeof(*aaa)

とするのがベターなのでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+5

ベターとかじゃなくて単純にバグです

printf("sizeof(struct aaa*)=%d\n", sizeof(struct aaa*));
printf("sizeof(struct aaa) =%d\n", sizeof(struct aaa));

を比べてください

int分というかポインタサイズしか取得できません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/20 18:47

    わかれば単純でした。結局割り当て方、間違ってるだけなんですね。

    キャンセル

+2

ちょっと元記事にも編集リクエスト投げましたが、こっちにも。

構造体を動的に確保する場合、普通はmalloc/calloc関数を利用すると思います。

ここで仮に構造体Hogeがあったとして

typedef struct {
  int value;
} Hoge;

要素数1だけ動的確保する場合

//                   構造体の大きさ * 要素数
Hoge* hoge_p = malloc(sizeof(Hoge) * 1);

のようにします(C89/C++ではできませんが、C99以降のCではvoid*型の値は他のポインタ型へ暗黙変換されるので、mallocの戻り値をキャストする必要はありません。)

ここでこのコードは次のコードと等価です。

Hoge* hoge_p = malloc(sizeof(Hoge));

でここまで書くと、「ふっふ~ん、わかった、mallocに渡すsizeofにはポインタじゃない型を書けばいいんでしょ?」という誤解をするかもしれません。そこで次のコードを見てください。

#include <stdio.h>
#include <stdlib.h>
typedef struct {
  int value;
} Hoge;

int main(void)
{
    const size_t col_size = 4;
    const size_t row_size = 3;
    Hoge** hoge_determinant = malloc(sizeof(Hoge*) * row_size);

    // init
    Hoge* tmp = calloc(sizeof(Hoge), col_size * row_size);
    for(size_t i = 0; i < row_size; ++i) hoge_determinant[i] = &tmp[i * row_size];

    //print
    for(size_t i = 0; i < row_size; ++i){
        putchar('|');
        for(size_t j = 0; j < col_size; ++j){
            if(j) putchar(' ');
            printf("%d", hoge_determinant[i][j].value);
        }
        puts("|");
    }

    return 0;
}

https://wandbox.org/permlink/esJmmwvPmD99qpbF

malloc(sizeof(Hoge*) * row_size)とあります。あれ、ちがうの?

ここでsizeof(Hoge*)としたのは、要素数row_sizeだけHoge*型の動的確保するためです。

すなわち、渡すべきは要素型の型の大きさです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

まず、ポインタのサイズと構造体のサイズが違うことは分かりますか?
構造体とポインタの名前が同じというのも誤解を招きますね。struct AAA *aaa= NULL;の方がまだ分かりやすいかな?
で、たとえば構図体のサイズが50バイトあった場合に、そのポインタは4バイト(32ビット環境の場合)になります。ようは、sizeof(AAA)とsizeof(aaa)は、各々50と4になる訳です。なので、4バイトの領域をはみ出して何か操作を行えば、確保した領域外にアクセスすることになります。場合によっては何も起こらないかもしれませんが、通常はメモリを破壊することになりますd^^・・・したがって、動いたり動かなかったり・・・厄介なバグになります。

#記事の記述にも問題があります。”ポインタ型を指定した場合、intサイズ分しかメモリを確保できません”はポインタとintが同じ環境でしか意味を持ちません。正しくは、”ポインタのサイズ分”と言うべきだと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/20 16:46

    これはひどい、元記事に編集リクエスト投げてきました

    キャンセル

  • 2018/01/20 17:18

    初心者+向けみたいなページなので、出来れば初心者にも理解しやすい形で提供してもらいたいです。

    キャンセル

  • 2018/01/20 17:24

    >cateyaさん
    回答ありがとうございます。構造体とポインタの名前は分けるべきというわけですね。
    あとおっしゃることを言い換えると、mallocで構造体のメモリサイズを確保する際は、ポインタ型にキャストすべきではないということですね。
    動いたり動かなかったりする理由は、50バイトで割り当てたつもりで他のリソースからfunc()にアクセスするコードを書いたことが原因でしょうか。

    キャンセル

  • 2018/01/20 18:10

    >あとおっしゃることを言い換えると、mallocで構造体のメモリサイズを確保する際は、ポインタ型にキャストすべきではないということですね。

    そんな話はしていないと思う

    キャンセル

  • 2018/01/20 18:22 編集

    >構造体とポインタの名前は分けるべき
    は、可読性の問題なのでそうしなくてはいけないわけではありません。
    >mallocで構造体のメモリサイズを確保する際は、ポインタ型にキャストすべきではない
    違います! malloc()が返すのはvoid*ですから構造体へのポインタへのキャストは必要です(aaa = (struct aaa*)malloc(sizeof(struct aaa)); )。が、確保するメモリは構造体のサイズsizeof(struct aaa)です。
    >動いたり動かなかったりする理由
    malloc()が確保するのは一定量(4KBとか環境次第。ここから先はユーザか確保したサイズと言う意味ではなく実際にコンパイラがOSから取ってきて、割り当てた領域サイズの事です)です。それをmalloc()が呼ばれる度に切り崩して使います。(足りなくなれば、また確保する)なので、動く場合は確保した領域内にアクセスした場合で、動かないのは確保した領域をはみ出した場合です。こればかりはコンパイラの作りとOSで確保できるメモリサイズによります。

    キャンセル

  • 2018/01/20 18:46

    読み違えてました。malloc()の返り値はvoid*なので構造体へのポインタへのキャストが必要で、aaa = (struct aaa *) malloc(sizeof(struct aaa *));だとsizeof()の引数が構造体へのポインタのサイズで場合によってははみ出してしまうよってことでまずいってことですね。

    キャンセル

  • 2018/01/20 19:44

    yumetodoさんの回答にもありますが、ポインタ領域を確保してはダメと言うわけではありません。要は、何の領域を確保するのか?(構造体なのか構造体へのポインタなのか)・・・と言うことです。あと、C99以降は(struct aaa *)のポインタのキャストは不要ですね。(C++の影響でしょうか・・・反省)

    キャンセル

  • 2018/01/20 21:16

    >C++の影響でしょうか

    むしろC++はその暗黙変換がないのでCより厳格かと

    キャンセル

+1

単純に「プログラムは意図した通りには動かない。書いた通りに動く」という格言通りなだけですね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 91.00%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Linux

    3201questions

    Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

  • C

    3080questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • Ubuntu

    1090questions

    Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

  • Vagrant

    1050questions

    Vagrantは、VirtualBox上の仮想マシンを コマンドラインから作成してくれるソフトウェアです。 ビルド環境など容易に構築が可能です。

  • GCC

    125questions

    GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。