今回はメモリーを動的に確保する関数malloc()とその仲間calloc(), realloc(), free()を使って、基本的な挙動の一部をチェックしてみました。
今回つかう関数
malloc() マロック
memory allocation=メモリーを割り当てる
calloc() カロック
contiguous allocation(コンティギュアス・アロケーション)
=(ブロックが)隣り合っているメモリー割り当て。
マロックが一つのカタマリをでん!と用意するのに対し、
カロックはメモリーのカタマリを複数用意するイメージ。
realloc() リアロック
re-allocation=一度マロックで確保したメモリ領域の大きさを確保し直して変更できる。
free() フリー
開放。allocation (割り当て) で確保したメモリ領域のデータを開放して空にする。
各関数の詳しい説明はマニュアル=manual(ターミナル上で"man malloc"などと入力)、ウェブ上の様々な記事や書籍を参照ください。
malloc()とfree()で得られるアドレスとサイズ
1. ファイル作成 (ここをクリックすると詳細表示)
% touch alloc_ptr.c
2. コーディング
#include <stdio.h>//printf()
#include <stdlib.h>//malloc(), free(), calloc(), realloc()
int main(void)
{
void *ptr;
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
free(ptr);
printf(" free() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
return (0);
}
printf()でみたいデータ
size: %zd == sizeof(ptr) // ポインタのサイズ
adrs: %p == ptr // メモリ領域におけるポインタのアドレス
%s == (char *)ptr // ポインタの中身
3. コンパイル&プログラム実行 (ここをクリックすると詳細表示)
% gcc -Wall -Wextra -Werror alloc_ptr.c -o allocptr ; time ./allocptr
4. 実行結果例
malloc() size: 8 adrs: 0x7fd185705b10
free() size: 8 adrs: 0x7fd185705b10
./allocptr 0.00s user 0.00s system 0% cpu 3.031 total
sizeはマロックで実際に確保したメモリのバイト数ではないことが示されてます。
adrs(アドレス)は16進数で表現される。
*ポインタアドレス(0x...)とtimeで出力される実行時間の値はコンパイルする毎に異なる
ポインター内に文字列を格納
次はstrcpy()を使って、配列内に文字列をコピーしてみます
#include <stdio.h>//printf()
#include <stdlib.h>//malloc(), free(), calloc(), realloc()
#include <string.h>//strcpy()
int main(void)
{
void *ptr;
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
strcpy(ptr, "Hello, World");
printf(" strcpy() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
free(ptr);
printf(" free() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
return (0);
}
実行結果例
malloc() size: 8 adrs: 0x7fe72af05b10
strcpy() size: 8 adrs: 0x7fe72af05b10 Hello, World
free() size: 8 adrs: 0x7fe72af05b10
./allocptr 0.00s user 0.00s system 0% cpu 0.164 total
calloc(), realloc()と併用
今度はmalloc()だけでなく、calloc(), realloc()も使ってみます。
#include <stdio.h>//printf()
#include <stdlib.h>//malloc(), free(), calloc(), realloc()
int main(void)
{
void *ptr;
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
free(ptr);
printf(" free() size: %zd adrs: %p\n", sizeof(ptr), ptr);
ptr = calloc(3, 5);
printf(" calloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
free(ptr);
printf(" free() size: %zd adrs: %p\n", sizeof(ptr), ptr);
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
ptr = realloc(ptr, 20);
printf("realloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
free(ptr);
printf(" free() size: %zd adrs: %p\n", sizeof(ptr), ptr);
return (0);
}
実行結果例
malloc() size: 8 adrs: 0x7fe8adf05b10
free() size: 8 adrs: 0x7fe8adf05b10
calloc() size: 8 adrs: 0x7fe8adf05b10
free() size: 8 adrs: 0x7fe8adf05b10
malloc() size: 8 adrs: 0x7fe8adf05b10
realloc() size: 8 adrs: 0x7fe8adf05b10
free() size: 8 adrs: 0x7fe8adf05b10
./allocptr 0.00s user 0.00s system 0% cpu 0.314 total
マロック後にリアロックやフリーをしても、最初に取得したメモリアドレスは変わってません。
同じ変数を使用しているからと考えられます。
異なるサイズのバイトを確保してみる
さらに、マロックで確保したサイズと異なる合計サイズをカロックで確保してみます。
#include <stdio.h>//printf()
#include <stdlib.h>//malloc(), free(), calloc(), realloc()
int main(void)
{
char *ptr;
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
free(ptr);
printf(" free() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
ptr = calloc(5, 5);
printf(" calloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
free(ptr);
printf(" free() size: %zd adrs: %p\n", sizeof(ptr), ptr);
ptr = malloc(15);
printf(" malloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
ptr = realloc(ptr, 40);
printf("realloc() size: %zd adrs: %p\n", sizeof(ptr), ptr);
free(ptr);
printf(" free() size: %zd adrs: %p\n", sizeof(ptr), ptr);
return (0);
}
実行結果例
malloc() size: 8 adrs: 0x7ff469f05b10
free() size: 8 adrs: 0x7ff469f05b10
calloc() size: 8 adrs: 0x7ff469f05b20
free() size: 8 adrs: 0x7ff469f05b20
malloc() size: 8 adrs: 0x7ff469f05b10
realloc() size: 8 adrs: 0x7ff469f05b10
free() size: 8 adrs: 0x7ff469f05b10
./allocptr 0.00s user 0.00s system 0% cpu 0.275 total
マロックで最初に確保したよりも大きいサイズをそのあとでカロックで確保すると、アドレスの最後の二桁が変わっています(10 -> 20)。
しかし、カロックのアドレスをフリーしたあと、再びマロックで最初のマロックと同じバイト数を確保すると、最初のアドレスと同じ (10) になっています。
ただ、リアロックに関しては確保し直したバイト数が大きくても、直前のマロックのアドレスの値と全く変わっていません。
2度目のマロックに入れる値は最初のマロックの値 (上記では15) と同じか、それより小さいもの (3, 5 = 3 x 5、あるいはそれ未満) にすると、最後の二桁は同じ (上記結果では"10") になり、前に確保したバイト数よりも大きいものを新たに確保しようとすると、最後の二桁が変わる (上記結果では"20") ようです。
ご自身の環境で試してみて、気づいたことがあればコメントにて教えていただけると嬉しいです。
また、ひょっとすると、リアロックに関しては、確保し直す前とあまり極端に差のあるバイト数を確保しようとしたり、多用したりしない方がいいのかもしれないと思いました。詳しい方いらっしゃったら教えていただきたいです。
注意事項
また、コメントでご指摘いただいたのですが、
意図していなくても多重free(二重開放)をしてしまうと重大なバグにつながることもあるので、
free(ptr);
ptr = NULL;
とすることで安全を確保した方が良さそうです。
また、コンパイラ・環境によっては、
free(ptr);
printf(" free() size: %zd adrs: %p %s\n", sizeof(ptr), ptr, (char *)ptr);
はエラーとなる場合もあるようです。
今回はmalloc()やfree()などの基本的な挙動、確保したポインタのアドレスやサイズはどうなっているのかをみたかったため、最初のようなコードにしましたが、通常の開発などではこのような手法は避けた方がいいようです。基本的に存在しない、もしくは保証されていない情報にアクセスしようとする行為は避けた方がいいということかと思います。
これらもご自身の環境で試してみて、何かわかったことがあれば教えていただけると助かります。
その他、コメントで指摘・シェアいただいたことは順次できる限り反映させていただきます。
ありがとうございます。