고정된 크기의 배열에 대한 포인터 배열
고정된 크기의 배열 두 개를 포인터 배열에 할당하려고 했지만 컴파일러가 경고했고 그 이유를 이해할 수 없습니다.
int A[5][5];
int B[5][5];
int*** C = {&A, &B};
이 코드는 다음 경고와 함께 컴파일됩니다.
경고: 호환되지 않는 포인터 유형에서 초기화 [기본적으로 활성화]
코드를 실행하면 세그멘테이션 오류가 발생합니다.하지만 동적으로 할당하면A
그리고.B
, 그것은 잘 작동합니다.이게 왜죠?
만약 당신이 선언을 원한다면,C
그것은 기존의 선언에 들어맞습니다.A
그리고.B
이렇게 해야 합니다.
int A[5][5];
int B[5][5];
int (*C[])[5][5] = {&A, &B};
.C
"C
는 배열에 대한 포인터의 배열입니다."로 읽힙니다.전체 배열을 할당할 수 없으므로 배열에 포인터를 할당해야 합니다.
으로,(*C[0])[1][2]
과은에고다리다yene와 같은 메모리 위치에 하고 있습니다.A[1][2]
.
만약 당신이 더 깨끗한 구문을 원한다면 다음과 같이.C[0][1][2]
를 동적으로 즉, 을 하고 를 으로 해야 이 해야 으로 를 하고 .
int **A;
int **B;
// allocate memory for A and each A[i]
// allocate memory for B and each B[i]
int **C[] = {A, B};
모스크바에서 Vlad가 제안한 구문을 사용하여 이 작업을 수행할 수도 있습니다.
int A[5][5];
int B[5][5];
int (*C[])[5] = {A, B};
ㅇㅇ의 C
"C
는 배열에 대한 포인터의 배열입니다."로 읽힙니다.이 경우, 각 배열 요소는C
는 입니다.int (*)[5]
, int [5][5]
이런 유형으로 붕괴될 수 있습니다.
이제, 당신은 사용할 수 있습니다.C[0][1][2]
다음과 같은 메모리 위치에 액세스합니다.A[1][2]
.
이 논리를 보다 높은 차원으로 확장할 수도 있습니다.
int A[5][5][3];
int B[5][5][3];
int (*C[])[5][3] = {A, B};
불행하게도 잘못된 것들을 가르쳐주는 형편없는 책들/튜터링들/선생님들이 많이 있습니다.
포인터 대 포인터는 잊어버려요. 배열과는 아무 상관이 없어요.마침표.
또한 경험칙으로서: 2단계 이상의 간접 사용을 발견할 때마다 프로그램 설계에 근본적인 결함이 있으므로 처음부터 다시 만들어야 합니다.
이 작업을 올바르게 수행하려면 다음과 같이 수행해야 합니다.
int [5][5]
배열 포인터라고 하며 다음과 같이 선언됩니다.int(*)[5][5]
예:
int A[5][5];
int (*ptr)[5][5] = &A;
배열 포인터의 배열을 원한다면, 그것은 타입일 것입니다.int(*[])[5][5]
예:
int A[5][5];
int B[5][5];
int (*arr[2])[5][5] = {&A, &B};
당신이 알 수 있듯이 이 코드는 불필요하게 복잡해 보입니다. 그리고 그렇습니다.ㅇㅇㅇㅇㅇㅇㅇㅇ을 해야 하므로 입니다. 입력해야 하기 때문입니다.(*arr[x])[y][z]
" 번호 x를 그 배열에서 ] 을 선택합니다.".. 의미: "배열 포인터 배열에서 배열 포인터 번호 x를 선택하고, 2D 배열인 2D 배열을 가리키는 내용을 선택한 후 해당 배열에서 인덱스 [y][z] 항목을 선택합니다."
그런 구조물을 발명하는 것은 미친 짓일 뿐이지 제가 추천하고 싶은 것은 없습니다.일반 배열 포인터로 작업하면 코드를 단순화할 수 있다고 생각합니다.
int A[5][5];
int B[5][5];
int (*arr[2])[5][5] = {&A, &B};
int (*ptr)[5][5] = arr[0];
...
ptr[x][y][z] = 0;
하지만 이것은 여전히 다소 복잡한 코드입니다.완전히 다른 디자인을 고려해보세요!예:
- 3D 배열을 만듭니다.
- 2D 배열이 포함된 구조물을 만든 다음 이러한 구조물의 배열을 만듭니다.
선이 많이 잘못되어 있습니다.
int*** C = {&A, &B};
당신은 하나의 포인터를C
안예요. 그건 통하지 않을 겁니다.당신이 해야 할 일은 신고하는 것입니다.C
그 배열들에 대한 포인터들의 배열로.
모두의 종류&A
그리고.&B
int (*)[5][5]
5의 5 5 의 5 의 5 의 5 의 5 의 5 의 5 의 5 의 5 3 의 5 5 의 배열"int
""의 5-element of 5-element array of "; , C의 "5-element 의 5-element 에 야 은 야 의 int
", 아니면
int (*C[2])[5][5] = { &A, &B };
라고 쓰여져 있습니다.
C -- C is a
C[2] -- 2-element array of
*C[2] -- pointers to
(*C[2])[5] -- 5-element arrays of
(*C[2])[5][5] -- 5-element arrays of
int (*C[2])[5][5] -- int
못 중에면씬더다의다더fr 중 의 요소에 더 .A
아니면B
를 통하여C
:
int x = (*C[0])[i][j]; // x = A[i][j]
int y = (*C[1])[i][j]; // y = B[i][j]
으로 는 으로 해야 을 해야 는 을 으로 C[i]
을 만들 수 에, 첨자 연산자된로에고로자자e로자자e된로고기에eeno[]
다가다선e가보다 높은 우선순위를 .*
오퍼레이터, 우리는 그룹을 짜야 합니다.*C[0]
우리는 이것을 조금만 치울 수 있습니다.를하고는하고는rte의 피연산자인 경우를 제외하고,sizeof
또는 단항의&
연산자(또는 선언에서 다른 배열을 초기화하는 데 사용되는 문자열 리터럴), 유형의 표현 "N
-πT
는 "에서 "과 의 으로 으로 의 과 "로 됩니다.T
", 식의 값은 배열의 첫 번째 요소의 주소가 될 것입니다.
이라는 이.A
그리고.B
활자를 가지다int [5][5]
, "5-π π π π"int
위의 모두 "의로의" 에 ". 의 에 의 "5-π 의 " " " 에 " " .int
", 아니면int (*)[5]
을 . 가 을 한다면으로 초기화하면,A
그리고.B
에 대신에&A
그리고.&B
, 는 에 의 이 합니다 합니다 이 의 에 의 5- 배열에 대한 포인터 배열이 합니다.int
, 아니면
int (*C[2])[5] = { A, B };
좋아요, 그래도 꽤나 눈에 거슬리는군요. 하지만 유형의 결함 없이도 이 정도면 깨끗할 겁니다.
그럼 우리는 어떻게 그들의 요소들에 접근할 것인가요.A
그리고.B
를 통하여C
?
작업 은 은 a[i]
다음과 같이 정의됩니다.*(a + i)
주소가 때; , 가 a
, i
해당 주소에서 요소(바이트가 아닌)1를 입력하고 결과를 역참조합니다.이 말은
*a == *(a + 0) == a[0]
따라서,
*C[i] == *(C[i] + 0) == C[i][0]
이 모든 것을 종합하면:
C[0] == A // int [5][5], decays to int (*)[5]
C[1] == B // int [5][5], decays to int (*)[5]
*C[0] == C[0][0] == A[0] // int [5], decays to int *
*C[1] == C[1][0] == B[0] // int [5], decays to int *
C[0][i] == A[i] // int [5], decays to int *
C[1][i] == B[i] // int [5], decays to int *
C[0][i][j] == A[i][j] // int
C[1][i][j] == B[i][j] // int
우리는 색인할 수 있습니다.C
마치 그것이 3D 배열인 것처럼.int
보다 보다 조금 더 깨끗합니다.(*C[i)[j][k]
.
이 표는 유용할 수도 있습니다.
Expression Type "Decays" to Value
---------- ---- ----------- -----
A int [5][5] int (*)[5] Address of A[0]
&A int (*)[5][5] Address of A
*A int [5] int * Value of A[0] (address of A[0][0])
A[i] int [5] int * Value of A[i] (address of A[i][0])
&A[i] int (*)[5] Address of A[i]
*A[i] int Value of A[i][0]
A[i][j] int Value of A[i][j]
:A
,&A
,A[0]
,&A[0]
,그리고.&A[0][0]
모두 같은 값을 산출하지만(배열의 주소와 배열의 첫번째 요소의 주소는 항상 동일함), 위의 표와 같이 유형이 다릅니다.
- 은의를다를다f의r;te은-eefedsrcor
p
를합니다의 되어 있습니다.int
어,럼p+1
는 2~4바이트 떨어진 다음 개체의 주소를 나타냅니다.
C 초보자들 사이에서 흔한 오해는 포인터와 배열이 동일하다고 가정한다는 것입니다.그건 완전히 잘못된 것입니다.
초보자들은 코드가 다음과 같은 것을 볼 때 혼란스러움을 느낍니다.
int a1[] = {1,2,3,4,5};
int *p1 = a1; // Beginners intuition: If 'p1' is a pointer and 'a1' can be assigned
// to it then arrays are pointers and pointers are arrays.
p1[1] = 0; // Oh! I was right
a1[3] = 0; // Bruce Wayne is the Batman! Yeah.
이제 초보자들은 배열이 포인터이고 포인터가 배열이라는 것을 확인하여 다음과 같은 실험을 수행합니다.
int a2[][5] = {{0}};
int **p2 = a2;
그러면 호환되지 않는 포인터 할당에 대한 경고가 나타납니다. 그러면 사람들은 "오 마이 갓!왜 이 배열이 하비 덴트(Harvey Dent)가 되었습니까?"
일부는 한발 앞서 가기도 합니다.
int a3[][5][10] = {{{0}}};
int ***p3 = a3; // "?"
그리고 리들러는 배열-포인터 동등성의 악몽에 빠집니다.
배열은 포인터가 아니며 그 반대도 마찬가지입니다.배열은 데이터 유형이고 포인터는 배열 유형이 아닌 다른 데이터 유형입니다.이는 몇 년 전 C-FAQ에서 다루어진 내용입니다.
배열과 포인터가 "동등하다"고 말하는 것은 그들이 동일하거나 심지어 상호 교환 가능하다는 것을 의미하지 않습니다.즉, 배열에 액세스하거나 배열을 시뮬레이션하기 위해 포인터를 편리하게 사용할 수 있도록 배열 및 포인터 산술이 정의된다는 것입니다.다시 말해, Wayne Throop이 말했듯이, "[C]에서 동일한 포인터 산술 및 배열 색인은 포인터와 배열이 다릅니다.")
이제 이러한 혼란을 피하기 위한 배열에 대한 몇 가지 중요한 규칙을 항상 기억해 두십시오.
- 배열은 포인터가 아닙니다.포인터는 배열이 아닙니다.
- 의 로 됩니다 됩니다 는 가 과 은 됩니다 는 과 에 가 될 은
sizeof
그리고.&
교환입니다. - 포인터 산술과 배열 색인이 동일합니다.
- 포인터와 배열이 다릅니다.
- 제가 "포인트는 배열이 아니며 그 반대입니다."라고 말했었나요?
이제 당신은 규칙을 가지고 있고, 당신은 다음과 같이 결론지을 수 있습니다.
int a1[] = {1,2,3,4,5};
int *p1 = a1;
a1
는고에다다e고s는nndyrn .int *p1 = a1;
첫번째 요소를 가리키는 포인터로 변환했습니다.의는다다ef 입니다.int
첫 면째에한가다일다일eenf가째면o에한rdsint *
됩니다.p1
.
인
int a2[][5] = {{0}};
int **p2 = a2;
a2
는고인에 있습니다.int **p2 = a2;
첫번째 요소를 가리키기 위해 붕괴됩니다.의는다다ef 입니다.int[5]
배열의 이므로 첫 번째 는 (2D은 1D로의다첫과는한에째다과는첫)((ee한f째에2oasf로ddat2nro의y,1 )int(*)[5]
배열에 ) (배열에 적용)과지됨에rint **
겁니다.그럴 것 같네요.
int (*p2)[5] = a2;
마찬가지로
int a3[][5][10] = {{{0}}};
int ***p3 = a3;
a3
는 입니다.int [5][10]
그 첫고그째에한다일는다일edef는고o그r째한에dint (*)[5][10]
,그렇지만p3
int ***
type,호환되도록 는 ①, ②, ③, ④, ⑤,
int (*p3)[5][10] = a3;
이제 여러분의 토막글로 이동합니다.
int A[5][5];
int B[5][5];
int*** C = {&A, &B};
&A
그리고.&B
int(*)[5][5]
.C
는 입니다.int***
이 이 . .C
두의를두다다두e두oA
그리고.B
은 해야 해야 은 을 선언해야 합니다.C
2파운드 로.int(*)[5][5]
활자 요소이 작업은 다음과 같이 수행해야 합니다.
int (*C[2])[5][5] = {&A, &B};
하지만 A와 B를 동적으로 할당하면 잘 됩니다.왜 이러한가?
그런 경우에는 당신이 그들을A
그리고.B
~하듯이int **
둘 배열이 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .C
는 입니다.int ***
, 그래서 그것은 주소를 가질 수 있습니다.int**
데이터를 입력합니다., 우은이은net이esnernint*** C = {&A, &B};
다
int*** C = &A;
의 int*** C = {&A, &B};
되지 않거나 정의될 입니다. , 의 되지 됩니다 이 은 됩니다 이 의 은 되지
C11: 5.1.1.3(P1):
동작 또한 명시적으로 정의되지 않았거나 구현이 정의된 것으로 명시적으로 지정된 경우에도, 사전 처리 번역 유닛 또는 번역 유닛이 어떤 구문 규칙 또는 제약 조건의 위반을 포함하는 경우, 적합한 구현은 (구현이 정의된 방식으로 식별된) 적어도 하나의 진단 메시지를 생성해야 합니다.
배열은 C의 다차원 포인터와 같은 것이 아닙니다.배열의 이름은 색인을 작성하는 방법에 관계없이 대부분의 경우 배열이 포함된 버퍼의 주소로 해석됩니다.A
로다됩니다 로 선언됩니다.int A[5][5]
,그리고나서A
번째 즉,의를할다즉로은n,로l즉dytsfeye,sntts통째다eti의int *
(파일))int *[5]
), a가 아닙니다int **
조금도. 주소 계산에는 가지 요소가 필요합니다 두 필요합니다 두 가지 요소가 필요합니다A[x][y] = A + x + 5 * y
. 이것은 하기에 편리합니다.A[x + 5 * y]
, 그것은 홍보하지 않습니다.A
다차원 버퍼에 연결합니다.
C에서 다차원 포인터를 원한다면 그렇게 해도 됩니다.구문은 매우 비슷하겠지만 설정이 조금 더 필요합니다.몇 가지 일반적인 방법이 있습니다.
단일 버퍼 사용 시:
int **A = malloc(5 * sizeof(int *));
A[0] = malloc(5 * 5 * sizeof(int));
int i;
for(i = 1; i < 5; i++) {
A[i] = A[0] + 5 * i;
}
각 행에 대해 별도의 버퍼가 있는 경우:
int **A = malloc(5 * sizeof(int *));
int i;
for(i = 0; i < 5; i++) {
A[i] = malloc(5 * sizeof(int));
}
배열과 포인터의 동등성 때문에 혼란스러워 하고 있습니다.
과 을 할 할과 같은 배열을 할 때A[5][5]
는 연속적으로 의 객체에 를 할당합니다 즉, 을 는 했으므로 으로 C 의 25 합니다 를 에 합니다 를 의 에 으로 는 다음과즉,는과이다다이slt,즉:dee과는
A00, A01, ... A04, A10, A11, ..., A14, A20, ..., A24, ...
인 대상인 인 는,A
는 이 포인터입니다 , 의 을 입니다 는 입니다 을 는 의 그것은 타입입니다.int *
,것은 아니다.int **
.
배열에 대한 포인터 벡터를 원하는 경우 변수를 다음과 같이 선언합니다.
int *A[5], *B[5];
그러면 다음과 같은 결과가 나옵니다.
A0, A1, A2, A3, A4
int*
할, 이 은 은 이 malloc()
아니면 뭐든.
아니면, 당신은 다음과 같이 선언할 수 있습니다.C
~하듯이int **C
.
배열과 포인터는 밀접하게 연관되어 있지만 전혀 같은 것이 아닙니다.대부분의 상황에서 배열 값은 포인터로 붕괴되고 배열 표기법은 함수 프로토타입에서 실제로 포인터인 매개 변수를 선언하는 데 사용될 수 있기 때문에 사람들은 때때로 이에 대해 혼란스러워 합니다.또한 배열 인덱싱 표기법이라고 생각하는 많은 사람들이 실제로 포인터 산술과 참조 해제의 조합을 수행하므로 포인터 값과 배열 값에 대해 동일하게 잘 작동합니다(배열 값이 포인터로 붕괴되기 때문입니다).
선언문을 보면
int A[5][5];
변수 A
는 5 5개 합니다.int
됩니다. 은 의 로 됩니다 됩니다 로 의 은 .int (*)[5]
5 --즉, 5을을 가리키는 입니다.int
전체 배열에 는 . 에 는 과 과 는 .int (*)[5][5]
의로의r)칭로f을5sf5(yo5r()int
는 는 전혀 다릅니다.int ***
pointer pointer to ( to) (pointer to ).int
). 와 같은 이와 같은 다차원 배열에 포인터를 선언하려면 다음과 같이 수행할 수 있습니다.
int A[5][5];
int B[5][5];
int (*C)[5][5] = &A;
이러한 포인터의 배열을 선언하려면 다음을 수행할 수 있습니다.
int (*D[2])[5][5] = { &A, &B };
추가됨:
이러한 구분은 다양한 방식으로 작용하며, 배열 값이 포인터로 붕괴되지 않는 맥락과 이와 관련된 맥락이 더 중요합니다.이들 중 가장 중요한 것 중 하나는 값이 피연산자일 때입니다.sizeof
. 위와에 의하여 1(로 됩니다.위의 선언을 고려할 때, 다음의 관계식들은 모두 1(true)로 평가됩니다.
sizeof(A) == 5 * 5 * sizeof(int)
sizeof(A[0]) == 5 * sizeof(int)
sizeof(A[0][4]) == sizeof(int)
sizeof(D[1]) == sizeof(C)
sizeof(*C) == sizeof(A)
또한, 이러한 관계식은 1로 평가될 가능성이 있지만 보장되지는 않습니다.
sizeof(C) == sizeof(void *)
sizeof(D) == 2 * sizeof(void *)
이는 배열 인덱싱이 작동하는 방식의 기본이며, 메모리를 할당할 때 이해하는 데 필수적입니다.
세 번째 배열을 다음과 같이 선언해야 합니다.
int A[5][5];
int B[5][5];
int ( *C[] )[N][N] = { &A, &B };
그것은 2차원 배열에 대한 포인터들의 배열입니다.
예를들면
#include <stdio.h>
#define N 5
void output( int ( *a )[N][N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", ( *a )[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 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 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N][N];
T C[] = { &A, &B };
*/
int ( *C[] )[N][N] = { &A, &B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
프로그램 출력은
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
25 24 23 22 21
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1
라든가
int A[5][5];
int B[5][5];
int ( *C[] )[N] = { A, B };
그것은 2차원 배열의 첫 번째 요소에 대한 포인터의 배열로서.
예를들면
#include <stdio.h>
#define N 5
void output( int ( *a )[N] )
{
for ( size_t i = 0; i < N; i++ )
{
for ( size_t j = 0; j < N; j++ ) printf( "%2d ", a[i][j] );
printf( "\n" );
}
}
int main( void )
{
int A[N][N] =
{
{ 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 }
};
int B[N][N] =
{
{ 25, 24, 23, 22, 21 },
{ 20, 19, 18, 17, 16 },
{ 15, 14, 13, 12, 11 },
{ 10, 9, 8, 7, 6 },
{ 5, 4, 3, 2, 1 }
};
/*
typedef int ( *T )[N];
T C[] = { A, B };
*/
int ( *C[] )[N] = { A, B };
output( C[0] );
printf( "\n" );
output( C[1] );
printf( "\n" );
}
프로그램 출력은 위와 같습니다.
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
25 24 23 22 21
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1
세 번째 배열을 어떻게 사용할지에 따라 달라질 수 있습니다.
(설명에 나와 있는 시연 프로그램에 나와 있는) type def를 사용하면 배열의 정의가 단순해집니다.
이 선언에 관해서는
int*** C = {&A, &B};
에는 과 의 가 됩니다 됩니다 가 의 과 에는 int ***
른진자이는안다라을에다ttftt는le에ssnea른tar이을 .int ( * )[N][N]
.
그래서 컴파일러는 메시지를 발행합니다.
저는 사용하는 것을 아주 좋아합니다.typedef
:
#define SIZE 5
typedef int OneD[SIZE]; // OneD is a one-dimensional array of ints
typedef OneD TwoD[SIZE]; // TwoD is a one-dimensional array of OneD's
// So it's a two-dimensional array of ints!
TwoD a;
TwoD b;
TwoD *c[] = { &a, &b, 0 }; // c is a one-dimensional array of pointers to TwoD's
// That does NOT make it a three-dimensional array!
int main() {
for (int i = 0; c[i] != 0; ++i) { // Test contents of c to not go too far!
for (int j = 0; j < SIZE; ++j) {
for (int k = 0; k < SIZE; ++k) {
// c[i][j][k] = 0; // Error! This proves it's not a 3D array!
(*c[i])[j][k] = 0; // You need to dereference the entry in c first
} // for
} // for
} // for
return 0;
} // main()
언급URL : https://stackoverflow.com/questions/37348741/array-of-pointers-to-an-array-of-fixed-size
'programing' 카테고리의 다른 글
부트스트랩 날짜 선택기로 선택한 날짜 변경 탐지 (0) | 2023.09.27 |
---|---|
Spring을 통해 주입된 지도 구조 추상 맵퍼에 대한 Junit test 작성 방법 (0) | 2023.09.27 |
레일 4 앱에서 CORS를 활성화하는 방법 (0) | 2023.09.07 |
오라클용 Toad를 사용하여 저장된 프로시저, 뷰, 함수, 트리거 검색 (0) | 2023.09.07 |
데이터베이스에서 검색한 데이터를 인쇄하는 방법 (0) | 2023.09.07 |