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-彼は少しの間彼のキープを獲得したと確信しています- -クワインをいじり回すことに戻ります。それを出荷!
または少なくとも、それが起こった可能性があります。