[c] 이 방법으로 숫자의 제곱을 계산할 수 없습니다

숫자의 제곱을 계산하는 함수를 찾았습니다.

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) :

두 개의 포인터를 빼면 둘 다 같은 배열 객체의 요소 또는 배열 객체의 마지막 요소를 지나는 요소를 가리켜 야합니다. 결과는 두 배열 요소의 아래 첨자의 차이입니다 . […].
즉, 식 경우 PQ포인트 각각 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= n2 입니다.


노트:

C11 : 6.5.6 가산 연산자 (p9) :

두 개의 포인터를 빼면 둘 다 같은 배열 객체의 요소를 가리 키거나 하나는 배열 객체의 마지막 요소를 지나야합니다 . 결과는 두 배열 요소의 아래 첨자의 차이입니다. 결과의 크기는 implementation-defined 이며 해당 유형 (부호있는 정수 유형)이 헤더에 ptrdiff_t정의됩니다 <stddef.h>. 해당 유형의 객체에서 결과를 표현할 수없는 경우 동작이 정의되지 않습니다.

이후 (&a)[n]어레이 오브젝트의 마지막 요소 과거 동일한 배열 오브젝트 나 하나의 요소도 포인트 (&a)[n] - a호출한다 정의되지 않은 동작 .

또한 함수의 반환 형식을 변경하는 것이 더 나은, 그주의 pptrdiff_t.


답변

a의 (변수) 배열입니다 n int.

&a의 (변수) 배열에 대한 포인터입니다 n int.

(&a)[1]의 포인터 intint마지막 배열 요소 과거. 이 포인터는 n int이후의 요소 &a[0]입니다.

(&a)[2]의 포인터를 intint두 배열의 마지막 배열 요소 과거. 이 포인터는 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]an 개의 요소의 행이 없음. 따라서 그 차이는 같습니다 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

그러므로,

  1. 의 유형 (&a)[n]int[n]포인터
  2. 의 유형 aint포인터

이제 표현식 (&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


답변