다음 선언의 차이점은 무엇입니까?
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
더 복잡한 선언을 이해하기위한 일반적인 규칙은 무엇입니까?
const
및 volatile
한정자가 누락되었습니다.
다음 선언의 차이점은 무엇입니까?
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
더 복잡한 선언을 이해하기위한 일반적인 규칙은 무엇입니까?
const
및 volatile
한정자가 누락되었습니다.
답변:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
세 번째는 첫 번째와 같습니다.
일반적인 규칙은 연산자 우선 순위 입니다. 함수 포인터가 그림에 들어 오면 훨씬 더 복잡해질 수 있습니다.
( ) [ ]
왼쪽에서 오른쪽으로 연결 되며 각 요소가 int를 가리키는 크기 8의 배열과 정수를 보유한 크기 8의 배열에 대한 포인터로 *
읽히는 것보다 우선 순위가 높습니다.int* arr[8]
int (*arr)[8]
K & R에서 제안한대로 cdecl 프로그램을 사용하십시오 .
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
다른 방식으로도 작동합니다.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
공식 명칭이 있는지는 모르겠지만 Right-Left Thingy (TM)라고합니다.
변수에서 시작한 다음 오른쪽, 왼쪽 및 오른쪽으로 이동하십시오.
int* arr1[8];
arr1
정수에 대한 8 개의 포인터 배열입니다.
int (*arr2)[8];
arr2
8 정수 배열의 포인터 (오른쪽 왼쪽 괄호 블록)입니다.
int *(arr3[8]);
arr3
정수에 대한 8 개의 포인터 배열입니다.
복잡한 선언에 도움이됩니다.
( ) [ ]
오른쪽에서 왼쪽으로의 오른쪽에서 왼쪽으로 의 lef를 잊지 마십시오* &
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
[5]
)는 내부 치수를 나타냅니다. 이는 (*a[8])
첫 번째 차원이므로 배열의 외부 표현입니다. 각 요소가 a
가리키는 크기는 5의 다른 정수 배열입니다.
마지막 두 가지에 대한 대답은 C의 황금률에서 공제 될 수도 있습니다.
사용에 따른 선언.
int (*arr2)[8];
역 참조하면 어떻게됩니까 arr2
? 8 개의 정수 배열을 얻습니다.
int *(arr3[8]);
요소를 가져 오면 어떻게됩니까 arr3
? 정수에 대한 포인터를 얻습니다.
함수에 대한 포인터를 다룰 때도 도움이됩니다. sigjuice의 예를 들어 보려면 :
float *(*x)(void )
역 참조하면 어떻게됩니까 x
? 인수없이 호출 할 수있는 함수를 얻습니다. 전화하면 어떻게 되나요? 에 대한 포인터를 반환합니다 float
.
그러나 연산자 우선 순위는 항상 까다 롭습니다. 그러나 선언에 따라 사용되므로 괄호를 사용하는 것도 실제로 혼란 스러울 수 있습니다. 적어도 나에게는 직관적 arr2
으로 int에 대한 8 개의 포인터 배열처럼 보이지만 실제로는 다른 방법입니다. 익숙해 지기만하면됩니다. 나에게 묻는다면 항상 이러한 선언에 의견을 추가 할 수있는 이유 :)
편집 : 예
그런데 정적 행렬이 있고 포인터 산술을 사용하여 행 포인터가 범위를 벗어 났는지 확인하는 함수에서 방금 넘어졌습니다. 예:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
산출:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
border 값은 절대 변경되지 않으므로 컴파일러는이를 최적화 할 수 있습니다. 이것은 처음에 사용하려는 것과 다릅니다.const int (*border)[3]
: 변수가 존재하는 한 값을 변경하지 않는 3 개의 정수 배열에 대한 포인터로 border를 선언합니다. 그러나 해당 포인터는 언제든지 이러한 다른 배열을 가리킬 수 있습니다. 우리는 인수에 대해 이런 종류의 동작을 원합니다 (이 함수는 그 정수를 변경하지 않기 때문에). 사용에 따른 선언.
(ps :이 샘플을 자유롭게 개선하십시오!)
엄지 손가락의 규칙을 마우스 오른쪽 단항 연산자 (등 []
, ()
왼쪽 사람 이상 등) 테이크 기본 설정. 따라서 int *(*ptr)()[];
포인터 배열을 int로 반환하는 함수를 가리키는 포인터가됩니다 (괄호를 벗어나면 가능한 한 빨리 올바른 연산자를 가져 오십시오)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];
와 GCC 8에서$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();
은 나중에 p()[3]
(또는 (*p)()[3]
) 와 같은 표현 을 사용할 수 있습니다.
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }
이처럼 호출 foo(arr)[4];
이 포함되어야한다 arr[2][4]
, 권리?
나는 우리가 간단한 규칙을 사용할 수 있다고 생각합니다 ..
example int * (*ptr)()[];
start from ptr
" ptr
는"오른쪽으로 가십시오. .. "" 에 대한 포인터입니다. 이제 왼쪽으로 "("나옵니다. 오른쪽 "배열에"정수의 왼쪽 ""
)
. 이제 왼쪽으로 *
이동합니다. "포인터로"입니다. 오른쪽으로 이동합니다 )
. 이제 왼쪽으로 이동합니다. (
바로 이동 나올 ()
... 바로 갈 "인수를받는 함수에"이렇게 []
"및 반환의 배열"잘 갈 ;
끝을, 그래서 왼쪽으로 가서 ... *
"포인터로"이동 왼쪽 ... int
"정수"
C에서 복잡한 유형을 읽는 방법을 설명하는 흥미로운 웹 사이트가 있습니다. http://www.unixwiz.net/techtips/reading-cdecl.html
내가 그것을 해석하는 방법은 다음과 같습니다.
int *something[n];
우선 순위 참고 : 배열 첨자 연산자 (
[]
)는 역 참조 연산자 (*
) 보다 우선 순위가 높습니다 .
따라서 여기에 []
before 를 적용 *
하여 명령문을 다음과 동일하게 만듭니다.
int *(something[i]);
선언이 어떻게 이해되는지에 주목하십시오 :
int num
meansnum
는 aint
,int *ptr
또는int (*ptr)
(value atptr
)는 a이며int
,에ptr
대한 포인터를 만듭니다int
.
이것을 읽을 수 있습니다. ((이것의 i 번째 인덱스 값)의 값은 정수입니다. 따라서 (뭔가의 i 번째 인덱스 값)은 (정수 포인터)로, 정수 포인터의 배열을 만듭니다.
두 번째는
int (*something)[n];
이 진술을 이해하려면 다음 사실에 익숙해야합니다.
배열의 포인터 표현에 대한 참고 사항
somethingElse[i]
은 다음과 같습니다.*(somethingElse + i)
따라서로 대체 somethingElse
하면 선언 당 정수 (*something)
인을 얻습니다 *(*something + i)
. 그래서, (*something)
우리에게 (배열을 가리키는) 무언가와 같은 배열을주었습니다 .
나는 두 번째 선언이 많은 사람들에게 혼란을 겪고 있다고 생각합니다. 이해하기 쉬운 방법이 있습니다.
정수 배열, 즉 int B[8]
.
또한 B를 가리키는 변수 A를 가지겠습니다. 이제 A의 값은 B입니다. (*A) == B
입니다. 따라서 A는 정수 배열을 가리 킵니다. 귀하의 질문에서 arr은 A와 유사합니다.
마찬가지로에서에서 int* (*C) [8]
C는 정수에 대한 포인터 배열에 대한 포인터입니다.
int *arr1[5]
이 선언에서 arr1
정수에 대한 5 개의 포인터 배열입니다. 이유 : 대괄호가 *보다 우선 순위가 높습니다 (영리 해제 연산자). 그리고이 유형에서는 행 수가 고정되어 있지만 (여기서는 5) 열 수는 가변적입니다.
int (*arr2)[5]
이 선언에서 arr2
5 요소의 정수 배열에 대한 포인터입니다. 이유 : 여기에서 () 괄호는 []보다 우선합니다. 그리고이 유형에서 행 수는 가변적이지만 열 수는 고정되어 있습니다 (여기서는 5).