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 & struct Type { static string get() { return "未知"; } };
template &<&> struct Type&<&> { static string get() { return "void"; } };
template &<&> struct Type& { static string get() { return "void"; } };
template &<&> struct Type& { static string get() { return "char"; } };
template & struct Type& { static string get() { return "指向" + Type&::get() + "的指針"; } };
template & struct Type& { static string get() { return Type&::get() + "、" + Type&::get(); } };
template & struct Type& { static string get() { return "「參數類型為" + Type&::get() + ",返回值類型為" + Type&::get() + "的函數」"; } };
template & struct Type& { static string get() { return "元素類型為" + Type&::get() + "且長度為" + to_string(N) + "的數組"; } };

int main() {

char*(*(*a)(void))[20];
cout &<&< "a的類型是" &<&< Type&::get() "。" &<&< endl; return 0; }

輸出結果:

a的類型是指向「參數類型為void,返回值類型為指向元素類型為指向char的指針且長度為2
0的數組的指針的函數」的指針。

這個結果和 @濤吳給出的結果一致。


cout &<&< tymeta::type_descriptor&() &<&< endl;

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指針》第三章:

解釋C指針聲明的步驟:

1. 首先著眼於標識符(變數名或者函數名)

2. 從距離標識符最近的地方開始,依照優先順序解釋派生類型(指針、數

組和函數)。優先順序說明如下,

(1) 用於整理聲明內容的括弧

(2) 用於表示數組的[],用於表示函數的()

(3) 用於表示指針的*

3. 解釋完成派生類型,使用「of」、「to」、「returning」將它們連接起來

4. 最後,追加數據類型修飾符(在左邊,int、double 等)

以 char*(*(*a)(void))[20] 為例解讀步驟如下:

1. 找出標識符是a

2. 因為*號在括弧內,所以 a is pointer

3. 接著同時出現表示函數的()和表示指針的*,根據前面提到的優先順序,先解釋表示函數的(),因此在第2步的基礎上完善為 a is pointer to function(void)

4. 接著是左邊的表示指針的*,在第3步的基礎上完善為 a is pointer to function(void) returning pointer

5. 接著同時出現表示數組的[]和表示指針的*,根據優先順序優先解釋數組,因此在第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 pointer

7. 最後再追加數據類型修飾符,在第6步基礎上完善為 a is pointer to function(void) returning pointer to array 20 of pointer to char


正如上面那個 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函數?

TAG:C編程語言 | CC | 編譯器 |