[英]Create a pointer to two-dimensional array
我需要一個指向靜態二維數組的指針。 這是怎么做的?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
我收到各種錯誤,例如:
在這里你想創建一個指向數組第一個元素的指針
uint8_t (*matrix_ptr)[20] = l_matrix;
使用 typedef,這看起來更干凈
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
然后你可以再次享受生活:)
matrix_ptr[0][1] = ...;
當心 C 中的指針/數組世界,圍繞這一點有很多困惑。
在此處查看其他一些答案,因為評論字段太短而無法在此處執行。 提出了多種替代方案,但沒有顯示它們的行為方式。 這是他們的做法
uint8_t (*matrix_ptr)[][20] = l_matrix;
如果您修復錯誤並在以下代碼段中添加地址運算符&
就像
uint8_t (*matrix_ptr)[][20] = &l_matrix;
然后那個創建一個指向不完整數組類型的元素的指針,該數組類型為 20 uint8_t。 因為指針指向一個數組數組,所以你必須用
(*matrix_ptr)[0][1] = ...;
而且因為它是指向不完整數組的指針,所以不能作為快捷方式
matrix_ptr[0][0][1] = ...;
因為索引需要知道元素類型的大小(索引意味着向指針添加一個整數,因此它不適用於不完整的類型)。 請注意,這僅適用於C
,因為T[]
和T[N]
是兼容類型。 C++ 沒有兼容類型的概念,因此它會拒絕該代碼,因為T[]
和T[10]
是不同的類型。
以下替代方法根本不起作用,因為數組的元素類型,當您將其視為一維數組時,不是uint8_t
,而是uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
以下是一個不錯的選擇
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
你訪問它
(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now
它的好處是保留了外部維度的大小。 所以你可以在它上面應用 sizeof
sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20
還有另一個答案利用數組中的項目連續存儲這一事實
uint8_t *matrix_ptr = l_matrix[0];
現在,這正式只允許您訪問二維數組的第一個元素的元素。 即,以下條件成立
matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid
matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior
您會注意到它可能在10*20-1
,但是如果您進行別名分析和其他積極優化,某些編譯器可能會做出可能破壞該代碼的假設。 話雖如此,我從未遇到過失敗的編譯器(但再說一次,我沒有在實際代碼中使用該技術),甚至 C FAQ 也包含該技術(並警告其 UB'ness ),如果你不能改變數組類型,這是拯救你的最后一個選擇:)
要完全理解這一點,您必須掌握以下概念:
首先(而且它已經被充分宣傳了),數組不是指針。 相反,在大多數用途中,它們“衰減”到它們的第一個元素的地址,該地址可以分配給一個指針:
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
我假設它以這種方式工作,以便可以訪問數組的內容而無需復制所有內容。 這只是數組類型的一種行為,並不意味着它們是同一回事。
多維數組只是一種以編譯器/機器可以理解和操作的方式“分區”內存的方法。
例如, int a[4][3][5]
= 一個包含 4*3*5 (60) 個“塊”的整數大小內存的數組。
使用int a[4][3][5]
與普通int b[60]
的優勢在於它們現在是“分區的”(如果需要,更容易處理它們的“塊”),並且程序現在可以執行邊界檢查。
事實上, int a[4][3][5]
與內存中的int b[60]
完全一樣 -唯一的區別是程序現在管理它,就好像它們是特定大小的獨立實體(特別是,四個每組五人一組)。
請記住: int a[4][3][5]
和int b[60]
在內存中是相同的,唯一的區別是應用程序/編譯器如何處理它們
{
{1, 2, 3, 4, 5}
{6, 7, 8, 9, 10}
{11, 12, 13, 14, 15}
}
{
{16, 17, 18, 19, 20}
{21, 22, 23, 24, 25}
{26, 27, 28, 29, 30}
}
{
{31, 32, 33, 34, 35}
{36, 37, 38, 39, 40}
{41, 42, 43, 44, 45}
}
{
{46, 47, 48, 49, 50}
{51, 52, 53, 54, 55}
{56, 57, 58, 59, 60}
}
由此可以清楚地看到,每個“分區”只是程序跟蹤的一個數組。
現在,數組在語法上與指針不同。 具體來說,這意味着編譯器/機器將區別對待它們。 這可能看起來很簡單,但看看這個:
int a[3][3];
printf("%p %p", a, a[0]);
上面的例子打印了兩次相同的內存地址,如下所示:
0x7eb5a3b4 0x7eb5a3b4
但是,只有一個可以直接分配給指針:
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
為什么不能將a
分配給指針而a[0]
可以?
簡單地說,這是多維數組的結果,我將解釋原因:
在' a
'的層面上,我們仍然看到我們還有另一個'維度'值得期待。 然而,在 ' a[0]
' 的層次上,我們已經在最高維度,所以就程序而言,我們只是在看一個普通的數組。
你可能會問:
如果數組是多維的,那么為它制作一個指針為什么很重要?
最好這樣想:
來自多維數組的“衰減”不僅僅是一個地址,而是一個帶有分區數據的地址(也就是它仍然理解它的底層數據是由其他數組組成的),它由數組在第一維之外設置的邊界組成。
除非我們指定它,否則這個“分區”邏輯不能存在於指針中:
int a[4][5][95][8];
int (*p)[5][95][8];
p = a; // p = *a[0] // p = a+0
否則,數組的排序屬性的意義就會丟失。
還要注意*p
周圍括號的使用: int (*p)[5][95][8]
- 那是為了指定我們正在創建一個具有這些邊界的指針,而不是具有這些邊界的指針數組: int *p[5][95][8]
讓我們來復習:
簡而言之:多維數組衰減到能夠理解其內容的地址。
在
int *ptr= l_matrix[0];
你可以訪問像
*p
*(p+1)
*(p+2)
畢竟二維數組也存儲為 1-d。
G'day,
聲明
static uint8_t l_matrix[10][20];
已為 10 行 20 個 unit8_t 位置預留了存儲空間,即 200 個 uint8_t 大小的位置,通過計算 20 x 行 + 列來找到每個元素。
所以不
uint8_t (*matrix_ptr)[20] = l_matrix;
給你你需要的東西並指向數組第一行的第零列元素?
編輯:進一步考慮一下,根據定義,數組名稱不是指針嗎? 即數組名是第一個元素位置的同義詞,即l_matrix[0][0]?
Edit2:正如其他人所說,評論空間太小,無法進一步討論。 反正:
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
不為相關陣列提供任何存儲分配。
如上所述,根據標准的定義,聲明:
static uint8_t l_matrix[10][20];
已經預留了 200 個 uint8_t 類型的連續位置。
使用以下形式的語句引用 l_matrix:
(*l_matrix + (20 * rowno) + colno)
將為您提供在 rowno 中找到的第 colno'th 元素的內容。
所有指針操作都會自動考慮所指向對象的大小。 - K&R 第 5.4 節,第 103 頁
如果手頭對象的存儲涉及任何填充或字節對齊移位,情況也是如此。 編譯器會自動調整這些。 根據 C ANSI 標准的定義。
HTH
干杯,
在 C99(由 clang 和 gcc 支持)中,通過引用將多維數組傳遞給函數有一個晦澀的語法:
int l_matrix[10][20];
void test(int matrix_ptr[static 10][20]) {
}
int main(void) {
test(l_matrix);
}
與普通指針不同,這暗示了數組大小,理論上允許編譯器警告傳遞太小的數組並發現明顯的越界訪問。
可悲的是,它沒有修復sizeof()
並且編譯器似乎還沒有使用該信息,因此它仍然是一個好奇心。
您始終可以通過將數組聲明為線性並自行執行 (row,col) 到數組索引計算來避免擺弄編譯器。
static uint8_t l_matrix[200];
void test(int row, int col, uint8_t val)
{
uint8_t* matrix_ptr = l_matrix;
matrix_ptr [col+y*row] = val; // to assign a value
}
這是編譯器無論如何都會做的。
初始化指向多維數組的指針的基本語法是
type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name
調用它的基本語法是
(*pointer_name)[1st index][2nd index][...]
下面是一個例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// The multidimentional array...
char balance[5][100] = {
"Subham",
"Messi"
};
char (*p)[5][100] = &balance; // Pointer initialization...
printf("%s\n",(*p)[0]); // Calling...
printf("%s\n",(*p)[1]); // Calling...
return 0;
}
輸出是:
Subham
Messi
有效...
你可以這樣做:
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
你想要一個指向第一個元素的指針,所以;
static uint8_t l_matrix[10][20];
void test(){
uint8_t *matrix_ptr = l_matrix[0]; //wrong idea
}
如果要使用負索引,還可以添加偏移量:
uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;
如果您的編譯器給出錯誤或警告,您可以使用:
uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.