長さでタグ付けされた配列を持つことの最大の問題は、その長さを保存するのに必要なスペースではなく、それをどのように保存するかという問題ではありません長い配列には余分なバイトがありますが、短い配列にも4バイトを使用することは可能です)。より大きな問題は、次のようなコードが与えられることです。
void ClearTwoElements(int *ptr)
{
ptr[-2] = 0;
ptr[2] = 0;
}
void blah(void)
{
static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
ClearTwoElements(foo+2);
ClearTwoElements(foo+7);
ClearTwoElements(foo+1);
ClearTwoElements(foo+8);
}
コードが最初の呼び出しを受け入れ、ClearTwoElements
2番目の呼び出しを拒否できる唯一の方法は、ClearTwoElements
メソッドが、どの場合でも、foo
どの部分を認識するかだけでなく、配列の一部への参照を受け取ることを知るのに十分な情報を受け取ることです。これは通常、ポインターパラメーターを渡すコストを2倍にします。さらに、各配列の前にアドレスのポインタが置かれている場合(検証の最も効率的な形式)、最適化されたコードはClearTwoElements
次のようになります。
void ClearTwoElements(int *ptr)
{
int* array_end = ARRAY_END(ptr);
if ((array_end - ARRAY_BASE(ptr)) < 10 ||
(ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||
(array_end - 4) < ADDRESS(ptr)))
trap();
*(ADDRESS(ptr) - 4) = 0;
*(ADDRESS(ptr) + 4) = 0;
}
メソッド呼び出し元は、一般に、配列の先頭へのポインタまたはメソッドへの最後の要素を完全に合法的に渡すことができることに注意してください。メソッドが、渡された配列の外側にある要素にアクセスしようとした場合にのみ、そのようなポインターは問題を引き起こします。したがって、呼び出されたメソッドは、まず、引数を検証するためのポインター演算が範囲外にならないように配列が十分に大きいことを確認してから、引数を検証するためのポインター計算を行う必要があります。そのような検証に費やされる時間は、実際の作業に費やされるコストを超える可能性があります。さらに、メソッドが記述されて呼び出された場合、メソッドはより効率的である可能性があります。
void ClearTwoElements(int arr[], int index)
{
arr[index-2] = 0;
arr[index+2] = 0;
}
void blah(void)
{
static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
ClearTwoElements(foo,2);
ClearTwoElements(foo,7);
ClearTwoElements(foo,1);
ClearTwoElements(foo,8);
}
オブジェクトを識別するための何かと、その一部を識別するための何かを組み合わせるタイプの概念は、良いものです。ただし、検証を実行する必要がない場合は、Cスタイルのポインターの方が高速です。