合計 昨日 今日

calloc + strcpyについて

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: わん
[URL]
Date: 2017年4月13日(木) 14:27
No: 1
(OFFLINE)

 calloc + strcpyについて

以下のコードを実行したときの実行結果についてわかりません。
連続して領域を確保すると、どのように考えればよろしいでしょうか?

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(){
  char *a,*b;
  int i;
 
  a = (char*)calloc(20,sizeof(char));
  b = (char*)calloc(20,sizeof(char));
  strcpy(a,"abcdefgh ijklmn op qrstuvwxyz! abcdefg hij lmnopqrst");
 
  for(i = 0; i<20; i++)
    printf("a[%d] = %c\n",i,a[i]);
  for(i = 0; i<20; i++)
    printf("b[%d] = %c\n",i,b[i]);
  return 0;
}


実行結果

a[0] = a
a[1] = b
a[2] = c
a[3] = d
a[4] = e
a[5] = f
a[6] = g
a[7] = h
a[8] =
a[9] = i
a[10] = j
a[11] = k
a[12] = l
a[13] = m
a[14] = n
a[15] =
a[16] = o
a[17] = p
a[18] =
a[19] = q
b[0] = b
b[1] = c
b[2] = d
b[3] = e
b[4] = f
b[5] = g
b[6] =
b[7] = h
b[8] = i
b[9] = j
b[10] =
b[11] = k
b[12] = l
b[13] = m
b[14] = n
b[15] = o
b[16] = p
b[17] = q
b[18] = r
b[19] = s

Name: わん
[URL]
Date: 2017年4月13日(木) 14:29
No: 2
(OFFLINE)

 calloc + strcpyについて

言語はC++となってますが、C言語です。

Name: usao
[URL]
ハッカー(138,194 ポイント)
Date: 2017年4月13日(木) 15:54
No: 3
(OFFLINE)

 Re: calloc + strcpyについて

何が言いたいのか(何の話をしたいのか,意図)がわかりません.

Name: YuO
[URL]
ハッカー(153,404 ポイント)
Date: 2017年4月13日(木) 15:56
No: 4
(OFFLINE)

 Re: calloc + strcpyについて

わん さんが書きました:連続して領域を確保すると、どのように考えればよろしいでしょうか?

単なるバッファーオーバーランによる未定義動作の結果なので,「たまたまこうなった」としか考えようがないです。

Name: 梅衣堂ひよ
[URL]
入門者(2,544 ポイント)
Date: 2017年4月13日(木) 23:38
No: 5
(OFFLINE)

 Re: calloc + strcpyについて

わん さんが書きました:
コード[C++]: 全て選択
1
2
  a = (char*)calloc(20,sizeof(char));
  b = (char*)calloc(20,sizeof(char));

メモリがaとbで連続した領域を確保できたため、事実上のa[40]になったことが原因だと思います。
プログラム実行直後だから起きた出来事ですね。
何度もメモリの確保と解放を繰り返した影響でメモリが断片化した状態で行えば、a[20]とb[20]で別々の場所にメモリが確保されるはずのなので、再現できる可能性は限りなく低いと思いますよ。

C++、メモリ断片化しますよね?(そこが自信ない
結構説明が下手ですのでご了承ください。割と言葉が足りなかったり文字だらけで分かりにくかったりします。

Name: みけCAT
[URL]
伝説なるハッカー(677,371 ポイント)
Date: 2017年4月13日(木) 23:49
No: 6
(ONLINE)

 Re: calloc + strcpyについて

まず、C言語では確保された領域の外にアクセスすると未定義動作となり、何があってもおかしくありません。

梅衣堂ひよ さんが書きました:メモリがaとbで連続した領域を確保できたため、事実上のa[40]になったことが原因だと思います。

これは違うでしょう。
提示された実行結果を見ると、「rstuvwxyz! a」の12文字が抜けており、領域は連続していません。
これは、メモリの管理に使う情報が入るためか、32バイトのアラインメントに合わせるためであると考えられます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Name: わん
[URL]
Date: 2017年4月14日(金) 15:16
No: 7
(OFFLINE)

 Re: calloc + strcpyについて

皆様 ご回答ありがとうございます。

このような結果となってしまった理由について、お願いしたいと思います。

aとbは不連続な領域をとり、aとbの間の領域に消えた12文字が存在する。
そのような結果となってしまった理由として、12文字はメモリの管理に使う情報またはアラインメントに合わせるためという理解でよろしいでしょうか?

Name: YuO
[URL]
ハッカー(153,404 ポイント)
Date: 2017年4月14日(金) 15:34
No: 8
(OFFLINE)

 Re: calloc + strcpyについて

わん さんが書きました:このような結果となってしまった理由について、お願いしたいと思います。

たまたま。

Name: みけCAT
[URL]
伝説なるハッカー(677,371 ポイント)
Date: 2017年4月14日(金) 22:02
No: 9
(ONLINE)

 Re: calloc + strcpyについて

わん さんが書きました:どのように考えればよろしいでしょうか?

こんな未定義動作の危険なコードを書くのはやめよう、未定義動作を起こさないように気をつけよう、と考えるのがいいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Name: みえ
[URL]
初心者(8,817 ポイント)
Date: 2017年4月15日(土) 02:06
No: 10
(OFFLINE)

 Re: calloc + strcpyについて

calloc や malloc といった API の仕様は、言語である C/C++ の範疇ではなく、ライブラリ (VC ランタイムや GLIBC) に依存する部分だと思います。"malloc しくみ" などのキーワードで検索すれば、どのようにメモリ領域が割り当てられるかを説明しているページが数多くヒットするので、幾つか探してみてはいかがでしょうか。また、Youtube には伝説の malloc 動画と呼ばれる有名な動画もあります。

わん さんが書きました:このような結果となってしまった理由について、お願いしたいと思います。

aとbは不連続な領域をとり、aとbの間の領域に消えた12文字が存在する。
そのような結果となってしまった理由として、12文字はメモリの管理に使う情報またはアラインメントに合わせるためという理解でよろしいでしょうか?


わんさんの見た 12 文字 (すなわち "b - a == 0x20" ) は、梅衣堂ひよさんが仰るように、プロセス開始直後でヒープ領域ががら空きであり、a と b に隣り合った領域 (= チャンク) が順当に割り当てられた結果だと思います。

VC++ の場合は分かりませんが、GLIBC であれば calloc を呼び出すと以下のコードで実装されている __libc_calloc という内部関数が呼ばれます。

sourceware.org Git - glibc.git/blob - malloc/malloc.c
https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=488579390578e09a1a232ac5161daee086dbbd6b;hb=db0242e3023436757bbc7c488a779e6e3343db04#l496

上記ソースコード中のコメントに、各チャンクの構造が略図で示されています。

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
   malloc_chunk details:
 
(..snip..)
 
    An allocated chunk looks like this:
 
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if unallocated (P clear)  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk, in bytes                     |A|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             User data starts here...                          .
            .                                                               .
            .             (malloc_usable_size() bytes)                      .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             (size of chunk, but used for application data)    |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of next chunk, in bytes                |A|0|1|
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(..snip..)
 
*/


もし隣り合ったチャンクがともに解放されていないのであれば、みけCAT さんの仰る「管理データ」は[Size of next chunk, in bytes] で示された 8 バイトということになります。ただし、各チャンクが管理データを持っているため、管理データの長さがチャンク間の長さに寄与することはなさそうです。

そこで出てくるのが「アラインメント」の方ですが、これは単純に 64bit 境界に合わせるのではなく、GLIBC の中でアラインメント境界 (= MALLOC_ALIGNMENT) がワード サイズの 2 倍とされているようなので、64bit だと 16 バイトでしょうか。さらに、チャンクの最小サイズ (= MIN_CHUNK_SIZE = &((struct malloc_chunk*)0)->fd_nextsize == 32?) も決まっています。

20 バイトを確保すると、まず最小サイズの 32 バイトに切り上げられ、これは 16 バイト境界に合うためチャンクは 32 バイトとなります。これが 12 バイトの真相ではないでしょうか。

上記はコードをざっと眺めただけの判断なので厳密には間違っているかもしれませんが、回答になっていれば幸いです。

Name: sleep
[URL]
Date: 2017年4月15日(土) 16:02
No: 11
(OFFLINE)

 Re: calloc + strcpyについて

みえ さんが書きました:20 バイトを確保すると、まず最小サイズの 32 バイトに切り上げられ、これは 16 バイト境界に合うためチャンクは 32 バイトとなります。これが 12 バイトの真相ではないでしょうか。

上記はコードをざっと眺めただけの判断なので厳密には間違っているかもしれませんが、回答になっていれば幸いです。

間違ってませんよ。

chunk のサイズ用の領域が8byte、それを含む最小サイズが32byte
以降、1 byteでも超えると16byteずつchunkの割り当てサイズが更新されていきます。
例えば25byte割り当てれば25+8=33byteになるので32+16=48byte、41byte割り当てれば41+8=49byteになるので32+16+16=64byte、57byte割り当てれば57+8=65byteになるので32+16+16+16=80byteになっていきます。

ただし、これはslabから連続したオブジェクトの領域を獲得できた場合の限定された話です。
サイズの大きな動的割り当てを実施した場合など、動的割り当て毎に別のslabから未割り当てオブジェクトが獲得されると今回のようなケースの結果にはなりません。

わん さんが書きました:このような結果となってしまった理由について、お願いしたいと思います。

aとbは不連続な領域をとり、aとbの間の領域に消えた12文字が存在する。
そのような結果となってしまった理由として、12文字はメモリの管理に使う情報またはアラインメントに合わせるためという理解でよろしいでしょうか?

calloc、strcpyについての質問のようですが、これLinux環境のglibcに限定された環境依存の実装の話なので、calloc、strcpyというC言語のライブラリ関数の仕様ではありません。


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: なし & ゲスト[19人]