char*(*(*a)(void))[20];這個是個什麼意思?
在一個地方看來的,編譯器能編過,看上去像個函數指針的數組?但是中間有個*沒看懂什麼意思
void (*a)(void)
名叫 a 的函數指針,指向「接受類型為 void,返回類型也為 void 」的函數。
char *(*a)(void)
名叫 a 的函數指針,指向「接受類型為 void,返回類型為 char 指針」的函數。
char (*(*a)(void))[20]
名叫 a 的函數指針,指向「接受類型為 void,返回類型為指向長度 20 的 char 數組的指針」的函數。
char *(*(*a)(void))[20]
名叫 a 的函數指針,指向「接受類型為 void,返回類型為指向長度 20 的 char * 數組的指針」的函數。
推薦閱讀:Reading C type declarations
推薦安裝:
$&> cdecl
Type `help" or `?" for help
cdecl&> explain char *(*(*a)(void))[20];
declare a as pointer to function (void) returning pointer to array 20 of pointer to char
看了 @Skillness 的類型名稱模板,於是自己試著寫了一個簡單的中文版(不完整,可以自己加更多的特化版本):
#include &
#include &
#include &
using namespace std;
template &
template &<&> struct Type&<&> { static string get() { return "void"; } };
template &<&> struct Type&
template &<&> struct Type&
template &
template &
template &
template &
int main() {
char*(*(*a)(void))[20]; 輸出結果: 這個結果和 @濤吳給出的結果一致。
cout &<&< "a的類型是" &<&< Type&a的類型是指向「參數類型為void,返回值類型為指向元素類型為指向char的指針且長度為2
0的數組的指針的函數」的指針。
cout &<&< tymeta::type_descriptor&
pointer to function () -&> pointer to array (size 20) of pointer to char
來自https://zhuanlan.zhihu.com/p/24651043
C語言類型聲明是從外到里解方程,把右邊看做是表達式
char *(*(*a)(void))[20]中
*(*(*a)(void))[20]是一個char
所以(*(*a)(void))[20]是一個char*
所以*(*a)(void)是一個char*的數組,有20個元素
所以(*a)(void)是一個指向20個char*的數組的指針
所以*a是無參數並返回指向20個char*的數組的指針的函數
所以a是無參數並返回指向20個char*的數組的指針的函數的指針
這麼寫看起來好痛苦……學習的時候看看也就算了,我建議自己寫不要這麼寫,一層層分開寫,先用typedef定義一個函數指針類型先。企業裡面這麼寫,codereview是要被批的
問:*(*(*a)(void))[20] 的類型為 char,求 a 的類型
解:
*(*(*a)(void))[20] : char
=&> (*(*a)(void))[20] : 指向 char 的指針
=&> *(*a)(void) : 長度為 20、元素為[指向 char 的指針]的數組
=&> (*a)(void) : 指向[長度為 20、元素為[指向 char 的指針]的數組]的指針
=&> *a : 返回值類型為[指向[長度為 20、元素為[指向 char 的指針]的數組]的指針],沒有參數的函數
=&> a : 指向[返回值類型為[指向[長度為 20、元素為[指向 char 的指針]的數組]的指針,沒有參數的函數]的指針
char*(*(*a)(void))[20];
聲明一個指向函數的指針a,該函數不需要傳參數,
該函數返回一個指針,該指針指向一個包含20個元素的數組,該數組的元素是指向char類型的指針。要看懂這類複雜聲明,要理解幾個*、()、的優先順序:
1. []和()具有相同優先順序,比*的優先順序高2.相同優先順序,從左到右結合。
3. 然後理解這路里*和[]的作用
3.1 某個函數
char foo(void);想要聲明一個指向foo的指針。只需要char (*a)(void);//其實就是把函數名改成變數名,然後括弧包裹起來,變數名前加*
3.2 * 號作用其實就是表示後面的「東西」是一個指針。
3.3 [] 其實時表示前面的「東西」是個數組,具體幾個就是[20]裡面的數組20。
我開始分析這道題:char*(*(*a)(void))[20];
a. 從外層開始分析char*(xxxxx)[20];這裡出現*、()、[],根據第一條規則,先計算()裡面內容。
b、()括弧裡面內容是:*(*a)(void);據3.1條規則,這裡是個函數聲明 (*a)(void) 的主體部分;前面加了一個*,據3.2條規則就是聲明了一個函數,函數返回一個指針。
c、括弧裡面分析完了,繼續按優先順序和[20]結合:(*(*a)(void))[20];根據3.3條規則,函數返回值(前面()裡面的結果)是包含20個元素的數組。d、然後與最前面的*結合:*(*(*a)(void))[20];據3.2條規則,數組裡面的元素都是指針。f、char*(*(*a)(void))[20]; 最終完整的說明數組裡面的元素(是指針)指向的元素是char類型。手機打字好累哦建議搜索 右左規則,然後讀一下。記憶強度不高。
比如[C++右左規則 - CUCmehp(likesmiles) - 博客園]或者[複合類型的混合使用--右左法則和替換規則 - 博客頻道 - CSDN.NET]。
這題目是我隨便寫的入群考核問題,居然有人拿到網上提問, 不過大神真多,小弟佩服
https://zhuanlan.zhihu.com/p/25170481你需要先區分基本類型(注意是base_type不是build_in)和類型描述符(declarator)。一個聲明的結構必然是:base type+declarator
這樣的結構。
如果描述符只是一個標識符,那這個標識符就是基本類型,除非它是一個複合類型。也就是說描述符的組成除了一個標識符還有(),*,[ ],這樣的符號,它們會使該標識符的名字成為一個複合類型,比如指針,數組,函數。最後一個規則,除非有括弧影響閱讀順序,一律從右向左讀。忽然發現沒人引用 clockwise rule
http://c-faq.com/decl/spiral.anderson.html
按照上圖的概念,從 a 開始走:
1. 起點 a
a is ...
2. 遇到 ), a 在括弧裡面,先順時針繞回最內側的 * ,變 (*a)
a is a pointer to .....
3. (*a)走完,順時針繞到 (void)
a is a pointer to a function taking () parameter ....
4. (*a)(void) 走完,繞到 *, ()
a is a pointer to a function taking () parameter returning a pointer to ....
5. (*(*a)(void)) 走完,繞到 [20]
a is a pointer to a function taking () parameter returning a pointer to an array of 20 ....
6. (*(*a)(void))[20] 走完,繞到 char*
a is a pointer to a function taking () parameter returning a pointer to an array of 20 pointers to char
寫 CPPGM 時遇到過,折騰了一陣子,上面很多牛人們解釋了原理,這個回答嘗試引用一個個人覺得好用的快速方法,要詳細的原理解釋請參考其他牛人們的解答
20170301 update: 回的快發現寫錯東西,汗
char*(*(*a)(void))[20];
總有人說從a開始左右左右閱讀,卻從來不指出到底什麼時候左,什麼時候右。根據個人經驗,有如下結論:
類型說明符和運算符之間具有完美的對稱性,可以借用運算符的優先順序等知識來倒序閱讀:1. 首先找到變數名a;
2. a跟*結合,說明a是個指針,(*a)整體表示a解引用得到的類型;3. 然後(*a)跟(void)結合(因為括弧優先順序高於解引用),說明(*a)是一個函數,(*a)(void)整體表示用(void)作為參數列表進行調用,得到的返回值類型;4. 然後(*a)(void)跟*結合,說明這個返回值是一個指針,(*(*a)(void))整體表示這個返回值解引用得到的類型;5. 然後(*(*a)(void))跟[20]結合,說明上一步得到的是一個20長度的數組,(*(*a)(void))[20]整體表示這個數組取元素的結果類型;6. 然後(*(*a)(void))[20]跟*結合,說明上一步得到的是指針,*(*(*a)(void))[20]整體表示對其解引用得到的類型;7. 最後整體跟基本類型char結合,說明上一步得到的*(*(*a)(void))[20]是一個字元。注意最後這一步碰到基本類型的時候,不再表示對上一步的類型進一步做變換,而是表示上一步得到的類型就是基本類型char。
然後從基本類型char開始,把以上步驟反過來讀,就是這個類型說明符整體表示的類型:
以(字元的指針的(長度為20的)數組的指針)為返回值的、空參數的函數的指針。首先要明確的一點是C語言指針是以英語的語序來聲明的,因此我們要用閱讀英語的思維來解讀C指針。
以下內容摘錄自《征服C指針》第三章:以 char*(*(*a)(void))[20] 為例解讀步驟如下:1. 找出標識符是a2. 因為*號在括弧內,所以 a is pointer3. 接著同時出現表示函數的()和表示指針的*,根據前面提到的優先順序,先解釋表示函數的(),因此在第2步的基礎上完善為 a is pointer to function(void)4. 接著是左邊的表示指針的*,在第3步的基礎上完善為 a is pointer to function(void) returning pointer5. 接著同時出現表示數組的[]和表示指針的*,根據優先順序優先解釋數組,因此在第4步的基礎上完善為 a is pointer to function(void) returning pointer to array 20 6. 接著再解釋左邊的*,在第5步的基礎上完善為 a is pointer to function(void) returning pointer to array 20 of pointer7. 最後再追加數據類型修飾符,在第6步基礎上完善為 a is pointer to function(void) returning pointer to array 20 of pointer to char解釋C指針聲明的步驟:
1. 首先著眼於標識符(變數名或者函數名)2. 從距離標識符最近的地方開始,依照優先順序解釋派生類型(指針、數組和函數)。優先順序說明如下, (1) 用於整理聲明內容的括弧 (2) 用於表示數組的[],用於表示函數的() (3) 用於表示指針的*3. 解釋完成派生類型,使用「of」、「to」、「returning」將它們連接起來4. 最後,追加數據類型修飾符(在左邊,int、double 等)
正如上面那個 cdecl 工具,有一個翻譯網站,可以實現英語白話到 C 語言的雙向翻譯:
C gibberish ? English
char*(*(*a)(void))[20];
declare a as pointer to function (void) returning pointer to array 20 of pointer to char
傳說中的垃圾代碼
a是函數指針,函數返回的是指針,指向一個指針數組
從外往內一層一層看。char*()[20]括弧內是數組指針,*()()是一個函數指針,參數是void,返回值是指針a。
有個網站叫 C gibberish ? English
就醬
(出於習慣匿名了)
去看《C專家編程》相關章節你就明白了
char*(*(*a[20])(void));
狗尾續貂一下推薦閱讀:
※在C語言中,如何安全地使用void*?
※如何理解C語言關鍵字restrict?
※C 語言中不同類型指針的大小是否完全相同,為什麼?
※指針的指針定義為什麼用int ** ptr,而不是int *ptr?
※C語言如何封裝printf函數?