2016-04-19 8 views
-2

私は長さが "内側"に格納されている配列の次の実装を発見しました。ここでは、コードは次のとおりポインタalingmentと配列の長さの格納

double* construct(const size_t nElements) 
{ 
    double* arr = malloc(sizeof(double) * nElements + sizeof(int)); 
    int i = 0; 

    *(int*)arr = nElements; 
    ((int*)arr)++; 

    for (i = 0; i < nElements; i++) arr[i] = 1.0 * i;  

    return arr; 
} 

void destroy(double* arr) 
{ 
    free((int*)arr - 1); 
} 

void print(double* arr) 
{ 
    int N = *((int*)arr - 1); 
    int i = 0; 

    printf("Supplied array of %d elements:\n", N); 

    for (i = 0; i < N; i++) printf("%d : %f\n", i, arr[i]);  
} 

int main(void) 
{ 
    double* a = construct(10); 

    print(a);  
    destroy(a); 

    return 0; 
} 

この技術の背後にある考え方は、配列の長さを格納するために追加のスペースを有するメモリブロックを割り当て、そしてユーザーにのみポインタ実際のデータ開始を得ました。しかし、私はポインターキャストの点でこの特定の実装で何か怪しいと思う(doubleからint *へ)。

これは一般に許可されていますか?

「はい」の場合、ポインタの位置合わせはどのように機能しますか?

良いアプローチがありますか?

更新: このコードは「ハック」なので、これは悪いことが分かります。私はちょうど何が壊れているのか、理由を知りたい。

所定の回答のほとんど要約: 線((int*)arr)++;

1)のみMSVS

2)下でコンパイルを悪い形成されてはsizeof(INT)ずれべき次のアドレスを引き起こすオフセットを行います二重境界によって。

これは明らかですが、キャスト自体がdouble *からint *に許可されていますか?どのようにしてalingmentに影響を及ぼしますか?

+2

これはひどいハックです。 _flexible配列member_とともに 'struct'を使用してください。 – Olaf

+0

これは確かに疑わしいです。しかし、サイズを表す整数(これには 'size_t'を使います)と柔軟な配列メンバ' double'を持つ構造体を単純に持つことができます。 – EOF

+0

二重配列が正しく整列されていない可能性があります。それ以外では、コードが定義されているようです。たぶんポインタの算術演算は、unsigned charを使って行われているはずです。 – 2501

答えて

4

あなたが投稿したコードは無効です。理由は簡単です。malloc()は、マシン上で最大の共通タイプに合わせてメモリを返します。これはdoubleで十分ですが、返されたメモリはintの後にdoubleを保存するために使用されます。 doubleが64ビットで、intが32ビット(これはかなり一般的です)のプラットフォームでは、これは有効なコードではなく、予想よりもパフォーマンスが低下する可能性があります。

+0

このコードが有効でない理由を説明してください(私はそれほど疑わしいが、その理由やtechincalの詳細は分からない)。 – HighPredator

+3

@HighPredator 'double'より小さい' int'をスキップすると、配列のdouble型が現在誤って処理されているので、少なくとも 'double'値全体をスキップするか、一般的にスキップします:あなたが割り当てようとしている型は(または 'int'のどちらか大きい方)に整列されます。 – BeyelerStudios

+0

@BeyelerStudios [s] how so?[/ s]言い換えれば、「ダブルス」が最初に整列していない場合(元のポインタがint境界で整列されているなど)はどうでしょうか?それとも、割り振り後の二重ポインタに対するフリスト割り当ての効果ですか? – HighPredator