簡體   English   中英

創建一個指向二維數組的指針

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM