ここでは簡単な健全性チェックの質問があります。基本的な要件は、パフォーマンス上の理由から2つの柔軟な配列メンバーを構造体に入れてmallocへの呼び出し回数を減らすことです。voidの上に構造体タイプを実装する*
structインスタンスが定数オフセットで複数のフィールドを含む整列されたメモリブロックであると仮定すると、オフセット計算とキャストを記述することによって、構造体と意味的に同等な機能を実装できますか?アライメント/計算が一貫したオフセットを維持
void f()
{
typedef struct
{
double x;
char y;
int32_t foo;
double z;
} equivalent;
equivalent * e = malloc(sizeof(equivalent));
free(e);
static_assert(sizeof(equivalent) == 24,"");
char* memory = malloc(24);
double* x = (double*) (0 + memory);
char* y = (char *) (8 + memory);
int32_t* foo = (int32_t*) (12 + memory);
double* z = (double*) (16 + memory);
free(memory);
}
は面倒ですが、種類と仮定すると、クライアントコードは、そのいずれかを確認する必要はありませんとにかく不透明です。同様に構文上のオーバーヘッドは隠されています。
私はエイリアシングルールをC11(「効果的なタイプ」の部分)で明確にして読んだことがあり、そこにはっきりとしていると思います。
この公正ゲームですか?私は非常に鈍いコードをたくさん書く前に第二の意見を求めると思った。
乾杯
編集:ジョナサン・レフラーに対する応答として、これは私がメモリの単一のブロックにランタイム所定の長さの配列のカップルを置くつもり方法の簡単な&汚いスケッチです。
私は、配列の位置を計算するために使用される整数を格納することをお勧めします。これは、構造体のコピーをより簡単にするためです。適切に初期化されたポインタを格納し、それらをコピーに再配置する方がおそらく高速です。退屈なものは何もありません
void* g(uint64_t N_first, uint64_t N_second)
{
// desired representation:
// uint64_t N_first;
// int32_t first[N_first];
// uint64_t N_second;
// double second[N_second];
// this function doesn't populate the arrays, only
// allocates storage and sets up the length fields
uint64_t bytes_for_lengths = 16;
char* bytes = malloc(bytes_for_lengths + bytes_for_first(N_first) +
bytes_for_second(N_second));
uint64_t* ptr_N_first = get_N_first(bytes);
*ptr_N_first = N_first;
uint64_t* ptr_N_second = get_N_second(bytes);
*ptr_N_second = N_second;
return (void*)bytes;
}
// I haven't decided how best to factor out the field access
// and associated functions yet, so this is not optimal
uint64_t* get_N_first(void* vdata)
{
char* data = (char*)vdata;
return (uint64_t*)(data + 0);
}
int32_t* get_first(void* vdata)
{
char * data = (char*)vdata;
return (int32_t*)(data + 8);
}
uint64_t bytes_for_first(uint64_t N_first)
{
// first is an int32_t
// the next field needs to be 8 byte aligned
uint64_t bytes = 4 * N_first;
if (bytes % 8 != 0)
{
bytes += 4;
}
return bytes;
}
uint64_t* get_N_second(void* vdata)
{
uint64_t n_first = *get_N_first(vdata);
uint64_t first_bytes = bytes_for_first(n_first);
char* data = (char*)vdata;
return (uint64_t*)(data + 8 + first_bytes);
}
double* get_second(void* vdata)
{
char * data = (char*)vdata;
uint64_t n_first = *get_N_first(vdata);
uint64_t first_bytes = bytes_for_first(n_first);
return (double*)(data + 8 + first_bytes + 8);
}
uint64_t bytes_for_second(uint64_t N_second)
{
// second is a double
return 8 * N_second;
}
ここまでは何も問題ありません。不明な点は、これを「構造体内の2つの柔軟な配列メンバー」を実現するためにどのように適応させるかです。あなたはそれを直接行うことはできません。構造体に2つのポインタを使用し、メモリを連続して割り当てることによって、結果を慎重にほぼ達成できますが、注意する必要があります。 –
@JonathanLefflerありがとうございました。私はこれをどのように適応させるつもりのスケッチを加えました。明らかにエラーが起こりやすいのは明らかですが、Cがうまくやってくれれば解決できます。 –
コードをコンパイルした後、これはどのように有効ですか?私は半分まともなコンパイラは、多かれ少なかれ同じコードを生成するか、またはsteuctメンバーにアクセスするために、または手動オフセットを行うための少なくとも速いコードとして少なくともと仮定します。しかし、私はちょうどここにポイントを見て失敗するかもしれない – adam10603