2011-10-21 62 views
1
#include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <assert.h> 

    static int cmpstringp(const void *p1, const void *p2) 
    { 
     /* The actual arguments to this function are "pointers to 
      pointers to char", but strcmp(3) arguments are "pointers 
      to char", hence the following cast plus dereference */ 

     return strcmp(* (char * const *) p1, * (char * const *) p2); 
    } 

    int main(int argc, char *argv[]) 
    { 
     int j; 

     assert(argc > 1); 

     qsort(&argv[1], argc - 1, sizeof(argv[1]), cmpstringp); 

     for (j = 1; j < argc; j++) 
      puts(argv[j]); 
     exit(EXIT_SUCCESS); 
    } 

我很困惑這個部分:強制轉換爲qsort函數指針

 return strcmp(* (char * const *) p1, * (char * const *) p2); 

他們爲什麼這樣做呢?爲什麼他們不這樣做:(const char**)(const char * const*)?如果我們取消引用(const char**)一次,我們不會得到一個指向const char的指針嗎?解引用第二個,我們不會得到一個指向const char的const指針。這兩者似乎都是strcmp()所要求的:兩位指向常量字符的指針。手冊頁似乎給了我們const指針指向非const的東西,這似乎不是strcmp()的聲明所要求的。即使合法,給一個函數提供一些不符合其參數的東西似乎也不是一個好主意。我錯過了什麼嗎?

最後,爲什麼不以下產生至少一個錯誤警告:

auto const char * const ptr3 = *(const char **) ptr1; //where ptr1 is  
    of the form int foo(const void * ptr). 

提領ptr1一次給了我們一個指向一個const char,但本身不是常量。但是,ptr3是常量。那爲什麼編譯器不生成警告?我是否錯過了一些東西,或者有什麼理由不應該產生警告?你的問題的

+0

忘記'auto'是C中的關鍵字 - 或者不使用它。 C++ 11爲它定義了一個用途,但在C99中它是噪聲。 –

回答

0

第一部分,我覺得有你在哪裏放置const,所以const char **char * const *是相同的一定的靈活性。

你的問題的第二部分,任何指針可以被分配到一個const指針。它會以另一種方式產生錯誤。你的顯式轉換刪除了編譯器關於指針原始類型的任何知識。

+2

有一些靈活性,但是'const char **'(指向const char指針的指針)僅等價於'char const **',而'char * const *'(指向const的指針指向char)是不同的。 –

2

最後一個問題第一:

這是罰款:

const void *ptr = ...; 
const char * const ptr3 = *(const char **) ptr1; 
^^^^^^^^^^^^ ~~~~~   ^^^^^^^^^^^^ 
    |       | 
    +----------------------------+ 

在這裏,我可以用一個typedef簡化它:

typedef const char * string; 
const void *ptr = ...; 
const string ptr3 = *(string *) ptr; 
~~~~~ ^^^^^^   ^^^^^^ 
     |    | 
     +----------------+ 

的局部變量const(中一個用~~~~~加下劃線)並不是非常必要的:它指定了局部變量ptr3const,而不是它指向的數據是const。

下一個問題:爲什麼(或爲什麼)不是*(const char * const *) ptr1

那麼,*(const char * const *) ptr1的類型是const char * constconst string如果您使用typedef。但它只被用作右值。 const和非const rvalue沒有區別。例如,看一下下面的代碼:

void *ptr = ...; 
int x = *(const int *) ptr; 
int y = *(int *) ptr; 

顯然,xy得到相同的值。因此,在這裏添加const沒有真正的語義好處,它只是額外的輸入。

但:一些編譯器會發出警告......

const void *ptr = ...; 
string ptr2 = *(string *) ptr; 

既然你把指針轉換const void的指針非const string,一些編譯器可能會給您丟棄預選賽警告。 C++編譯器甚至不應該讓這樣的代碼通過,因爲他們會要求const_castconst void *轉換爲string *(又名const char *const *)。

但是:反向轉換是好的。將指向非const對象的指針傳遞給strcmp是很好的。 strcmp指向常數數據的事實表明strcmp本身不會修改數據。

有很多方法可以編寫好的函數。下面是一些例子。

return strcmp(*(char **) p1, *(char **) p2); 
return strcmp(*(const char **) p1, *(const char **) p2); 
return strcmp(*(char * const *) p1, *(char * const *) p2); 
return strcmp(*(const char * const *) p1, *(const char * const *) p2); 

// The following are not technically portable, but are portable in practice 
return strcmp(*(void **) p1, *(void **) p2); 
return strcmp(*(const void **) p1, *(const void **) p2); 
return strcmp(*(void * const *) p1, *(void * const *) p2); 
return strcmp(*(const void * const *) p1, *(const void * const *) p2); 

既然你無論如何鑄造變量,編譯器將讓很多不同的潛在錯誤幻燈片,但一些編譯器可能給予警告。使用適合您編碼風格的版本。

最後提示:關鍵字auto已過時。這些日子並不意味着什麼 - 它是默認的存儲類說明符。請勿使用auto

+0

感謝您的徹底回覆。然而,有一件事我無法繞開我的頭。你說:「因爲你正在將一個指向const的指針指向非const字符串的指針,所以一些編譯器可能會提示你放棄了限定符。」我感到困惑的是,const void * ptr是一個指向const數據的void指針。但是(string *)和(const char *)是相同的,它是一個指向const數據的指針。編譯器爲什麼會抱怨這個?聽起來好像void指針是一個指向const數據的常量指針。但是這不可能是正確的。那麼爲什麼演員呢? – user678392

+0

不,'string *'與'const char *'不同,它與'const char **'相同。這就是我使用typedef的原因。如果你把一個'const void *',它是一個指向const的指針,並將其轉換爲'string *',它是非const指針,那麼一些編譯器會抱怨。 –