456

就職の面接の一環としてテストを終えたばかりで、Google を参考にしても、1 つの質問に困惑しました。StackOverflow のクルーがそれを使って何ができるか見てみたい:

このmemset_16aligned関数には、16 バイトにアラインされたポインターを渡す必要があります。そうしないと、クラッシュします。

a) 1024 バイトのメモリをどのように割り当て、それを 16 バイトの境界に合わせますか?
b) のmemset_16aligned実行後にメモリを解放します。

{    
   void *mem;
   void *ptr;

   // answer a) here

   memset_16aligned(ptr, 0, 1024);

   // answer b) here    
}
4

17 に答える 17

626

元の答え

{
    void *mem = malloc(1024+16);
    void *ptr = ((char *)mem+16) & ~ 0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

決まった答え

{
    void *mem = malloc(1024+15);
    void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

ご要望に応じた説明

最初のステップは、万が一に備えて十分な予備スペースを割り当てることです。メモリは 16 バイトにアラインされている必要があるため (つまり、先頭のバイト アドレスは 16 の倍数である必要があります)、16 バイトを追加することで十分なスペースが確保されます。最初の 16 バイトのどこかに、16 バイトにアラインされたポインターがあります。( は、あらゆるmalloc()目的に対して十分に整列されたポインターを返すことになっていることに注意してください。ただし、「any」の意味は主に、基本型 ( 、、、オブジェクトへのポインター、および関数へのポインター) のようなものを意味します。グラフィックス システムで遊ぶなど、より専門的なことを行う場合、システムの残りの部分よりも厳密な調整が必要になる可能性があります。そのため、このような質問と回答があります。)longdoublelong doublelong long

次のステップは、void ポインターを char ポインターに変換することです。GCC にもかかわらず、void ポインターでポインター演算を行うことは想定されていません (また、GCC には、それを悪用した場合に通知する警告オプションがあります)。次に、開始ポインターに 16 を追加します。0x800001malloc()というありえないほどアラインされていないポインタが返されたとします。16 を追加すると、0x800011 が得られます。ここで、16 バイト境界に切り捨てたいので、最後の 4 ビットを 0 にリセットします。0x0F は最後の 4 ビットを 1 に設定します。したがって、~0x0F最後の 4 つを除くすべてのビットが 1 に設定されます。それに 0x800011 を加えると 0x800010 になります。他のオフセットを反復して、同じ演算が機能することを確認できます。

最後のステップ ,free()は簡単です. , の 1 つまたは自分に返された値に常に戻るだけです.free()それ以外は災害です. あなたはその値を保持するために正しく提供しました — ありがとう。フリーはそれをリリースします。malloc()calloc()realloc()mem

最後に、システムのパッケージの内部構造を知っていれば、malloc16 バイト アラインされたデータ (または 8 バイト アライン) を返す可能性が高いと推測できます。16 バイトでアラインされていれば、値をいじる必要はありません。ただし、これは危険で移植性がありません。他のmallocパッケージには最小アライメントが異なるため、別のことを行うときに 1 つのことを想定すると、コア ダンプが発生します。広い範囲内で、このソリューションは移植可能です。

posix_memalign()アラインされたメモリを取得する別の方法として、他の誰かが言及しました。これはどこでも利用できるわけではありませんが、多くの場合、これをベースとして実装できます。アラインメントが 2 の累乗であると便利であることに注意してください。他のアライメントはより厄介です。

もう 1 つコメントします。このコードは、割り当てが成功したかどうかをチェックしません。

修正

Windows Programmerは、ポインターに対してビット マスク操作を行うことはできないと指摘しました。実際、GCC (3.4.6 および 4.3.1 テスト済み) はそのように文句を言います。したがって、基本コードの修正バージョン (メイン プログラムに変換) は次のとおりです。また、指摘されているように、16 の代わりに 15 だけを自由に追加しました。私が使用しuintptr_tているのは、C99 がほとんどのプラットフォームでアクセスできるようになってからです。PRIXPTRステートメントで を使用しない場合は、代わりに を使用printf()するだけで十分です。[このコードにはCRによって指摘された修正が含まれています。これは、何年も前にBill Kによって最初に指摘された点を繰り返したものであり、私は今まで見落としていました。]#include <stdint.h>#include <inttypes.h>

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

int main(void)
{
    void *mem = malloc(1024+15);
    void *ptr = (void *)(((uintptr_t)mem+15) & ~ (uintptr_t)0x0F);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
    return(0);
}

そして、これはわずかに一般化されたバージョンで、2 の累乗のサイズで機能します。

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void memset_16aligned(void *space, char byte, size_t nbytes)
{
    assert((nbytes & 0x0F) == 0);
    assert(((uintptr_t)space & 0x0F) == 0);
    memset(space, byte, nbytes);  // Not a custom implementation of memset()
}

static void test_mask(size_t align)
{
    uintptr_t mask = ~(uintptr_t)(align - 1);
    void *mem = malloc(1024+align-1);
    void *ptr = (void *)(((uintptr_t)mem+align-1) & mask);
    assert((align & (align - 1)) == 0);
    printf("0x%08" PRIXPTR ", 0x%08" PRIXPTR "\n", (uintptr_t)mem, (uintptr_t)ptr);
    memset_16aligned(ptr, 0, 1024);
    free(mem);
}

int main(void)
{
    test_mask(16);
    test_mask(32);
    test_mask(64);
    test_mask(128);
    return(0);
}

test_mask()汎用の割り当て関数に変換するには、アロケータからの単一の戻り値でリリース アドレスをエンコードする必要があります。

面接官の問題

Uriのコメント: 今朝の読解力に問題があるのか​​もしれませんが、インタビューの質問で「1024 バイトのメモリをどのように割り当てますか」と具体的に述べられていて、明らかにそれ以上のメモリを割り当てているとします。それはインタビュアーからの自動的な失敗ではないでしょうか?

私の返信は 300 文字のコメントに収まりません...

場合によると思います。ほとんどの人 (私を含む) は、「1024 バイトのデータを格納でき、ベース アドレスが 16 バイトの倍数であるスペースをどのように割り当てるか」という意味で質問を受け取ったと思います。インタビュアーが 1024 バイト (のみ) を割り当てて 16 バイトに揃える方法を本当に意味している場合、オプションはさらに制限されます。

  • 明らかに、1 つの可能性は、1024 バイトを割り当ててから、そのアドレスに「アライメント処理」を与えることです。このアプローチの問題点は、実際に使用可能なスペースが適切に決定されないことです (使用可能なスペースは 1008 から 1024 バイトの間ですが、どのサイズを指定するために使用できるメカニズムがありませんでした)。
  • もう 1 つの可能性は、フル メモリ アロケータを記述し、返される 1024 バイト ブロックが適切に配置されていることを確認することです。その場合、提案されたソリューションとかなり似た操作を実行することになる可能性がありますが、アロケーター内に隠します。

ただし、面接担当者がこれらの回答のいずれかを期待していた場合、この解決策が密接に関連する質問への回答であることを認識し、会話を正しい方向に向けるために質問を再構成することを期待します。(さらに、もし面接官が本当にしつこいなら、私はその仕事をしたくありません; 不十分な正確さの要件への答えが訂正されずに炎上するなら、その面接官は安全に働くことができる人ではありません. )

世界は進む

質問のタイトルが最近変更されました。私を困惑させたのは、C のインタビューの質問でメモリ アラインメントを解決することでした。改訂されたタイトル (標準ライブラリのみを使用してアラインされたメモリを割り当てる方法は? ) は、わずかに改訂された回答を要求しています — この補遺はそれを提供します。

C11 (ISO/IEC 9899:2011) 追加機能aligned_alloc():

7.22.3.1aligned_alloc関数

あらすじ

#include <stdlib.h>
void *aligned_alloc(size_t alignment, size_t size);

説明
この関数は、アラインメントが で指定され、サイズが で指定され、値が不定でaligned_allocあるオブジェクトにスペースを割り当てます。の値は、実装によってサポートされている有効なアライメントであり、 の値はの整数倍でなければなりません。alignmentsizealignmentsizealignment

戻り値
このaligned_alloc関数は、NULL ポインターまたは割り当てられたスペースへのポインターを返します。

また、POSIX では次のように定義されていますposix_memalign()

#include <stdlib.h>

int posix_memalign(void **memptr, size_t alignment, size_t size);

説明

このposix_memalign()関数は、 でsize指定された境界に整列されたバイトを割り当てalignment、 で割り当てられたメモリへのポインタを返しますmemptr。の値はalignmentの 2 乗の倍数である必要がありsizeof(void *)ます。

正常に完了すると、 が指す値はmemptrの倍数になりalignmentます。

要求されたスペースのサイズが 0 の場合、動作は実装定義です。で返される値はmemptr、ヌル ポインターまたは一意のポインターのいずれかになります。

このfree()関数は、以前に によって割り当てられたメモリの割り当てを解除しますposix_memalign()

戻り値

正常に完了すると、posix_memalign()ゼロを返します。それ以外の場合は、エラーを示すためにエラー番号が返されます。

現在、これらのいずれかまたは両方を使用して質問に回答できますが、質問が最初に回答されたときは、POSIX 関数のみがオプションでした。

舞台裏では、新しい整列メモリ関数は、質問で概説されているのとほぼ同じ仕事をしますが、整列をより簡単に強制し、整列メモリの開始を内部的に追跡して、コードが特別に対処する必要があります—使用された割り当て関数によって返されたメモリを解放するだけです。

于 2008-10-22T23:27:13.923 に答える
62

質問の見方によって、わずかに異なる 3 つの回答があります。

1) 質問された正確な質問には、Jonathan Leffler の解決策で十分ですが、16 アラインに切り上げるには、16 バイトではなく 15 バイトしか必要ありません。

A:

/* allocate a buffer with room to add 0-15 bytes to ensure 16-alignment */
void *mem = malloc(1024+15);
ASSERT(mem); // some kind of error-handling code
/* round up to multiple of 16: add 15 and then round down by masking */
void *ptr = ((char*)mem+15) & ~ (size_t)0x0F;

B:

free(mem);

2) より一般的なメモリ割り当て関数の場合、呼び出し元は 2 つのポインター (使用するポインターと解放するポインター) を追跡する必要はありません。したがって、アラインされたバッファーの下にある「実際の」バッファーへのポインターを格納します。

A:

void *mem = malloc(1024+15+sizeof(void*));
if (!mem) return mem;
void *ptr = ((char*)mem+sizeof(void*)+15) & ~ (size_t)0x0F;
((void**)ptr)[-1] = mem;
return ptr;

B:

if (ptr) free(((void**)ptr)[-1]);

mem に 15 バイトしか追加されなかった (1) とは異なり、実装が malloc からの 32 バイト アラインメントを保証する場合、このコードは実際にアラインメントを減らす可能性があることに注意してください (可能性は低いですが、理論的には C 実装は 32 バイトを持つ可能性があります)。整列タイプ)。memset_16aligned を呼び出すだけであれば問題ありませんが、構造体にメモリを使用する場合は問題になる可能性があります。

実装固有のアラインメント保証が何であるかをプログラムで判断する方法がないため、これに対する適切な修正が何であるかはわかりません(返されたバッファーが任意の構造体に必ずしも適しているとは限らないことをユーザーに警告することを除いて)。起動時に 2 つ以上の 1 バイト バッファーを割り当てることができると思います。表示される最悪のアラインメントが保証されたアラインメントであると想定します。間違っていると、メモリが無駄になります。もっと良いアイデアを持っている人は、そう言ってください...

[追加: 「標準的な」トリックは、必要なアラインメントを決定するために「最大限にアラインメントされる可能性が高い型」の結合を作成することです。long long最大限にアラインされた型は、(C99 では) ' '、' long double'、' void *'、または ' 'である可能性がありvoid (*)(void)ます。を含める場合<stdint.h>、おそらく ' intmax_t' を代わりに使用できますlong long(そして、Power 6 (AIX) マシンでintmax_tは、128 ビット整数型になります)。そのユニオンのアラインメント要件は、単一の char の後にユニオンが続く構造体に埋め込むことによって決定できます。

struct alignment
{
    char     c;
    union
    {
        intmax_t      imax;
        long double   ldbl;
        void         *vptr;
        void        (*fptr)(void);
    }        u;
} align_data;
size_t align = (char *)&align_data.u.imax - &align_data.c;

次に、要求されたアライメント (例では 16) とalign上記で計算された値の大きい方を使用します。

(64 ビット) Solaris 10 では、結果の基本的なアラインメントmalloc()は 32 バイトの倍数のようです。
]

実際には、アラインされたアロケーターは、ハードワイヤードではなく、アラインメントのパラメーターを取ることがよくあります。したがって、ユーザーは、関心のある構造体のサイズ (またはそれ以上の最小の 2 のべき乗) を渡すと、すべてがうまくいきます。

3) プラットフォームが提供するものを使用します: posix_memalignPOSIX の場合_aligned_malloc、Windows の場合。

4) C11 を使用する場合、最もクリーンで移植性が高く簡潔なオプションはaligned_alloc、このバージョンの言語仕様で導入された標準ライブラリ関数を使用することです。

于 2008-10-23T00:22:32.863 に答える
40

試すこともできますposix_memalign()(もちろん、POSIX プラットフォームで)。

于 2008-10-22T23:36:01.457 に答える
20

これが「切り上げ」部分への代替アプローチです。最も見事にコード化されたソリューションではありませんが、それは仕事を成し遂げます、そしてこのタイプの構文は少し覚えやすいです(さらに2の累乗ではないアライメント値に対しても機能します)。uintptr_tコンパイラをなだめるためにキャストが必要でした。ポインタ演算は、除算や乗算はあまり好きではありません。

void *mem = malloc(1024 + 15);
void *ptr = (void*) ((uintptr_t) mem + 15) / 16 * 16;
memset_16aligned(ptr, 0, 1024);
free(mem);
于 2008-10-23T00:46:25.310 に答える
20

残念ながら、C99 では、C99 に準拠するすべての C 実装で移植可能な方法で、あらゆる種類のアライメントを保証することはかなり難しいようです。なんで?ポインターが「バイトアドレス」であるとは限らないため、フラットメモリモデルで想像するかもしれません。uintptr_tの表現もそれほど保証されておらず、それ自体はオプションの型です。

単純なバイトアドレスであるvoid * (および定義により、char *も)の表現を使用するいくつかの実装を知っているかもしれませんが、C99 では、プログラマーである私たちには不透明です。実装は、 set { segment , offset }によってポインターを表す場合があります。ここで、 offsetは、「実際には」誰が何を知っているかのアライメントを持つことができます。ポインターは、何らかの形のハッシュ テーブル ルックアップ値や、リンク リスト ルックアップ値でさえある可能性があります。境界情報をエンコードできます。

C 標準の最近の C1X ドラフトでは、_Alignasキーワードが見られます。それは少し役立つかもしれません。

C99 が提供する唯一の保証は、メモリ割り当て関数が、任意のオブジェクト型を指すポインターへの割り当てに適したポインターを返すことです。オブジェクトのアラインメントを指定できないため、明確に定義された移植可能な方法でアラインメントを担当する独自の割り当て関数を実装することはできません。

この主張について間違っているのは良いことです。

于 2010-08-07T10:36:21.557 に答える
15

16 対 15 バイト カウントのパディング フロントでは、N のアラインメントを取得するために追加する必要がある実際の数はmax(0,NM)です。ここで、M はメモリ アロケーターの自然なアラインメントです (両方とも 2 のべき乗です)。

アロケータの最小メモリ アラインメントは 1 バイトであるため、15=max(0,16-1) は控えめな答えです。ただし、メモリ アロケータが 32 ビットの int で整列されたアドレスを提供することがわかっている場合 (これはかなり一般的です)、12 をパッドとして使用できます。

これはこの例では重要ではありませんが、保存されたすべての int がカウントされる 12K の RAM を搭載した組み込みシステムでは重要になる可能性があります。

実際に可能な限りすべてのバイトを保存しようとする場合、それを実装する最良の方法は、マクロとして使用することです。これにより、ネイティブ メモリ アラインメントをフィードできます。繰り返しますが、これはおそらく、すべてのバイトを保存する必要がある組み込みシステムにのみ役立ちます。

以下の例では、ほとんどのシステムでは値 1 で問題ありませMEMORY_ALLOCATOR_NATIVE_ALIGNMENTんが、32 ビットでアラインされた割り当てを持つ理論上の組み込みシステムでは、次のようにして貴重なメモリを少し節約できます。

#define MEMORY_ALLOCATOR_NATIVE_ALIGNMENT    4
#define ALIGN_PAD2(N,M) (((N)>(M)) ? ((N)-(M)) : 0)
#define ALIGN_PAD(N) ALIGN_PAD2((N), MEMORY_ALLOCATOR_NATIVE_ALIGNMENT)
于 2009-10-21T16:40:40.810 に答える
9

もしかしたら、彼らはメマラインの知識に満足していたでしょうか?そして、Jonathan Leffler が指摘しているように、知っておくべき 2 つの新しい望ましい関数があります。

おっと、フローリンは私を打ち負かしました。ただし、リンク先の man ページを読めば、以前のポスターで提供された例を理解できる可能性が高いでしょう。

于 2008-10-22T23:42:39.650 に答える
5

Accelerate.framework は、高度にベクトル化された OS X / iOS ライブラリで常にこの種のことを行っており、常にアライメントに注意を払う必要があります。かなりの数のオプションがありますが、そのうちの 1 つまたは 2 つは上で説明したものではありません。

このような小さな配列の最速の方法は、スタックに貼り付けることです。GCC/clang の場合:

 void my_func( void )
 {
     uint8_t array[1024] __attribute__ ((aligned(16)));
     ...
 }

free() は必要ありません。通常、これは 2 つの命令です。スタック ポインターから 1024 を引き、スタック ポインターと -alignment の AND をとります。おそらく、配列の寿命がスタックを超えたか、再帰が機能しているか、スタック領域が非常に貴重であるため、リクエスターはヒープ上のデータを必要としていました。

OS X / iOS では、malloc/calloc/etc へのすべての呼び出し。常に 16 バイトでアラインされます。たとえば、AVX 用に 32 バイトに揃える必要がある場合は、posix_memalign を使用できます。

void *buf = NULL;
int err = posix_memalign( &buf, 32 /*alignment*/, 1024 /*size*/);
if( err )
   RunInCirclesWaivingArmsWildly();
...
free(buf);

一部の人々は、同様に機能する C++ インターフェイスについて言及しています。

ページは 2 の累乗に整列されるため、ページ整列バッファも 16 バイト整列されることを忘れてはなりません。したがって、mmap() と valloc() およびその他の同様のインターフェースもオプションです。mmap() には、必要に応じて、バッファーをゼロ以外の値で事前に初期化して割り当てることができるという利点があります。これらはページにアラインされたサイズであるため、これらから最小割り当てを取得することはできず、最初に触れたときに VM 障害が発生する可能性があります。

Cheesy: ガード malloc などをオンにします。VM はオーバーランをキャッチするために使用され、その境界はページ境界にあるため、このようなサイズが n*16 バイトのバッファーは n*16 バイトにアラインされます。

一部の Accelerate.framework 関数は、ユーザー提供の一時バッファーを取り込み、スクラッチ スペースとして使用します。ここでは、渡されたバッファが大幅にずれており、ユーザーが積極的に私たちの生活を困難にしようとしていると仮定する必要があります。(私たちのテスト ケースでは、一時バッファーの直前と直後にガード ページを貼り付けて、それを強調しています。) ここでは、16 バイトで整列されたセグメントを保証するために必要な最小サイズを返し、その後手動でバッファーを整列させます。このサイズは、desired_size + アライメント - 1 です。したがって、この場合、1024 + 16 - 1 = 1039 バイトです。次に、次のように整列します。

#include <stdint.h>
void My_func( uint8_t *tempBuf, ... )
{
    uint8_t *alignedBuf = (uint8_t*) 
                          (((uintptr_t) tempBuf + ((uintptr_t)alignment-1)) 
                                        & -((uintptr_t) alignment));
    ...
}

アライメント 1 を追加すると、ポインターは最初にアライメントされたアドレスを通過して移動し、-alignment (アライメント = 16 の場合は 0xfff...ff0 など) と AND 演算すると、アライメントされたアドレスに戻ります。

他の投稿で説明されているように、16 バイトのアラインメントが保証されていない他のオペレーティング システムでは、より大きなサイズで malloc を呼び出し、後で free() のポインターを確保し、すぐ上で説明したようにアラインして、アラインされたポインターを使用できます。一時バッファのケースについて説明します。

aligned_memset に関しては、これはばかげています。整列されたアドレスに到達するために最大 15 バイトをループするだけで済み、その後、最後にいくつかの可能なクリーンアップ コードを使用して、整列されたストアに進みます。アライメントされた領域にオーバーラップするアライメントされていないストアとして (長さが少なくともベクトルの長さである場合)、または movmaskdqu などを使用して、ベクトル コードでクリーンアップ ビットを実行することもできます。誰かがただ怠けているだけです。ただし、stdint.h、ビットごとの演算子、およびメモリの基礎に慣れているかどうかをインタビュアーが知りたがっている場合、それはおそらく合理的なインタビューの質問です。したがって、不自然な例は許されます。

于 2014-06-05T05:19:14.063 に答える
5

私が理解しているように、ポインタを整数型に正式に変換することは未定義の動作であるため、標準のC99で求められていることを行うことは不可能であるというShao答えに誰も投票しなかったことに驚いています。( uintptr_t<->の変換を許可する標準は別として、標準void*では、値を操作してから元に戻すことは許可されていないようですuintptr_t。)

于 2011-07-14T16:34:12.040 に答える
4

memalign、Aligned-Memory-Blocks の使用は、問題の良い解決策かもしれません。

于 2010-10-12T18:09:09.643 に答える
3

この質問を読んで最初に頭に浮かんだのは、整列された構造体を定義し、それをインスタンス化し、それを指すことでした。

誰もこれを提案していないので、私が見逃している根本的な理由はありますか?

補足として、char の配列を使用したため (システムの char が 8 ビット (つまり 1 バイト) であると仮定)、__attribute__((packed))必ずしも必要があるとは思いません (間違っている場合は訂正してください)。とにかく。

これは、私が試した 2 つのシステムで動作しますが、コードの有効性と比較して誤検知を与えていることに気付いていないコンパイラーの最適化がある可能性があります。gcc 4.9.2OSXとgcc 5.2.1Ubuntuで使用しました。

#include <stdio.h>
#include <stdlib.h>

int main ()
{

   void *mem;

   void *ptr;

   // answer a) here
   struct __attribute__((packed)) s_CozyMem {
       char acSpace[16];
   };

   mem = malloc(sizeof(struct s_CozyMem));
   ptr = mem;

   // memset_16aligned(ptr, 0, 1024);

   // Check if it's aligned
   if(((unsigned long)ptr & 15) == 0) printf("Aligned to 16 bytes.\n");
   else printf("Rubbish.\n");

   // answer b) here
   free(mem);

   return 1;
}
于 2016-05-10T21:28:41.873 に答える
1

MacOS X 固有:

  1. malloc で割り当てられたすべてのポインターは、16 バイトでアラインされます。
  2. C11 がサポートされているので、aligned_malloc (16, size) を呼び出すだけで済みます。

  3. MacOS X は、起動時に memset、memcpy、memmove の個々のプロセッサに最適化されたコードを選択し、そのコードは聞いたことのないトリックを使用して高速化します。memset が手書きの memset16 よりも高速に実行される可能性は 99% であるため、質問全体が無意味になります。

100% ポータブルなソリューションが必要な場合、C11 より前には何もありません。ポインターのアライメントをテストする移植可能な方法がないためです。100% 移植可能である必要がない場合は、次を使用できます。

char* p = malloc (size + 15);
p += (- (unsigned int) p) % 16;

これは、ポインターを unsigned int に変換するときに、ポインターのアラインメントが最下位ビットに格納されることを前提としています。unsigned int に変換すると情報が失われ、実装で定義されますが、結果をポインターに変換し直さないため、それは問題ではありません。

もちろん、恐ろしいのは、元のポインターをどこかに保存して、 free () を呼び出す必要があることです。全体として、私はこの設計の賢明さを本当に疑っています.

于 2013-11-25T13:23:51.053 に答える
0

解決策として、メモリを整列させ、1バイトのメモリを無駄にしないパディングの概念を使用しました。

という制約がある場合、1 バイトも無駄にすることはできません。malloc で割り当てられたすべてのポインターは、16 バイトでアラインされます。

C11 がサポートされているので、呼び出すだけで済みますaligned_alloc (16, size)

void *mem = malloc(1024+16);
void *ptr = ((char *)mem+16) & ~ 0x0F;
memset_16aligned(ptr, 0, 1024);
free(mem);
于 2014-03-14T14:05:20.757 に答える
0

また、16 バイトを追加してから、ポインターの下に (16-mod) を追加することで、元の ptr を 16 ビットにプッシュすることもできます。

main(){
void *mem1 = malloc(1024+16);
void *mem = ((char*)mem1)+1; // force misalign ( my computer always aligns)
printf ( " ptr = %p \n ", mem );
void *ptr = ((long)mem+16) & ~ 0x0F;
printf ( " aligned ptr = %p \n ", ptr );

printf (" ptr after adding diff mod %p (same as above ) ", (long)mem1 + (16 -((long)mem1%16)) );


free(mem1);
}
于 2013-03-25T18:27:14.500 に答える
-3
long add;   
mem = (void*)malloc(1024 +15);
add = (long)mem;
add = add - (add % 16);//align to 16 byte boundary
ptr = (whatever*)(add);
于 2012-09-04T08:58:41.210 に答える