[英]Is it valid to typecast to a pointer to a variable length array type?
對指向可變長度數組類型的指針進行類型轉換是否有效?
int main() {
int n = 10;
int m = 20;
int (*p)[n][m] = malloc(sizeof(int[n][m]));
void *q = p;
int a = n;
int b = m;
(*(int (*)[a][b])q)[5][5] = 1;
printf("%zd %d\n", sizeof(*p), (*p)[5][5]);
return 0;
}
打印800 1
。
和
int a = 1;
仍然打印800 1
。
和
int a = 1;
int b = 1;
現在打印800 0
。
到哪一點是明確定義的行為?
這是未定義的行為,因為您訪問的數組越界。
可能值得指出的是,C 類型系統在這里有點混亂。 使用malloc
分配的malloc
在訪問之前不會被賦予類型。 我將引用 C17 6.5/6 並在兩者之間留下評論:
訪問其存儲值的對象的有效類型是該對象的聲明類型(如果有)。
從 malloc 返回的這個“堆塊”沒有聲明類型。
如果一個值通過一個類型不是字符類型的左值存儲到一個沒有聲明類型的對象中,那么左值的類型將成為該訪問的對象的有效類型,並且對於不修改儲值。
這意味着如果我們在堆塊中的給定位置存儲一些東西,該地址將獲得有效類型並被視為對象(在這種情況下為單個int
)。 編譯器並不真正知道整個塊是否應被視為數組、結構或其他東西。
對於沒有聲明類型的對象的所有其他訪問,對象的有效類型只是用於訪問的左值的類型。
這意味着如果我們在寫入之前讀取訪問此數據,它將被視為用於讀取的類型的變量。 (不過,堆塊不是由malloc
初始化的,因此在寫入之前讀取沒有多大意義。)
假設 32 位int
然后malloc(sizeof(int[n][m]));
分配一個10*20*4
= 800 字節的塊。 malloc 留出的這個塊還沒有有效的類型。 編譯器尚未跟蹤存儲在該數據中的類型。 您在數據上具有特定的指針類型這一事實不會改變這一點,只要數據未被取消引用,它就還沒有有效的類型。
通常,堆塊不會獲得類型,直到您執行[5][5]
取消引用並將1
寫入該塊中的某個位置。 現在可以說該位置具有有效類型int
。
然而,在你這樣做之前,你需要做*(int (*)[a][b])q
。 q
的類型在這里無關緊要 - 您對其進行轉換並說這是一個指向數組的指針。 然后您取消引用它並獲得一個數組 - 這是一個左值(它將衰減為指向數組第一個元素的指針)。 從那時起,至少在這個表達式中,編譯器可以假設它正在處理一個數組 - 無論該數組是否恰好存儲在沒有有效類型的內存中。 如果這個數組是一個 VLA 並且沒有足夠大的維度來滿足... [5][5] = 1;
訪問,您最終會得到普通的舊數組越界訪問,這是未定義的行為。
具體來說,UB 根據 6.5.6/8 加法運算符:
如果指針操作數和結果都指向同一個數組對象的元素,或者數組對象的最后一個元素之后,求值不會產生溢出; 否則,行為未定義。
arr[i]
100% 等價於*(arr+i)
, +
運算符設置了上面引用的指針算術規則 - 不允許使用指針算術來訪問數組或越界對象 - 這是未定義的行為。 這意味着編譯器可能會生成行為不正確的代碼,無論假定的地址是否有可用內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.