teratail header banner
teratail header banner
質問するログイン新規登録
C

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

Q&A

解決済

1回答

474閲覧

構造体を動的に追加確保(realloc)せずに書き込めてしまう

qaz_sd

総合スコア4

C

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

0グッド

1クリップ

投稿2025/06/16 06:30

0

1

前提

以下のような、テキストファイルを読み込むプログラムを考えます。

c

1#include <stdio.h> 2#include <stdlib.h> 3 4typedef struct {//構造体の定義 5 int num;//数字を保存 6 char c;//アルファベットを保存 7} Kouzo; 8 9void read(Kouzo *ptr);//プロトタイプ宣言 10 11int main(void){ 12 Kouzo *ptr = (Kouzo *)malloc(sizeof(Kouzo)); //要素1つ分の構造体を作成 13 read(ptr); //別関数でデータ読み込み 14 free(ptr); //メモリ解放 15} 16 17 18void read(Kouzo *ptr){ 19 FILE *file = fopen("test.txt","r"); /*ファイルを読む*/ 20 char buf[300]; 21 int loop = 0; //読み込んだ行数 22 while(fgets(buf,sizeof(buf),file)!=NULL){ //fgetsで1行読む 23 sscanf(buf,"%d,%c",&ptr[loop].num,&ptr[loop].c); //sscanfでデータを分割して読む 24 printf("%d,%c\n",ptr[loop].num,ptr[loop].c); 25 loop++; 26 ptr = realloc(ptr,loop+1); //構造体を追加で動的確保 27 } 28 fclose(file); 29}

テキストファイルの構造は[1桁の数字0~9],[アルファベット大文字A~Z]となっていますが、何行あるのかは事前に分からないものとします。

text

11,A 22,B 33,C 4...//略

謎な点

今回、test.txtは3行分あるとして、上記をプログラムを実行すると、問題なく動作します。

1,A 2,B 3,C

さて、ここでptr = realloc(ptr,loop+1);部分をコメントアウトして実行すると、これも問題なく動作します。

1,A 2,B 3,C

Kouzo *ptr = (Kouzo *)malloc(sizeof(Kouzo)); の部分では構造体1つ分の範囲(Kouzo[0])しか確保していないので、2行目以降は構造体の範囲外に書き込もうとしてエラーが発生しそうなのですが、なぜか正常に動作します。これは何故でしょうか。

自分で思いついた理由としては・・・↓
Kouzo *ptr = (Kouzo *)malloc(sizeof(Kouzo)); の部分で、実は構造体が複数要素分確保されている
・たまたまエラーが発生しない部分に書き込んでいる(メモリ破壊が発生している)
のどれかだと思うのですが・・・。

補足情報

C99,gcc

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2025/06/16 07:36

> ptr = realloc(ptr,loop+1); そもそもコレでは想定通りのサイズを確保できているとは思えないんだけど…
melian

2025/06/16 08:17 編集

gcc の場合、-fsanitize=address オプション付けてコンパイルするとメモリ確保関連の潜在的なエラーを捕捉してくれます。例えば、上記のソースコードをコンパイルして実行すると以下の様なレポートを表示します。 $ gcc -std=c99 -fsanitize=address -Wall -Wextra -g test.c -o test $ ./test ==2810103==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000038 at pc ...     : 0x502000000038 is located 6 bytes after 2-byte region ...     : SUMMARY: AddressSanitizer: heap-buffer-overflow ...
qaz_sd

2025/06/16 08:38 編集

fanaさん ありがとうございます。ご指摘の通り、 reallocの使い方も間違えてました。 reallocの第二引数の数値ぶんだけ第一引数の要素数を増やすものだと思ってました。 今回の場合だとsizeof(Kouzo)が必要で ptr = (Kouzo *)realloc(ptr,(loop + 1)*sizeof(Kouzo)); が正しい(はず)ですね。
fana

2025/06/16 08:55

> 正しい (本題とは関係ない話になるけど)「とりあえず確保サイズが足りていれば(過剰でも)OK」という話なのか,それとも「ぴったりのサイズに確保する」という話なのかで,何が「正しい」かは変わると思う. (「例えばファイルの内容がN行のとき,最終的に確保されるサイズは何行分になってるのか」みたいなのをチェックしてみてもよいかもね.N=0とかN=1とかを例にしてみるとわかりやすいかと)
jimbe

2025/06/16 09:55

”何かあったらエラーになる”という考え方自体が c の在り方とマッチしていないです。 c は究極的にはプログラマがコンピュータの全てを制御するための言語であり、一見問題がありそうなコードでもプログラマが意図してそう書いていると前提されます。 「問題があったらコンピュータが教えてくれるだろ」という前提は c では用いないほうが良いです。
guest

回答1

0

ベストアンサー

たまたまエラーが発生しない部分に書き込んでいる(メモリ破壊が発生している)

この考え方で正しいです。

確保していない場所を読み書きした場合の挙動は言語仕様的には未定義とされており、その場所がどのように使われているかはわかりません。 他の用途に使われていて後になって変な挙動が明らかになるかもしれませんし、何事もなく使えてしまうかもしれませんし、ただちにクラッシュするかもしれません。

投稿2025/06/16 07:18

SaitoAtsushi

総合スコア5740

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

qaz_sd

2025/06/16 11:31

ありがとうございます。 gdbでメモリを見てみたところ、やはり変なところに代入しておかしくなっていることが分かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問