[英]Casting to pointer to function returning array - is it allowed?
這個問題是在某種程度上與此掛鈎一個 。
我的上一修訂版顯示了一段說明不能調用返回函數的數組的段落可能有一些實際用法。 請記住這一點($ 6.5.2.2.1):
表示被調用函數的表達式應具有指向函數的類型指針,該函數返回void或返回數組類型以外的完整對象類型。
返回數組的函數的局限性僅涉及函數“聲明符”和“定義”。 但是,如果我們看一下強制轉換運算符,則沒有禁止“函數類型”的規則,該函數將返回數組用作其中的“類型名稱”。
看一下'$ 6.5.4.2':
6.5.4演員
句法
投表達式:
unary-expression ( type-name ) cast-expression
約束
除非類型名稱指定了void類型,否則類型名稱應指定原子,限定或不限定的標量類型 ,並且操作數應具有標量類型。
現在,如果我們查看“ $ 6.2.5.21”:
21 算術類型和指針類型統稱為標量類型 。 數組和結構類型統稱為集合類型。
然后在“ $ 6.2.5.20”處:
—函數類型描述具有指定返回類型的函數。 函數類型的特征在於其返回類型以及其參數的數量和類型。 據說函數類型是從其返回類型派生的,如果其返回類型為T,則有時將該函數類型稱為“函數返回T”。 從返回類型構造函數類型的過程稱為“函數類型派生”。
—指針類型可以從稱為引用類型的函數類型或對象類型派生。 指針類型描述了一個對象,該對象的值提供對所引用類型的實體的引用。 從引用類型T派生的指針類型有時被稱為“ T的指針”。 從引用類型構造指針類型的過程稱為“指針類型派生”。 指針類型是完整的對象類型。
如我所見,沒有任何約束可以禁止這樣的事情:
void *ptr;
(int (*)()[4])ptr;
還是?
我比以前不太確定。 請參閱該答案底部的Jens Gustedt的評論和我的不完整分析。
通常說C不允許返回數組的函數,但是強制執行此限制的唯一約束是(引用N1570 C11草稿 ):
6.5.2.2p1(函數調用):
表示被調用函數的表達式應具有指向函數的類型指針,該函數返回
void
或返回數組類型以外的完整對象類型。
和6.7.6.3p1(函數聲明符):
函數聲明器不得指定函數類型或數組類型的返回類型。
(我在標准的第6部分中搜索了“約束”一詞。我認為我沒有錯過任何內容。如果這樣做了,我相信有人會指出這一點。)
強制轉換運算符中的類型名稱不是函數調用的一部分,也不是聲明符,因此這兩個約束均不適用。
結果,我相信這個程序:
int main(void) {
if (0) {
void *ptr;
(int (*)()[4])ptr;
}
}
是嚴格符合的,必須由符合的實現接受。 (我添加了if (0)
以避免與轉換的運行時語義有關的任何問題;將void*
轉換為函數指針的行為是不確定的。)
我認為,這意味着只要函數調用或函數聲明符中未使用表示類型的函數(返回數組)或函數(返回函數),就可以使用該類型名。 例如,它可以用於一般選擇, sizeof
或_Alignof
表達式以及其他幾種上下文中。
當然,這沒有用,可能只是委員會的疏忽。
我注意到gcc(帶有-std=c11 -pedantic
5.3.0版本)拒絕了帶有消息的類型名稱:
type name declared as function returning an array
這似乎是一種合理的診斷,但是嚴格來說,這是不合格的,因為沒有違反任何實際約束。
gcc暫時偏離了問題的主題,還抱怨道:
warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
這不是嚴格正確的。 ISO C 禁止這種轉換; 它只是沒有定義其行為。
更新:
Jens Gustedt的評論建議類型名稱是一個聲明符,因此,引用返回數組的函數的類型名稱違反了6.7.6.3p1中的約束。 讓我們來看一下,遵循N1570附件A中的語法,並參考其中的章節編號。
該約束是指“功能聲明器”。 由於沒有稱為function-declarator的語法生成,因此必須引用一個引用了函數類型的聲明器。 如果沒有聲明符,則不違反約束。
類型名稱int (*)()[4]
(如果有效)是指向函數的指針,該函數返回一個4 int
數組(感謝cdecl
)。
我的分析不完整。 我得稍后再講。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.