在純 C 中,指向 void 的指標可用作泛型函式的引數,例如泛型快速排序或泛型交換等,如下所示:
通用快速排序等的實作
但是,正如該鏈接上的通用磁區函式示例所示:
size_t generic_partition(void *v, size_t nelems, size_t size, compare_function comp)
{
// for brevity we'll be using the right-most 'element' for the
// location of the pivot storage. a better choice would use
// a median-of-three or median-of-five for pivot selection,
// then swap it to that location before the partitioning loop.
void *ploc = (unsigned char*)v (nelems-1) * size;
size_t pvt = 0;
for (size_t i=0; i<nelems; i)
{
if (comp((unsigned char*)v (i*size), ploc) < 0)
generic_swap((unsigned char*)v (pvt *size), (unsigned char*)v (i*size), size);
}
generic_swap((unsigned char*)v (pvt*size), ploc, size);
return pvt;
}
在回圈內,指標算術和型別轉換可能非常繁重。原因似乎是我們必須經常在 void* 和 char* 之間進行轉換,因為 char* 支持指標運算,而 void* 不支持(因為元素大小未知)。
那么為什么我們不使用 char* 而不是 void* 來實作泛型函式的泛型引數呢?它似乎具有 void* 的優點(通用性,因為不是每種型別都占用一個位元組的整數倍?)以及支持指標算術的額外優勢。
僅使用指向 char 的指標,而不使用指向 void 的指標,上述代碼中的回圈不會需要更少的加法、乘法和型別轉換嗎?如果是這樣,那不是更有效率嗎?
也許我錯過了與“comp”之類的函式自動將傳遞給它們的引數轉換為預期型別的??功能?
也許我一般都忽略了這一點。
編輯(包括 generic_swap 的代碼):
void generic_swap(void *a, void *b, size_t size)
{
if (a == b)
return;
unsigned char *p1 = a, *p2 = b;
while (size-- > 0)
{
unsigned char c = *p1;
*p1 = *p2;
*p2 = c;
}
}
編輯 - 這就是我避免乘法的意思(為了它的價值)。我把它寫成對 dbush 的好答案的修改:
size_t generic_partition(void *v, size_t nelems, size_t size, compare_function comp)
{
// for brevity we'll be using the right-most 'element' for the
// location of the pivot storage. a better choice would use
// a median-of-three or median-of-five for pivot selection,
// then swap it to that location before the partitioning loop.
unsigned char *pboundary = v;
unsigned char *ploc = pboundary (nelems-1) * size;
size_t pvt = 0;
unsigned char *pcurrent = pboundary size;
while (pcurrent < ploc)
{
if (comp(pcurrent, ploc) < 0)
{
generic_swap(pboundary, pcurrent, size);
pboundary = pboundary size;
pvt ;
}
pcurrent = pcurrent size;
}
generic_swap(pboundary, ploc, size);
return pvt;
}
編輯:還有更多的澄清。算術:
如果未定義while條件中的比較pcurrent < ploc,則可以將其替換為pcurrent != ploc。
另外,我知道有些極端情況可能會失敗,例如,如果陣列只有一個元素,但我認為我會小心現實生活中的那些情況,這里我的問題更多關于 void*、char* 和指標算術。
編輯:(第四個!)我很樂意將“while”回圈恢復為原始的“for”回圈,代價是額外的變數(迭代器),以換取在比較指標時避免可能的棘手行為通過 < 或通過 !=。
uj5u.com熱心網友回復:
到和從的轉換void *可以在沒有強制轉換的情況下發生,并且保證適用于任何物件型別。
void *實際上,您可以通過將引數分配給一次來消除強制轉換unsigned char *,然后在函式的其余部分中使用它:
size_t generic_partition(void *v, size_t nelems, size_t size, compare_function comp)
{
// for brevity we'll be using the right-most 'element' for the
// location of the pivot storage. a better choice would use
// a median-of-three or median-of-five for pivot selection,
// then swap it to that location before the partitioning loop.
unsigned char *pstart = v;
unsigned char *ploc = pstart (nelems-1) * size;
size_t pvt = 0;
for (size_t i=0; i<nelems; i)
{
if (comp(pstart (i*size), ploc) < 0)
generic_swap(pstart (pvt *size), pstart (i*size), size);
}
generic_swap(pstart (pvt*size), ploc, size);
return pvt;
}
請注意,在構造指標時,您仍然必須乘以元素大小。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/467406.html
