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


120

정적 2 차원 배열에 대한 포인터가 필요합니다. 어떻게하나요?

static uint8_t l_matrix[10][20];

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

다음과 같은 모든 종류의 오류가 발생합니다.

  • 경고 : 호환되지 않는 포인터 유형에서 할당
  • 아래 첨자 값은 배열도 포인터도 아닙니다.
  • 오류 : 유연한 배열 구성원의 잘못된 사용

읽기 stackoverflow.com/questions/423823/... 당신을 도움이 될 수 있습니다
litb - 요하네스 SCHAUB을

1
@ JohannesSchaub-litb 더 이상 존재하지 않습니다. (? 내가 다시 보는 방법 ... 내가 알고 ... 저 담당자 회원을 볼 수 있지만 내가 어떻게 잊어)
Mateen Ulhaq

1
@muntoo : 여기에 사본이 있습니다 : gist.github.com/sharth/ede13c0502d5dd8d45bd
Bill Lynch

답변:


140

여기에서 배열의 첫 번째 요소에 대한 포인터를 만들고 싶습니다.

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이므로 에서만 작동 합니다. C는 ++의 개념이없는 호환 유형을 , 그리고 그것은 그 코드를 거부합니다 때문에 그리고T[]T[N]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'ness에 대한 경고 포함). ), 배열 유형을 변경할 수없는 경우 이것이 마지막으로 저장되는 옵션입니다. :)


+1-int (*) [] [20] 분석에 대한 좋은 정보-C ++에서는 할 수 없습니다
Faisal Vali

@litb, 미안하지만 솔루션이 어레이에 대한 스토리지 할당을 제공하지 않기 때문에 이것은 잘못된 것입니다.
Rob Wells

2
@Rob, 나는 당신을 잘 이해하지 못합니다. 이 모든 경우에 스토리지는 l_matix 어레이 자체에서 제공됩니다. 그들에 대한 포인터는 (스택, 정적 데이터 세그먼트, ...) 선언 된 모든 위치에서 스토리지를 가져옵니다.
Johannes Schaub-litb

l_matrix의 "&"주소가 왜 필요한지 궁금하십니까?
일렉트로

1
@Sohaib-아니요, 포인터가 하나만 생성됩니다. uint8_t *d[20]uint8_t에 대한 3 개의 포인터 배열을 생성하는 과 혼동했을 수 있지만이 경우에는 작동하지 않습니다.
Palo

28

완전히 이것을 이해, 당신은 해야합니다 다음 개념을 .

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

우선 (그리고 충분히 설교되었습니다) 배열은 포인터가 아닙니다 . 대신 대부분의 용도에서 포인터에 할당 할 수있는 첫 번째 요소의 주소로 '감쇠'합니다.

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]



결론

재검토 해보자:

  • 사용 된 컨텍스트에서 다른 목적이없는 경우 배열은 주소로 붕괴됩니다.
  • 다차원 배열은 배열의 배열 일뿐입니다. 따라서 '부패 된'주소는 "하위 차원이 있습니다."
  • 포인터에 지정 하지 않는 한 차원 데이터는 포인터에 존재할 수 없습니다 .

요약하자면 다차원 배열은 내용을 이해할 수있는 주소로 붕괴됩니다.


1
대답의 첫 번째 부분은 훌륭하지만 두 번째 부분은 그렇지 않습니다. 이것은 정확하지 않습니다 : int *p1 = &(a[0]); // RIGHT !, 실제로는 동일합니다int *p1 = a;
2501

@ 2501 오류를 찾아 주셔서 감사합니다. 수정했습니다. 나는 왜이 "규칙"을 정의하는 예가 그것을 무시했는지 확실히 말할 수 없다. 다시 언급 할 가치가있는 것은 두 엔티티가 포인터로 해석되고 동일한 값을 산출 할 수 있다고해서 동일한 의미를 전달한다는 의미는 아닙니다.
슈퍼 고양이

7

int *ptr= l_matrix[0];

다음과 같이 액세스 할 수 있습니다.

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

2 차원 배열도 모두 1-d로 저장됩니다.


5

G'day,

선언

static uint8_t l_matrix[10][20];

20 개의 unit8_t 위치, 즉 200 개의 uint8_t 크기 위치의 10 개 행에 대한 스토리지를 따로 설정했으며 각 요소는 20 x 행 + 열을 계산하여 찾습니다.

그래서하지 않습니다

uint8_t (*matrix_ptr)[20] = l_matrix;

필요한 것을 제공하고 배열의 첫 번째 행의 열 0 요소를 가리 킵니까?

편집 : 이것에 대해 조금 더 생각하면 배열 이름이 정의에 따라 포인터가 아닙니까? 즉, 배열의 이름은 첫 번째 요소의 위치에 대한 동의어입니다. 즉, 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];

uint8_t 유형의 200 개의 순차적 위치를 따로 설정했습니다.

다음 형식의 문을 사용하여 l_matrix 참조 :

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

rowno 행에있는 colno'th 요소의 내용을 제공합니다.

모든 포인터 조작은 가리키는 개체의 크기를 자동으로 고려합니다. -K & R 섹션 5.4, p.103

패딩이나 바이트 정렬 시프트가 객체 저장에 관여하는 경우에도 마찬가지입니다. 컴파일러는이를 자동으로 조정합니다. C ANSI 표준의 정의에 따라.

HTH

건배,


1
uint8_t (* matrix_ptr)] [20] << 제 브래킷이 정확이 uint8_t (* matrix_ptr) [20]은 제거되어야
아콘

5

C99 (clang 및 gcc에서 지원)에는 참조를 통해 다차원 배열을 함수에 전달하는 모호한 구문이 있습니다.

int l_matrix[10][20];

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

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

일반 포인터와 달리 이것은 배열 크기에 대한 힌트를 제공 하여 이론적으로 컴파일러가 너무 작은 배열을 전달하는 것에 대해 경고하고 명백한 범위를 벗어난 액세스를 발견 할 수 있도록합니다.

안타깝게도 수정되지 않고 sizeof()컴파일러가 아직 해당 정보를 사용하지 않는 것 같으므로 여전히 호기심이 많습니다.


1
이 대답은 오해의 소지가 있습니다. 인수를 고정 크기 배열로 만들지는 않지만 여전히 포인터입니다. 최소 10 개의 요소가 존재 static 10한다는 일종의 보증입니다 . 다시 말해서 크기가 고정되지 않았 음을 의미합니다.
bluss

1
@bluss 문제는 포인터 (지적에 응답하는 방법을 내가 볼 수 없습니다, 포인터에 대해이었다 참조 ) 오해의 소지가있다. 이러한 경계를 넘어서는 요소에 대한 액세스가 정의되지 않았기 때문에 배열은 함수의 관점에서 고정 된 크기입니다.
Kornel 2016

10을 초과하는 액세스가 정의되지 않았다고 생각하지 않으며이를 나타내는 어떤 것도 볼 수 없습니다.
bluss

이 대답은 키워드 static가 없으면 배열이 참조로 전달되지 않으며 사실이 아닙니다. 어쨌든 배열은 참조로 전달됩니다. 원래 질문은 동일한 함수 / 네임 스페이스 내에서 보조 포인터를 사용하여 2D 배열의 요소에 액세스하는 다른 사용 사례에 대해 질문했습니다.
Palo

4

배열을 선형으로 선언하고 (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

}

이것은 컴파일러가 어쨌든 한 일입니다.


1
어쨌든 이것이 C 컴파일러가하는 일입니다. C에는 실제로 "배열"이라는 개념이 없습니다. [] 표기법은 포인터 산술을위한 구문 적 설탕 일뿐입니다
Ken Keenan

7
이 솔루션은 올바른 방법을 찾지 못하는 단점이 있습니다.
Craig McQueen

2

다차원 배열을 가리키는 포인터를 초기화하는 기본 구문은 다음과 같습니다.

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

작동했습니다 ...


1

다음과 같이 할 수 있습니다.

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

1
이것은 10 * 20 바이트의 램을 차지하지 않습니까? (마이크로 컨트롤러에 있음)
Dill

4 바이트 또는 상자에서 포인터가 큰 크기를 차지합니다. 그러나 이것이 있다면 matrix_ptr [0] [x] [y] 또는 (* matrix_ptr) [x] [y]로 인덱싱해야합니다. 이것은 "2 차원 배열에 대한 포인터"의 직접적인 단어 단위 해석입니다. : p
Johannes Schaub-litb

감사합니다 litb, 액세스 방법을 언급하는 것을 잊었습니다. 당신 : 당신의 대답과 함께 훌륭한 일을 한 이후로 내 대답을 편집 할 점은 없습니다
닉 Dandoulakis

그렇다면 이것은 10 * 20 바이트의 RAM을 차지합니까?
Danijel

@Danijel, 2 차원 배열에 대한 포인터이기 때문에 4 바이트 또는 상자에있는 포인터 크기 (예 : 16 비트, 32 비트, 64 비트 등) 만
차지합니다

1

첫 번째 요소에 대한 포인터를 원하므로;

static uint8_t l_matrix[10][20];

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

0

음수 인덱스를 사용하려는 경우 오프셋을 추가 할 수도 있습니다.

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;

안녕하세요. 이 질문에는로 태그가 지정되어 c있으므로 답변은 동일한 언어로 작성되어야합니다. 태그를 확인하십시오.
2501
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.