我有以下程式,它在 g 中編譯良好,但在 gcc 中編譯不正常。
g() 接受一個指向 char* 陣列的指標,并且不應修改 p 指向的任何內容。
無論如何要使其在 C 中正常作業?
% cat t.c
void g(const char* const* p) {}
int main()
{
char** p = 0;
g(p);
}
% g t.c
% gcc t.c
t.c: In function ‘main’:
t.c:5:5: warning: passing argument 1 of ‘g’ from incompatible pointer type [-Wincompatible-pointer-types]
g(p);
^
t.c:1:6: note: expected ‘const char * const*’ but argument is of type ‘char **’
void g(const char* const* p) {}
^
%
uj5u.com熱心網友回復:
錯誤訊息會告訴您需要了解的所有資訊。使 p 與 g 的引數具有相同的簽名。
const char* const* p = 0;
但是,如果您需要 p 指向的任何內容為只讀,那么此型別規范足以分配 p 和 g 引數(盡管可以更改陣列指向的任何內容):
char* const* p = 0;
uj5u.com熱心網友回復:
C中的情況
在 C 中,只允許為最頂層的指標級別隱式添加限定符:
6.3.2.3 指標
2對于任何限定符 q,指向非 q 限定型別的指標可以轉換為指向該型別的 q 限定版本的指標;存盤在原始指標和轉換后的指標中的值比較相等。
所以你只能隱式地將 const 添加到最頂層的指標,例如:
char** p = 0;
char* const* other = p; // ok
const char** other = p; // not ok: adding const not at top-level
這樣做的原因是,如果您跳過幾個環節,它將允許您獲得指向 const 值的非常量指標:
const char c = 'C';
char* ptr;
const char **ptrPtr = &ptr; // this is not legal
*ptrPtr = &c;
乍一看,這段代碼看起來很合理——你有ptrPtr那個指向ptr那個指向c.
問題是,您可以修改c通過ptr現在(這是不是const限定),例如:
*ptr = 'X'; // will change c
如果您想閱讀更多相關資訊,這里有一個關于此的常見問題解答條目。
如果你想顯式轉換,你仍然可以強制它:
void g(const char* const* p) {}
int main()
{
char** p = 0;
g((const char* const*)p);
}
C 中的情況
C 在可以隱式添加多少常量方面稍微寬松一些。
7.3.6 資格轉換
(1)型別 T 的限定分解是 cv i和 P i的序列,使得 T 是
“cv 0 P 0 cv 1 P 1 ? cv n-1 P n?1 cv n U” for n ≥ 0 ,
其中每個 cv i是一組 cv 限定符([basic.type.qualifier]),每個 P i是“指向”([dcl.ptr]),“指向型別 C i成員的指標” ([dcl.mptr])、“N i陣列”或“未知邊界陣列”([dcl.array])。如果 P i指定一個陣列,則 cv 限定符 cv i 1元素型別上的也被視為陣列的 cv 限定符 cv i。(2)兩種型別 T1 和 T2 是相似的,如果它們具有相同 n 的限定分解,使得對應的 P i分量要么相同,要么一個是“N i 的陣列”,另一個是“未知界的陣列” , U 表示的型別相同。
(3) T1和T2兩種型別的資格組合型是類似于T1的T3型,其資格分解是這樣的:
- (3.1)對于每一個 i > 0,cv 3 i是 cv 1 i和 cv 2 i 的并集,
- (3.2)如果 P 1 i或 P 2 i是“未知界陣列”,則 P 3 i是“未知界陣列”,否則為 P 1 i,并且
- (3.3)如果結果 cv 3 i與 cv 1 i或 cv 2 i 不同,或者結果 P 3 i與 P 1 i或 P 2 i 不同,則 const 被添加到每個 cv 3 k為 0 < k <我,
其中 cv j i和 P j i是 T j的限定分解的分量。
這基本上可以歸結為:
- 你總是可以將 const 添加到頂層
- 如果將 const 添加到較低級別,則該級別以上的所有級別也必須為 const
這種“常量傳播”對于避免 C 中存在的邊緣情況是必要的:獲取指向常量物件的非常量指標。
幾個例子:godbolt
int i = 1;
int* ip = &i;
int** ipp = &ip;
int*** ippp = &ipp;
const int* t1 = ip; // ok
const int* const* t2 = ipp; // ok
const int* const* const* t3 = ippp; // ok
// const int** t4 = ipp; // not ok
// const int*** t5 = ippp; // not ok
// const int* const** t6 = ippp; // not ok
int* const* t7 = ipp; // ok
int* const* const* t8 = ippp; // ok
// int* const** t9 = ippp; // not ok
}
結論
所以這就是為什么你的代碼用 g 而不是 gcc 編譯的原因:
C 允許您為多個指標級別隱式添加 const 限定符(只要您遵循上面概述的 da 規則),而 C 只允許您隱式地將 const 添加到頂級指標。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/402016.html
