Cの `free`が解放されるバイト数を受け取らないのはなぜですか?


84

明確にするために:私はそれを知ってmallocおりfree、Cライブラリに実装されています。Cライブラリは通常、OSからメモリのチャンクを割り当て、独自の管理を行ってアプリケーションに少量のメモリを分割し、割り当てられたバイト数を追跡​​します。この質問は、無料がどれだけ解放するかをどのように知るかではありません。

むしろ、freeそもそもなぜこのように作られたのか知りたい。低水準言語であるため、Cプログラマーに、割り当てられたメモリだけでなく、どのくらいの量も追跡するように依頼するのは完全に合理的だと思います(実際、私は通常、バイト数を追跡​​することになります。とにかくmalloced)。また、バイト数を明示的に指定freeすると、パフォーマンスの最適化が可能になる場合があります。たとえば、割り当てサイズが異なる個別のプールを持つアロケータは、入力引数を確認するだけで、解放するプールを決定できます。全体的にスペースのオーバーヘッドが少なくなります。

だから、要するに、なぜし、mallocそしてfreeそれらは内部的に割り当てられたバイト数を追跡するために必要としているように作成?それは単なる歴史的な事故ですか?

ちょっとした編集:「割り当てた金額とは異なる金額を解放したらどうなるか」などのポイントを提供している人もいます。私が想像したAPIは、割り当てられたバイト数を正確に解放するために1つを必要とするだけです。多かれ少なかれ解放することは、単にUBまたは実装定義である可能性があります。しかし、他の可能性についての議論を思いとどまらせたくはありません。


18
割り当て自体を追跡するのはすでに面倒であり、さらにサイズを追跡する必要がある場合は、コードがさらに複雑になるためです。
Jens Gustedt 2014年

11
私はいくつかの理由を考えることができます:彼らがそうする必要がないのになぜユーザーにそれをさせるのですか?ユーザーがそれを台無しにした場合はどうなりますか?とにかく、それは一種の冗長な質問です。彼らが他の選択をした場合、あなたはまだ理由を尋ねているでしょう。
BoBTFish 2014年

15
@BoBTFish:これは、C、我々がないのPythonあるいはC ++、話をしています。ユーザーはすでに$ h!1トンを実行する必要はありません。それは理由ではありません。
user541686 2014年

4
K&Rもこれについては何も言うことはありません。私たちは好きなことをすべて推測することができますが、元の理由は歴史の中失われる可能性があると思います。
BoBTFish 2014年

35
の呼び出し元はmalloc返されたブロックのサイズを知らないため、プログラマーにブロックのサイズを正しく渡すように要求することはできません。 malloc多くの場合、要求よりも大きいブロックを返します。せいぜい、プログラマーはmalloc()呼び出しで要求されたサイズを渡すことができますが、それは実装者free()をまったく助けません。
Ben Voigt 2014年

回答:


97

1つの引数free(void *)(Unix V7で導入)には、mfree(void *, size_t)ここで言及していない以前の2つの引数に比べてもう1つの大きな利点があります。1つの引数は、ヒープメモリで動作する他のfreeすべてAPIを劇的に簡素化します。たとえばfree、メモリブロックのサイズが必要な場合、strdupどういうわけか1つ(ポインタ)ではなく2つの値(ポインタ+サイズ)を返す必要があり、Cは単一値の戻りよりも複数値の戻りをはるかに面倒にします。代わりに、char *strdup(char *)書くchar *strdup(char *, size_t *)か、さもなければなりませんstruct CharPWithSize { char *val; size_t size}; CharPWithSize strdup(char *)。(現在、2番目のオプションはかなり魅力的に見えます。これは、NULで終了する文字列が「コンピューティングの歴史の中で最も壊滅的な設計バグ」であることがわかっているためです。、しかしそれは後知恵です。70年代に、文字列を単純なものとして処理するCの機能char *は、実際にはPascalやAlgolなどの競合他社に対する明確な利点と見なされていました。)さらに、strdupこの問題に悩まされているだけでなく、すべてのシステム定義またはユーザー定義に影響します。ヒープメモリを割り当てる関数。

初期のUnix設計者は非常に賢い人でしたが、それfreeよりも優れている理由はたくさんあります。mfree基本的に、質問に対する答えは、彼らがこれに気づき、それに応じてシステムを設計したことだと思います。彼らがその決定をした瞬間に彼らの頭の中で何が起こっていたかの直接の記録を見つけることができるとは思えません。しかし、私たちは想像することができます。

2つの引数を持つV6Unixで実行するアプリケーションをCで作成していると仮定しますmfree。これまでは問題なく管理できましたが、プログラムがより野心的になり、ヒープに割り当てられた変数をますます使用する必要があるため、これらのポインターサイズを追跡することはますます面倒になっています。しかし、あなたは素晴らしいアイデアを持っています:これらをsize_t常にコピーする代わりに、割り当てられたメモリ内に直接サイズを隠しておくいくつかのユーティリティ関数を書くことができます:

void *my_alloc(size_t size) {
    void *block = malloc(sizeof(size) + size);
    *(size_t *)block = size;
    return (void *) ((size_t *)block + 1);
}
void my_free(void *block) {
    block = (size_t *)block - 1;
    mfree(block, *(size_t *)block);
}

そして、これらの新しい関数を使用して作成するコードが多いほど、それらはより素晴らしいように見えます。だけでなく、彼らが書くためにあなたのコードを簡単にするか、彼らはまた、あなたのコードを作るより速く-よく一緒に行っていない二つのことを!これらをsize_tあちこちに渡す前は、コピーにCPUオーバーヘッドが追加され、レジスターをより頻繁にこぼさなければならず(特に追加の関数引数の場合)、メモリが無駄になりました(ネストされた関数呼び出しが頻繁に発生するため)size_t異なるスタックフレームに格納されている複数のコピー)。新しいシステムでは、メモリを使用して保存する必要がありますsize_t、ただし1回だけで、どこにもコピーされることはありません。これらは小さな効率のように思えるかもしれませんが、256KiBのRAMを搭載したハイエンドマシンについて話していることに注意してください。

これはあなたを幸せにします!それで、あなたは次のUnixリリースに取り組んでいるひげを生やした男性とあなたのクールなトリックを共有します、しかしそれは彼らを幸せにしません、それは彼らを悲しませます。ご覧のとおり、彼らはのような新しいユーティリティ関数を追加する過程にありstrdup、新しい関数はすべて面倒なポインタとサイズを使用しているため、クールなトリックを使用している人は新しい関数を使用できないことに気付きます。 API。そして、それはあなたも悲しくなります。なぜならstrdup(char *)、システムバージョンを使用するのではなく、作成するすべてのプログラムで優れた関数を自分で書き直す必要があることに気付くからです。

ちょっと待って!これは1977年であり、下位互換性は今後5年間は発明されません。その上、真面目な人は誰も実際にこのあいまいな「Unix」のものをそのオフカラーの名前で使用していません。K&Rの初版は現在出版社に向かっていますが、それは問題ありません。最初のページで、「Cは文字列などの複合オブジェクトを直接処理する操作を提供していません...ヒープはありません。 ...」。歴史のこの時点で、string.hそしてmallocベンダー拡張(!)です。したがって、Bearded Man#1を提案しますが、好きなように変更できます。トリッキーなアロケーターを公式アロケーターとして宣言してみませんか?

数日後、Bearded Man#2は新しいAPIを見て、ちょっと待ってください。これは以前よりも優れていますが、サイズを格納するために割り当てごとに単語全体を費やしています。彼はこれを冒涜の次のことと見ています。他に何ができるので、他の誰もが彼を狂ったように見ています。その夜、彼は遅くまで滞在し、サイズをまったく保存しない新しいアロケーターを発明しましたが、代わりにポインター値に対して黒魔術のビットシフトを実行してその場で推測し、新しいAPIを所定の位置に維持しながらスワップインします。新しいAPIは、誰も切り替えに気付かないことを意味しますが、翌朝、コンパイラーが使用するRAMが10%少なくなることに気づきます。

そして今、誰もが満足しています:あなたはあなたの書きやすくて速いコードを手に入れます、Bearded Man#1はstrdup人々が実際に使う素敵なシンプルなものを書くようになりますそしてBearded Man#2-彼は少しの間彼のキープを獲得したと確信しています- -クワインいじり回すことに戻ります。それを出荷!

または少なくとも、それ起こった可能性があります。


14
エラー、それが不明確な場合に備えて、これは空想の飛行であり、芸術的な真実らしさを提供するために裏付けとなる詳細が投入されています。生きている人や死んでいる人との類似点は、関係者全員が同じように見えたからです。実際の歴史と混同しないでください。
Nathaniel J. Smith

5
あなたが勝ちます。これは私には最ももっともらしい説明(そして最高の文章)に見えます。ここのすべてが正しくないか無効であることが証明されたとしても、これはひげを生やした男性の優れた描写に対する最良の答えです。
jaymmer -復活モニカ

うわー、実際にもっともらしいように聞こえるこのページの答え。私から+1。
user541686 2014年

さらに良い-実際に楽しいこのページの答え!+1も。
デビッドC.ランキン2014

注目すべきPascalシステムが、マイクロコンピューターのBASICインタープリターと同様の方法でガベージコレクションされた文字列プールを使用したのではないかと思います。Cのセマンティクスはそのようなものでは機能しませんが、Pascalでは、コードが追跡可能なスタックフレームを維持していれば、そのようなものは非常にうまく処理できます(とにかく多くのコンパイラが行いました)。
スーパーキャット2015

31

「なぜfreeCでは解放されるバイト数を受け取らないのですか?」

ありませんので、それのための必要は、それは非常に意味がありませんとにかく。

何かを割り当てるときは、(明らかな理由で)割り当てるバイト数をシステムに通知する必要があります。

ただし、オブジェクトをすでに割り当てている場合は、返されるメモリ領域のサイズが決定されます。それは暗黙的です。これは、1つの連続したメモリブロックです。あなたはそれの一部を割り当て解除することはできません(忘れてくださいrealloc()、それはとにかくそれがしていることではありません)、あなたは全体を割り当て解除することしかできません。「Xバイトの割り当てを解除」することもmalloc()できません。取得したメモリブロックを解放するか、解放しないかのどちらかです。

そして今、それを解放したい場合は、メモリマネージャシステムに「これがこのポインタ、free()それが指しているブロックです」と伝えることができます。-そして、メモリマネージャは、暗黙的にサイズを知っているか、サイズを必要としない可能性があるため、その方法を知っています。

たとえば、の最も一般的な実装でmalloc()は、空きおよび割り当てられたメモリブロックへのポインタのリンクリストを維持します。にポインタを渡すfree()と、「割り当て済み」リストでそのポインタが検索され、対応するノードのリンクが解除されて「空き」リストに添付されます。リージョンサイズも必要ありませんでした。問題のブロックを再利用しようとする可能性がある場合にのみ、その情報が必要になります。


私があなたから$ 100を借りて、もう一度あなたから$ 100を借りて、それをさらに5回行う場合、私があなたから7回お金を借りたことを本当に気にしますか(実際に利息を請求していない限り)?? それとも私があなたから700ドルを借りたことを気にしますか?ここでも同じことが言えます。システムは、割り当てられていないメモリのみを考慮し、割り当てられたメモリがどのように分割されるかは考慮しません(必要ではなく、すべきではありません)。
user541686 2014年

1
@Mehrdad:いいえ、そうではありません。Cは、しかし、そうします。その全体的な目的は、物事を(少し)安全にすることです。私はあなたがここで何を探しているのか本当にわかりません。
軌道上でのライトネスレース

12
@ user3477950:バイト数を渡す必要はありません:ええ、このように設計されているためです。OPは、なぜこのように設計されたのかと
deduplicator 2014年

5
「必要がないので」-必要になるように設計されている可能性もあります。
user253751 2014年

5
@Mehrdadは、完全に不明確で誤ったアナロジーです。4バイトを100回割り当てる場合、どれを解放するかは間違いなく重要です。最初のものを解放することは、2番目のものを解放することと同じではありません。一方、お金の場合、最初の借り入れを最初に返済するか、2番目の借り入れを最初に返済するかは関係ありません。それはただ1つの大きな山です
user2520938 2014年

14

CはC ++ほど「抽象的」ではないかもしれませんが、それでもアセンブリを抽象化することを目的としています。そのために、最下位レベルの詳細が方程式から取り出されます。これにより、ほとんどの場合、すべてのCプログラムが移植できなくなるような位置合わせやパディングに悩まされる必要がなくなります。

要するに、これが抽象化を書くことの全体的なポイントです


9
配置またはパディングがこれとどのような関係があるのか​​わからない。答えは実際には何も答えません。
user541686 2014年

1
@Mehrdad Cはx86言語ではありません。移植性を(多かれ少なかれ)試みているため、プログラマーはその重大な負担から解放されます。とにかく他のさまざまな方法(インラインアセンブリなど)でそのレベルに到達できますが、抽象化が重要です。私はこの答えに同意します。
マルコA.

4
@Mehrdad:mallocNバイトを要求し、代わりにページ全体の先頭へのポインターを返した場合(配置、パディング、またはその他の制約のため、ユーザーがそれを追跡する方法はありません-強制的にそれは逆効果になります。
マイケルFoukarakis

3
@MichaelFoukarakis:malloc割り当てのサイズを保存せずに、常に整列されたポインタを返すことができます。free次に、適切な配置に切り上げて、すべてが適切に解放されるようにします。問題がどこにあるのかわかりません。
user541686 2014年

6
@Mehrdad:あなたが今述べた余分な仕事すべてに利益はありません。さらに、sizeパラメータを渡すと、free別のバグの原因が発生します。
Michael Foukarakis 2014年

14

実際、古代のUnixカーネルメモリアロケータでmfree()は、size議論がありました。malloc()そしてmfree()空きブロックアドレスとサイズに関する情報を含まれる2つのアレイ(コアメモリのための1つ、スワップのための別のもの)を維持しました。

Unix V6までユーザースペースアロケーターはありませんでした(プログラムは単にを使用しますsbrk())。UnixのV6で、iolibはとアロケータ含まalloc(size)およびfree()サイズの引数を取りませんでしたコールを。各メモリブロックの前には、そのサイズと次のブロックへのポインタがあります。ポインタは、空きリストをウォークするときに空きブロックでのみ使用され、使用中のブロックのブロックメモリとして再利用されました。

Unixの32VでとUnix V7で、これは新しいで置換されていたmalloc()free()実装、free()時間はかからなかったsize引数を。実装は循環リストであり、各チャンクの前には、次のチャンクへのポインターを含むワードと「ビジー」(割り当て済み)ビットがありました。そのmalloc()/free()ため、明示的なサイズを追跡することすらしませんでした。


10

なぜfreeCでは解放されるバイト数を受け取らないのですか?

する必要がないからです。この情報は、malloc / freeによって実行される内部管理ですでに利用可能です。

ここに2つの考慮事項があります(この決定に貢献したかもしれないし、貢献しなかったかもしれません):

  • 関数が必要のないパラメーターを受け取ることを期待するのはなぜですか?

    (これにより、動的メモリに依存する実質的にすべてのクライアントコードが複雑になり、アプリケーションに完全に不要な冗長性が追加されます)。ポインタの割り当てを追跡することは、すでに難しい問題です。関連するサイズとともにメモリ割り当てを追跡すると、クライアントコードの複雑さが不必要に増加します。

  • freeこれらの場合、変更された機能は何をしますか?

    void * p = malloc(20);
    free(p, 25); // (1) wrong size provided by client code
    free(NULL, 10); // (2) generic argument mismatch
    

    それは解放さませんか(メモリリークを引き起こしますか?)?2番目のパラメーターを無視しますか?exitを呼び出してアプリケーションを停止しますか?これを実装すると、おそらく必要のない機能のために、アプリケーションに追加の障害点が追加されます(必要な場合は、以下の最後のポイント「アプリケーションレベルでのソリューションの実装」を参照してください)。

むしろ、そもそもなぜこのように無料になったのか知りたい。

これが「適切な」方法だからです。APIは、それはそれの操作を実行するために必要な引数を必要としない、する必要がありこれ以上よりと

また、解放するバイト数を明示的に指定すると、パフォーマンスの最適化が可能になる場合があります。たとえば、割り当てサイズが異なる個別のプールを持つアロケータは、入力引数を確認するだけで、解放するプールを決定できます。全体的にスペースのオーバーヘッドが少なくなります。

それを実装する適切な方法は次のとおりです。

  • (システムレベルで)mallocの実装内-受信したサイズに基づいて、ライブラリ実装者が内部でさまざまな戦略を使用するためにmallocを作成することを妨げるものは何もありません。

  • (アプリケーションレベルで)mallocとfreeを独自のAPI内でラップし、代わりにそれらを使用します(アプリケーション内の必要な場所すべて)。


6
@ user3477950:バイト数を渡す必要はありません:ええ、このように設計されているためです。OPは、なぜこのように設計されたのかと
deduplicator 2014年

4
「必要がないので」-その情報を保存しないことで、必要になるように設計されている可能性もあります。
user253751 2014年

1
あなたのポイント2に関して、あなたはfree(NULL)が定義された振る舞いであるかどうか疑問に思っています。Aha、「Cライブラリのすべての標準準拠バージョンはfree(NULL)をno-opとして扱います」-ソースstackoverflow.com/questions/1938735/…-Mawg
はMonicaを2015

10

5つの理由が思い浮かびます。

  1. 便利です。プログラマーからのオーバーヘッドの全負荷を取り除き、エラーを追跡するのが非常に難しいクラスを回避します。

  2. ブロックの一部を解放する可能性を開きます。しかし、メモリ管理者は通常追跡情報を必要としているので、これが何を意味するのか明確ではありませんか?

  3. Lightness Races In Orbitは、パディングと位置合わせにスポットを当てています。メモリ管理の性質上、割り当てられる実際のサイズは、要求したサイズとはかなり異なる可能性があります。これは、割り当てられた実際のサイズを返すfreeために、サイズと場所mallocを変更する必要があることを意味します。

  4. とにかく、サイズを渡すことに実際の利点があるかどうかは明らかではありません。一般的なメモリマネージャには、サイズを含むメモリのチャンクごとに4〜16バイトのヘッダーがあります。このチャンクヘッダーは、割り当てられたメモリと割り当てられていないメモリに共通であり、隣接するチャンクが解放されると、一緒に折りたたむことができます。呼び出し元に空きメモリを格納させる場合、割り当てられたメモリに個別のサイズフィールドを持たないことで、チャンクごとにおそらく4バイトを解放できますが、呼び出し元がどこかに格納する必要があるため、そのサイズフィールドはおそらく取得されません。しかし、今では、情報がヘッダーチャンクに予測どおりに配置されるのではなく、メモリ内に分散されているため、操作効率が低下する可能性があります。

  5. それより効率的であったとしても、あなたのプログラムがとにかくメモリ解放するのに長い時間を費やしている可能性は根本的に低いので、利益はごくわずかです。

ちなみに、サイズの異なるアイテムに個別のアロケーターを使用するというアイデアは、この情報がなくても簡単に実装できます(アドレスを使用して、割り当てが発生した場所を特定できます)。これはC ++で日常的に行われます。

後で追加

別の答えは、かなりばかげて、このように機能する可能性のある証拠としてstd :: allocatorを提起しましたfreeが、実際には、なぜfreeこのように機能しないのかを示す良い例として役立ちます。malloc/ freedoとstd :: allocatorの機能には2つの重要な違いがあります。まず、mallocおよびfreeユーザーが直面している-一方で-彼らはとの仕事への一般的なプログラマのために設計されているstd::allocator標準ライブラリに専門的なメモリ割り当てを提供するように設計されています。これは、私の最初のポイントが重要ではない、または重要ではない場合の良い例を提供します。それはライブラリなので、とにかく追跡サイズの複雑さを処理することの難しさはユーザーから隠されています。

次に、std :: allocatorは常に同じサイズのアイテムで機能します。これは、最初に渡された要素の数を使用して、空きの量を決定できることを意味します。これがfreeそれ自体と異なる理由は実例です。std::allocatorそれらは常にアラインメント要件の同じ種類を有するように割り当てられるアイテム常に同じ、既知の、サイズおよびアイテムの常に同じ種類のものです。つまり、アロケータは、最初にこれらのアイテムの配列を単純に割り当て、必要に応じてそれらを実行するように特化することができます。あなたはでこれを行うことができなかったfree代わりに、それは時々 、発信者は*を要求し、これよりも大きなブロックを返すために、はるかに効率的である、復帰への最高のサイズはを求めたサイズであることを保証する方法がないため、どちらかユーザーまたはマネージャーは、実際に付与された正確なサイズを追跡する必要があります。これらの種類の実装の詳細をユーザーに渡すことは、呼び出し元に利益をもたらさない不必要な頭痛の種です。

-*まだこの点を理解しにくい人がいる場合は、次のことを考慮してください。一般的なメモリアロケータは、メモリブロックの先頭に少量の追跡情報を追加し、そこからポインタオフセットを返します。ここに格納される情報には、通常、たとえば次の空きブロックへのポインタが含まれます。ヘッダーの長さがわずか4バイト(実際にはほとんどの実際のライブラリよりも小さい)であり、サイズが含まれていないと仮定します。次に、ユーザーが16バイトのブロックを要求したときに20バイトの空きブロックがあると想像してください。システムは16バイトのブロックを返しますが、4バイトのフラグメントを残すため、毎回時間を無駄にすることはありません。malloc呼び出されます。代わりに、マネージャが単に20バイトのブロックを返す場合、これらの厄介なフラグメントが蓄積するのを防ぎ、使用可能なメモリをよりクリーンに割り当てることができます。ただし、システムがサイズ自体を追跡せずにこれを正しく実行する場合は、ユーザーが1回の割り当てごとに、無料で戻す場合に実際に割り当てられたメモリの量を追跡する必要があります。同じ引数が、目的の境界に一致しないタイプ/割り当てのパディングにも当てはまります。したがって、せいぜい、freeサイズを取る必要があることは、(a)メモリアロケータが実際に割り当てられたサイズと一致するように渡されたサイズに依存できないために完全に役に立たないか、(b)ユーザーが実際の追跡作業を行うことを無意味に要求することです。 賢明なメモリマネージャで簡単に処理できるサイズ。


#1は本当です。#2:どういう意味かわからない。#3についてはよくわかりませんが、配置には追加情報を保存する必要はありません。#4は循環論法です。メモリマネージャはサイズを格納するため、チャンクごとにそのオーバーヘッドのみを必要とします。したがって、サイズを格納する理由の引数としてそれを使用することはできません。そして#5は非常に議論の余地があります。
user541686 2014年

私は受け入れた後、受け入れなかった-これは良い答えのように見えるが、質問は多くの活動をしているようで、私は少し時期尚早だったかもしれないと思う。これは間違いなく私に考えるべき何かを与えてくれます。
jaymmer -復活モニカ

2
@jaymmer:ええ、それは時期尚早です。受け入れるまでに1、2日以上待つことをお勧めします。また、自分で考えることをお勧めします。これは非常に興味深い質問であり、StackOverflowでこのような「理由」の質問に対して最初に得られる回答のほとんど/すべては、根本的な質問に実際に対処するのではなく、現在のシステムを正当化するための半自動の試みにすぎません。
user541686 2014年

@Mehrdad:あなたは私が#4で言っていることを誤解しました。余分なサイズが必要になると言っているのではなく、(a)サイズを保存する必要がある人を移動して、実際にスペースを節約しないようにし、(b)結果として生じる変更によって実際にサイズが大きくなる可能性があると言っています効率が悪く、それ以上ではありません。#5に関しては、それが議論の余地があるとはまったく確信していません。私たちは、せいぜい、無料通話からいくつかの指示を保存することについて話しているのです。わずかな無料通話の費用と比較して。
ジャックエイドリー2014年

1
@Mehrdad:ああ、そして#3では、追加の情報は必要ありません。追加のメモリが必要です。16バイトのアラインメントで動作するように設計された一般的なメモリマネージャは、115バイトのブロックを要求されたときに128バイトのブロックへのポインタを返します。free呼び出しが解放されるサイズを正しく渡すことである場合、それはこれを知っている必要があります。
ジャックエイドリー2014年

5

私はこれを答えとして投稿しているのは、それがあなたが望んでいるものだからではなく、それが唯一のもっともらしい正しいものだと信じているからです。

もともとは便利だと思われ、その後は改善できませんでした。
説得力のある理由はおそらくありません。(ただし、間違っていることが示された場合は、喜んで削除します。)

そこしまうことが可能であった場合のメリットも:あなたは一度に少し解放そして、そのサイズはあなたが事前に知っていたメモリの単一の大きな部分を割り当てることができ-繰り返しメモリの小さな塊を割り当て、解放とは対照的です。現在、このようなタスクは不可能です。


サイズを渡すのはとてもばかげていると思うあなたの多く(多くの1!)に:

std::allocator<T>::deallocateメソッドのC ++の設計上の決定を参照できますか?

void deallocate(pointer p, size_type n);

が指すエリア内のすべてのオブジェクトは、この呼び出しの前に破棄されるものとします。このメモリを取得するために渡された値と一致する必要があります。n Tp
nallocate

この設計上の決定を分析するのは、かなり「興味深い」時間になると思います。


についてoperator deleteは、2013 N3778提案(「C ++サイズの割り当て解除」)もそれを修正することを目的としていることがわかりました。


1元の質問の下のコメントを見て、パラメータの欠如を正当化するために、要求されたサイズはfree呼び出しにはまったく役に立たない」などの急いで主張した人の数を確認しsizeます。


5
以下のためにmalloc()実装それはまた、覚えておく必要性を除去するであろうアライメントオーバーヘッドに割り当てオーバーヘッドを低減する、割り当てられた領域については。malloc()解放されたチャンク自体の中ですべての簿記を行うことができます。これが大幅に改善されるユースケースがあります。大きなメモリチャンクをいくつかの小さなチャンクに割り当て解除することは推奨されませんが、これにより断片化が大幅に増加します。
cmaster - REINSTATEモニカ

1
@cmaster:これらのユースケースは、まさに私が言及していた種類のものでした。ありがとう、頭に釘を打ちました。断片化について:メモリを小さなチャンクで割り当てたり解放したりするという代替案と比較して、断片化がどのように増加するかはわかりません。
user541686 2014年

std::allocator特定の既知のサイズの要素のみを割り当てます。これは汎用アロケーターではなく、リンゴとオレンジの比較です。
ジャックエイドリー2014年

Cの標準ライブラリを、事実上すべてを構築できる最小限のプリミティブのセットにするために、部分的に哲学的で部分的に設計上の決定がなされたように思われます。元々はシステムレベルの言語として意図されており、このプリミティブなアプローチが作成するシステムに移植可能です。センス。C ++では、標準ライブラリを非常に拡張する(そしてC ++ 11で大きくなる)という別の決定がなされました。より高速な開発ハードウェア、より大きなメモリ、より複雑なアーキテクチャ、およびより高レベルのアプリケーション開発に対処する必要性が、おそらくこの重点の変化に貢献しています。
クリフォード

1
@Clifford:その通りです-だからこそ、説得力のある理由はないと言ったのです。これは単なる決定であり、他の方法よりも厳密に優れていると信じる理由はありません。
user541686 2014年

2

mallocとfreeは密接に関連しており、各「malloc」は1つの「free」と一致します。したがって、前の「malloc」と一致する「free」は、そのmallocによって割り当てられたメモリの量を単純に解放する必要があることは完全に理にかなっています。これは、99%のケースで意味のある大多数のユースケースです。世界中のすべてのプログラマーがmalloc / freeをすべて使用し、プログラマーがmallocに割り当てられた量を追跡し、同じものを解放する必要がある場合、すべてのメモリエラーを想像してみてください。あなたが話しているシナリオは、実際には、ある種のメモリ管理の実装で複数のmalloc / freeを使用する必要があります。


5
私はあなたのような他のバグの工場を考えるとき議論の余地のある「すべての[...]エラーが想像」だと思いgetsprintfマニュアルループ(オフ・バイ・ワン)、未定義の動作、フォーマット文字列、暗黙の変換、ビットのトリック、ら、セテラ。
セバスチャンマッハ

1

この方法で(場合によっては)手動でサイズ情報を追跡する必要がなく、プログラマーのエラーが発生しにくいため、これをお勧めします。

さらに、reallocにはこの簿記情報が必要です。これには、割り当てサイズ以上のものが含まれていると思います。つまり、それが機能するメカニズムを実装定義することができます。

あなたはあなたが提案する方法でいくらか機能するあなた自身のアロケーターを書くことができます、そしてそれは特定の場合(潜在的に大規模なパフォーマンスの向上を伴う)の一種の同様の方法でプールアロケーターのためにc ++でしばしば行われますがこれは一般的にオペレーターの観点から実装されますプールブロックを割り当てるための新しい。


1

割り当てのサイズを追跡しないアロケーターがどのように機能するかわかりません。これを行わなかった場合、将来のmalloc要求を満たすためにどのメモリが使用可能であるかをどのようにして知ることができますか?使用可能なメモリブロックがどこにあるかを示すために、少なくともアドレスと長さを含むある種のデータ構造を格納する必要があります。(そしてもちろん、空きスペースのリストを保存することは、割り当てられたスペースのリストを保存することと同じです)。


明示的なサイズフィールドも必要ありません。次のブロックへのポインタと割り当てられたビットを持つことができます。
ninjalj 2014年

0

必要なのは、以前に割り当てたメモリを解放するために使用するポインタだけです。バイト数はオペレーティングシステムによって管理されるものなので、心配する必要はありません。free()によって返される割り当てられたバイト数を取得する必要はありません。実行中のプログラムによって割り当てられたバイト/位置の数を手動でカウントする方法をお勧めします。

Linuxで作業していて、mallocが割り当てたバイト/位置の量を知りたい場合は、mallocを1回またはn回使用して、取得したポインターを出力する単純なプログラムを作成できます。さらに、プログラムを数秒間スリープさせる必要があります(次のことを行うのに十分です)。その後、そのプログラムを実行し、そのPIDを探し、cd / proc / process_PIDと記述して、「catmaps」と入力します。出力には、ヒープメモリ領域(メモリを動的に割り当てているメモリ領域)の最初と最後のメモリアドレスの両方が特定の1行に表示されます。割り当てられているこれらのメモリ領域へのポインタを出力すると、割り当てたメモリの量を推測できます。

それが役に立てば幸い!


0

なぜそれが必要ですか?malloc()とfree()は意図的に非常に単純なメモリ管理プリミティブであり、Cでの高レベルのメモリ管理は主に開発者次第です。T

さらに、realloc()はすでにそれを行っています-realloc()で割り当てを減らすと、データは移動せず、返されるポインターは元のポインターと同じになります。

標準ライブラリ全体は、アプリケーションのニーズに合わせてより複雑な関数を構築できる単純なプリミティブで構成されているのが一般的です。したがって、「標準ライブラリがXを実行しないのはなぜですか」という形式の質問に対する答えは、プログラマーが考える可能性のあるすべてのことを実行できないため(プログラマーの目的)、ほとんど実行しないことを選択するためです。サードパーティのライブラリを使用します。より柔軟なメモリ管理を含む、より広範な標準ライブラリが必要な場合は、C ++がその答えかもしれません。

質問にCと同様にC ++のタグを付けましたが、C ++を使用している場合は、どのような場合でもmalloc / freeを使用することはほとんどありません-new / deleteを除いて、STLコンテナクラスはメモリを自動的に管理します。さまざまなコンテナの性質に特に適していること。


さらに、realloc()はすでにそれを行っています-realloc()で割り当てを減らすと、データは移動せず、返されるポインターは元のポインターと同じになります。これは保証された動作ですか?これはいくつかの組み込みアロケーターで一般的であるようですが、この動作がstandard-cの一部として指定されているかどうかはわかりませんでした。
rsaxvc 2014年

@rsaxvc:良い質問-cplusplus.comは、「新しいサイズが大きい場合、新しく割り当てられた部分の値は不確定です」と文書化しています。、小さい場合は確定的であることを意味します。[opengroup.org()は、「メモリオブジェクトの新しいサイズでオブジェクトの移動が必要になる場合、オブジェクトの以前のインスタンス化のためのスペースが解放されます」と述べています。-小さければ、データを移動する必要はありません。繰り返しになりますが、小さい方は再割り当てされません。ISO規格が何を言っているのかわかりません。
クリフォード2014年

1
reallocデータの移動は絶対に許可されています。Cの標準によれば、実装に完全に合法reallocとしてmalloc+ memcpy+ free。また、メモリの断片化を回避するためなど、実装でサイズが縮小された割り当てを移動する必要があるのには十分な理由があります。
ナサニエルJ.スミス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.