숫자의 제곱을 계산하는 함수를 찾았습니다.
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
n 2의 값을 반환합니다 . 질문은, 어떻게합니까? 약간의 테스트 후, 나는 사이에 발견 (&a)[k]
하고 (&a)[k+1]
있다 sizeof(a)
/ sizeof(int)
. 왜 그런 겁니까?
답변
분명히 해킹 …하지만 *
연산자 를 사용하지 않고 숫자를 제곱하는 방법입니다 (코딩 컨테스트 요구 사항이었습니다).
(&a)[n]
int
위치를 가리키는 포인터와 같습니다.
(a + sizeof(a[n])*n)
따라서 전체 표현은
(&a)[n] -a
= (a + sizeof(a[n])*n -a) /sizeof(int)
= sizeof(a[n])*n / sizeof(int)
= sizeof(int) * n * n / sizeof(int)
= n * n
답변
이 해킹을 이해하려면 먼저 포인터 차이를 이해해야합니다. 즉, 동일한 배열의 요소를 가리키는 두 포인터 를 빼면 어떻게됩니까?
한 포인터를 다른 포인터에서 빼면 결과는 포인터 간의 거리 (배열 요소로 측정)입니다. 그래서, 경우 p
에 점 a[i]
과 q
점하기 위해 a[j]
, 다음 p - q
과 같다i - j
.
C11 : 6.5.6 가산 연산자 (p9) :
두 개의 포인터를 빼면 둘 다 같은 배열 객체의 요소 또는 배열 객체의 마지막 요소를 지나는 요소를 가리켜 야합니다. 결과는 두 배열 요소의 아래 첨자의 차이입니다 . […].
즉, 식 경우P
와Q
포인트 각각i
번째 및j
배열 객체의 번째 요소 식은(P)-(Q)
값 갖는i−j
유형의 객체에 맞는 값을 제공ptrdiff_t
.
이제 배열 이름을 포인터로 a
변환하고 포인터를 배열의 첫 번째 요소로 변환하는 것을 알고 a
있습니다. &a
는 전체 메모리 블록의 주소입니다 a
. 즉, 배열의 주소입니다 . 아래 그림은 이해하는 데 도움이됩니다 ( 자세한 설명은 이 답변 을 읽으십시오 ).
이렇게하면 왜 a
, 왜 &a
같은 주소를 가지고 있고 (&a)[i]
i 번째 배열 의 주소가 어떻게 주소와 같은지 이해하는 데 도움이됩니다 a
.
그래서, 진술
return (&a)[n] - a;
에 해당
return (&a)[n] - (&a)[0];
이 차이는 포인터 (&a)[n]
와 사이의 요소 수를 제공하며 각 요소 (&a)[0]
는 n
배열입니다 n
int
. 따라서 총 배열 요소는 n*n
= n
2 입니다.
노트:
C11 : 6.5.6 가산 연산자 (p9) :
두 개의 포인터를 빼면 둘 다 같은 배열 객체의 요소를 가리 키거나 하나는 배열 객체의 마지막 요소를 지나야합니다 . 결과는 두 배열 요소의 아래 첨자의 차이입니다. 결과의 크기는 implementation-defined 이며 해당 유형 (부호있는 정수 유형)이 헤더에
ptrdiff_t
정의됩니다<stddef.h>
. 해당 유형의 객체에서 결과를 표현할 수없는 경우 동작이 정의되지 않습니다.
이후 (&a)[n]
어레이 오브젝트의 마지막 요소 과거 동일한 배열 오브젝트 나 하나의 요소도 포인트 (&a)[n] - a
호출한다 정의되지 않은 동작 .
또한 함수의 반환 형식을 변경하는 것이 더 나은, 그주의 p
에 ptrdiff_t
.
답변
a
의 (변수) 배열입니다 n
int
.
&a
의 (변수) 배열에 대한 포인터입니다 n
int
.
(&a)[1]
의 포인터 int
일 int
마지막 배열 요소 과거. 이 포인터는 n
int
이후의 요소 &a[0]
입니다.
(&a)[2]
의 포인터를 int
한 int
두 배열의 마지막 배열 요소 과거. 이 포인터는 2 * n
int
이후의 요소 &a[0]
입니다.
(&a)[n]
의 포인터를 int
하나 int
의 마지막 배열 요소 과거의 n
배열. 이 포인터는 n * n
int
이후의 요소 &a[0]
입니다. 그냥 빼기 &a[0]
또는 a
당신은이 n
.
물론 이것은 (&a)[n]
배열 내부 또는 마지막 배열 요소를 지나지 않는 (포인터 산술의 C 규칙 에 따라) 시스템에서 작동하더라도 기술적으로 정의되지 않은 동작입니다 .
답변
동일한 배열의 두 요소를 가리키는 두 개의 포인터가 있으면 그 차이로 인해 이러한 포인터 사이의 요소 수가 산출됩니다. 예를 들어이 코드 스 니펫은 2를 출력합니다.
int a[10];
int *p1 = &a[1];
int *p2 = &a[3];
printf( "%d\n", p2 - p1 );
이제 표현을 고려하자
(&a)[n] - a;
이 표현 a
에서 유형은int *
있으며 첫 번째 요소를 가리 킵니다.
표현식 &a
에는 유형이 int ( * )[n]
있으며 이미지화 된 2 차원 배열의 첫 번째 행을 가리 킵니다. a
유형은 다르지만 값이 일치합니다 .
( &a )[n]
이 이미지화 된 2 차원 배열의 n 번째 요소이고 유형 int[n]
은 이미지 배열의 n 번째 행입니다. 표현(&a)[n] - a
첫 번째 요소의 주소로 변환되며`int * 유형을 갖습니다.
그래서 사이 (&a)[n]
와 a
n 개의 요소의 행이 없음. 따라서 그 차이는 같습니다 n * n
.
답변
Expression | Value | Explanation
a | a | point to array of int elements
a[n] | a + n*sizeof(int) | refer to n-th element in array of int elements
-------------------------------------------------------------------------------------------------
&a | a | point to array of (n int elements array)
(&a)[n] | a + n*sizeof(int[n]) | refer to n-th element in array of (n int elements array)
-------------------------------------------------------------------------------------------------
sizeof(int[n]) | n * sizeof(int) | int[n] is a type of n-int-element array
그러므로,
- 의 유형
(&a)[n]
은int[n]
포인터 - 의 유형
a
은int
포인터
이제 표현식 (&a)[n]-a
은 포인터 빼기를 수행합니다.
(&a)[n]-a
= ((a + n*sizeof(int[n])) - a) / sizeof(int)
= (n * sizeof(int[n])) / sizeof(int)
= (n * n * sizeof(int)) / sizeof(int)
= n * n