developer tip

2 차원 배열에 대한 포인터 만들기

optionbox 2020. 8. 4. 07:31
반응형

2 차원 배열에 대한 포인터 만들기


정적 2 차원 배열에 대한 포인터가 필요합니다. 이것은 어떻게 이루어 집니까?

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]다른 유형입니다.


다음 대안은 전혀 작동하지 않습니다. 배열의 요소 유형은 1 차원 배열로 볼 때 그렇지 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];

이제 공식적으로는 2 차원 배열의 첫 번째 요소의 요소에만 액세스 할 수 있습니다. 즉, 다음 상태 유지

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에 대한 경고와 함께) ), 배열 유형을 변경할 수없는 경우 마지막으로 저장하는 옵션입니다. :)


이를 완전히 이해 하려면 다음 개념을 파악 해야합니다 .

배열은 포인터가 아닙니다!

우선 (그리고 그것은 충분히 설교되었습니다), 배열은 포인터가 아닙니다 . 대신, 대부분의 경우, 첫 번째 요소의 주소로 '부패'되며 포인터에 지정할 수 있습니다.

int a[] = {1, 2, 3};

int *p = a; // p now points to a[0]

배열의 내용을 모두 복사하지 않고 액세스 할 수 있도록이 방식으로 작동한다고 가정합니다. 이는 배열 유형의 동작 일 뿐이며 동일한 유형임을 의미하지는 않습니다.



다차원 배열

다차원 배열은 컴파일러 / 컴퓨터가 이해하고 작동 할 수있는 방식으로 메모리를 '파티션'하는 방법입니다.

예를 들어, int a[4][3][5]= 4 * 3 * 5 (60) 개의 '청크'정수 크기 메모리를 포함하는 배열입니다.

The advantage over using int a[4][3][5] vs plain int b[60] is that they're now 'partitioned' (Easier to work with their 'chunks', if needed), and the program can now perform bound checking.

In fact, int a[4][3][5] is stored exactly like int b[60] in memory - The only difference is that the program now manages it as if they're separate entities of certain sizes (Specifically, four groups of three groups of five).

Keep in mind: Both int a[4][3][5] and int b[60] are the same in memory, and the only difference is how they're handled by the application/compiler

{
  {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}
}

From this, you can clearly see that each "partition" is just an array that the program keeps track of.



Syntax

Now, arrays are syntactically different from pointers. Specifically, this means the compiler/machine will treat them differently. This may seem like a no brainer, but take a look at this:

int a[3][3];

printf("%p %p", a, a[0]);

The above example prints the same memory address twice, like this:

0x7eb5a3b4 0x7eb5a3b4

However, only one can be assigned to a pointer so directly:

int *p1 = a[0]; // RIGHT !

int *p2 = a; // WRONG !

Why can't a be assigned to a pointer but a[0] can?

This, simply, is a consequence of multidimensional arrays, and I'll explain why:

At the level of 'a', we still see that we have another 'dimension' to look forward to. At the level of 'a[0]', however, we're already in the top dimension, so as far as the program is concerned we're just looking at a normal array.

You may be asking:

Why does it matter if the array is multidimensional in regards to making a pointer for it?

It's best to think this way:

A 'decay' from a multidimensional array is not just an address, but an address with partition data (AKA it still understands that its underlying data is made of other arrays), which consists of boundaries set by the array beyond the first dimension.

This 'partition' logic cannot exist within a pointer unless we specify it:

int a[4][5][95][8];

int (*p)[5][95][8];

p = a; // p = *a[0] // p = a+0

Otherwise, the meaning of the array's sorting properties are lost.

Also note the use of parenthesis around *p: int (*p)[5][95][8] - That's to specify that we're making a pointer with these bounds, not an array of pointers with these bounds: int *p[5][95][8]



Conclusion

Let's review:

  • Arrays decay to addresses if they have no other purpose in the used context
  • Multidimensional arrays are just arrays of arrays - Hence, the 'decayed' address will carry the burden of "I have sub dimensions"
  • Dimension data cannot exist in a pointer unless you give it to it.

In brief: multidimensional arrays decay to addresses that carry the ability to understand their contents.


In

int *ptr= l_matrix[0];

you can access like

*p
*(p+1)
*(p+2)

after all 2 dimensional arrays are also stored as 1-d.


G'day,

The declaration

static uint8_t l_matrix[10][20];

has set aside storage for 10 rows of 20 unit8_t locations, i.e. 200 uint8_t sized locations, with each element being found by calculating 20 x row + column.

So doesn't

uint8_t (*matrix_ptr)[20] = l_matrix;

give you what you need and point to the column zero element of the first row of the array?

Edit: Thinking about this a bit further, isn't an array name, by definition, a pointer? That is, the name of an array is a synonym for the location of the first element, i.e. l_matrix[0][0]?

Edit2: As mentioned by others, the comment space is a bit too small for further discussion. Anyway:

typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;

does not provide any allocation of storage for the array in question.

As mentioned above, and as defined by the standard, the statement:

static uint8_t l_matrix[10][20];

has set aside 200 sequential locations of type uint8_t.

Referring to l_matrix using statements of the form:

(*l_matrix + (20 * rowno) + colno)

will give you the contents of the colno'th element found in row rowno.

All pointer manipulations automatically take into account the size of the object pointed to. - K&R Section 5.4, p.103

This is also the case if any padding or byte alignment shifting is involved in the storage of the object at hand. The compiler will automatically adjust for these. By definition of the C ANSI standard.

HTH

cheers,


In C99 (supported by clang and gcc) there's an obscure syntax for passing multi-dimensional arrays to functions by reference:

int l_matrix[10][20];

void test(int matrix_ptr[static 10][20]) {
}

int main(void) {
    test(l_matrix);
}

Unlike a plain pointer, this hints about array size, theoretically allowing compiler to warn about passing too-small array and spot obvious out of bounds access.

Sadly, it doesn't fix sizeof() and compilers don't seem to use that information yet, so it remains a curiosity.


You can always avoid fiddling around with the compiler by declaring the array as linear and doing the (row,col) to array index calculation by yourself.

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

}

this is what the compiler would have done anyway.


You can do it like this:

uint8_t (*matrix_ptr)[10][20] = &l_matrix;

You want a pointer to the first element, so;

static uint8_t l_matrix[10][20];

void test(){
   uint8_t *matrix_ptr = l_matrix[0]; //wrong idea 
}

The basic syntax of initializing pointer that points to multidimentional array is

type (*pointer)[ 1st dimension size ][2nd dimension size ][..]=&array_name

The the basic syntax for calling it is

(*pointer_name)[ 1st index][2nd index][...]

Here is a example

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
   char balance[5][100] = { {"Subham"},{"Messi"} };//The multidimentional array...

   char (*p)[5][100]=&balance;//Pointer initialization...

   printf("%s\n",(*p)[0]);//Calling...
   printf("%s\n",(*p)[1]);//Calling...

  return 0;
}

Output is :

Subham
Messi

That did it...


You could also add an offset if you want to use negative indexes:

uint8_t l_matrix[10][20];
uint8_t (*matrix_ptr)[20] = l_matrix+5;
matrix_ptr[-4][1]=7;

If your compiler gives an error or warning you could use:

uint8_t (*matrix_ptr)[20] = (uint8_t (*)[20]) l_matrix;

참고URL : https://stackoverflow.com/questions/1052818/create-a-pointer-to-two-dimensional-array

반응형