>

오늘 나는 정말로 나를 혼란스럽게하는 C 스 니펫을 읽었다 :

#include <stdio.h>
int
main(void)
{
    int a[] = {0, 1, 2, 3};
    printf("%d\n", *(*(&a + 1) - 1));
    return 0;
}

내 의견으로는, &a + 1  말이되지 않지만 오류없이 실행됩니다.

무엇을 의미합니까? 그리고 K&R C 성경 이 이것을 다루고 있습니까?

업데이트 0 : 답을 읽은 후, 나는이 두 표현이 저를 혼란스럽게한다는 것을 알고 있습니다 :

<올>
  • &a + 1 에서 StackOverflow에 대해 질문했습니다. c의 '&anArray'표현식에 대해

  • *(&a + 1) -1 배열 붕괴와 관련이 있습니다.

    • 답변 # 1

      해부하자.

      a   int [4] 유형이 있습니다  (4 int의 배열). 크기는 4 * sizeof(int) 입니다 .

      &a   int (*)[4] 유형이 있습니다  (4 int 배열의 포인터).

      (&a + 1)  또한 int (*)[4] 유형이 있습니다 . 1 * sizeof(a) 를 시작하는 4 int의 배열을 가리 킵니다.  바이트 (또는 4 * sizeof(int)   a 시작 후 바이트) .

      *(&a + 1)   int [4] 유형입니다  (4 int 배열) 스토리지가 1 * sizeof(a) 를 시작합니다  바이트 (또는 4 * sizeof(int)   a 시작 후 바이트 .

      *(&a + 1) - 1   int * 유형입니다  배열 *(&a + 1) 때문에 (포인터에 대한 포인터)  이 표현식에서 첫 번째 요소에 대한 포인터로 붕괴합니다. 1 * sizeof(int) 를 시작하는 int를 가리킬 것입니다.   *(&a + 1) 가 시작되기 전의 바이트 . 이것은 &a[3] 와 동일한 포인터 값입니다. .

      *(*(&a + 1) - 1)   int 유형입니다 . *(&a + 1) - 1 이기 때문에   &a[3] 와 동일한 포인터 값입니다. *(*(&a + 1) - 1)   a[3] 와 같습니다 3 로 초기화되었습니다. 그것은 printf 가 인쇄 한 숫자입니다. .

    • 답변 # 2

      먼저 작은 알림 (또는 이전에 이것을 몰랐다면 새로운 것) : 배열 또는 포인터 p  및 인덱스 i  표현 p[i]   *(p + i) 와 정확히 동일 .

      이제 무슨 일이 일어나고 있는지 이해하도록 도와 드리겠습니다 ...

      배열 a  프로그램에서 메모리의 어딘가에 저장됩니다. 정확히 중요하지 않은 곳. a 의 위치를 ​​얻으려면   a 에 대한 포인터를 얻습니다. , 당신은 주소 연산자 & 를 사용합니다   &a 처럼 . 여기서 배워야 할 중요한 점은 포인터 자체만으로 특별한 의미가 없으며 중요한 것은 포인터의기본 유형입니다. a 의 유형   int[4] 입니다 즉, a  네 개의 int 의 배열입니다  집단. 표현 &a 의 유형  네 개의 int 의 배열에 대한 포인터입니다 또는 int (*)[4] . int *[4] 유형이므로 괄호가 중요합니다.   int 에 대한 네 개의 포인터 배열 매우 다른 것입니다.

      이제 p[i] 로 돌아가서 *(p + i) 와 동일 . p 대신  우리는 &a 그래서 우리의 표현 *(&a + 1)   (&a)[1] 와 동일 .

      이제 *(&a + 1) 가 무엇인지 설명합니다  의미와 무엇을 하는가. 이제 배열 a 의 메모리 레이아웃에 대해 잠시 생각해 봅시다. . 메모리에서는 다음과 같습니다.

      + --- + --- + --- + --- +
      | 0 | 1 | 2 | 3 |
      + --- + --- + --- + --- +
      ^
      |
      &에이
      
      표현 (&a)[1]  Wyzwyz 취급  배열의 배열이므로 확실히 그렇지 않습니다.이 배열의 두 번째 요소에 액세스하면 범위를 벗어납니다. 이것은 기술적으로정의되지 않은 동작입니다. 그래도 잠시 함께 실행 해보고,가 어떻게 메모리에 보일지 생각해보십시오 :

      + --- + --- + --- + --- + --- + --- + --- + --- +
      | 0 | 1 | 2 | 3 | . | . | . | . |
      + --- + --- + --- + --- + --- + --- + --- + --- +
      ^ ^
      | |
      (&a) [0] (&a) [1]
      

      이제 &a 의 유형을 기억하십시오  ( a 와 동일  따라서 (&a)[0]  이 유형이어야합니다.)는4 개의 (&a)[1] 의 배열입니다. . 배열은 첫 번째 요소에 대한 포인터로 자연스럽게 붕괴하므로 int 식   (&a)[1] 와 동일 , 유형은 &(&a)[1][0] 의 포인터입니다. . int 를 사용할 때  식에서 컴파일러가 우리에게주는 것은 (&a)[1] 의 두 번째 (존재하지 않는) 배열의 첫 번째 요소에 대한 포인터입니다. . 그리고 다시 우리는 &a 에 온다   p[i] 와 같습니다  방정식 : *(p + i)   (&a)[1] 의 포인터입니다. , 그것은 int 입니다  Wyzwyz에서  전체 표현은 p 입니다. , 위의 *(p + i) 빼기 위의 메모리 레이아웃을보고   *((&a)[1] - 1) 가 제공 한 포인터에서   int 전에 우리에게 요소를 제공  이것은 (&a)[1] 의 마지막 요소입니다 즉, 그것은 우리에게 (&a)[1] 를 제공합니다  이것은 (&a)[0] 와 동일합니다 .

      따라서 표현 (&a)[0][3]   a[3] 와 동일 .

      긴 바람이 불며, 위험한 영역 (범위를 벗어난 인덱싱으로 인한 것)을 통과하지만 포인터 산술의 힘으로 인해 결국 모든 것이 잘 작동합니다. 그래도 이와 같은 코드를 작성하지 않는 것이 좋습니다. 사람들은 이러한 변환이 어떻게 작동하는지 알고 있어야 해독 할 수 있습니다.

    • 답변 # 3

      *(*(&a + 1) - 1)  마지막 a[3] 직후 메모리를 가리킬 것입니다.  요소 또는 &a + 1 후 말할 더 나은   a 이후 배열   a 의 유형이 있습니다  (4 개의 &a 의 배열을 가리키는 포인터 '에스). 이러한 포인터의 구성은 표준에 의해 허용되지만 역 참조는 허용하지 않습니다. 결과적으로 후속 산술에 사용할 수 있습니다.

      그래서 int (*)[4] 의 결과  정의되지 않았습니다. 그러나 그럼에도 불구하고 int 더 흥미로운 것입니다. 효과적으로 *(&a + 1) 의 마지막 요소로 평가됩니다. 자세한 설명은 https://stackoverflow.com/a/38202469/2878070을 참조하십시오. 이 말은 더 읽기 쉽고 명확한 구성으로 대체 될 수 있습니다 : *(*(&a + 1) - 1)  (물론 포인터가 아닌 배열에만 적용해야합니다.)

    • 답변 # 4

      자신에게 증명하는 것이 가장 좋습니다 :

      a
      
      

      주소는 다음과 같습니다.

      a[sizeof a / sizeof a[0] - 1]
      
      

      처음 2는 동일한 주소입니다. 세 번째는 $ cat main.c #include <stdio.h> main() { int a[4]; printf("a %p\n",a); printf("&a %p\n",&a); printf("a+1 %p\n",a+1); printf("&a+1 %p\n",&a+1); } 입니다  더 ( $ ./main a 0x7fff81a44600 &a 0x7fff81a44600 a+1 0x7fff81a44604 &a+1 0x7fff81a44610 입니다) ). 네 번째는 4 입니다  더 ( sizeof(int) 입니다) )

    • 답변 # 5

      예를 들어 T 유형의 객체가있는 경우

      0x10 = 16
      
      

      그런 다음 선언

      sizeof(a)
      
      

      포인터 T obj; 를 초기화합니다   T *p = &obj; 오브젝트가 점유 한 메모리 주소

      p  객체 obj 이후 메모리를 가리킴 . 표현 p + 1 의 값   obj 의 값과 같습니다  이는

      와 같습니다.
      p + 1
      
      

      따라서 게시물 &obj plus sizeof( obj ) 에 표시된 배열이 있으면  다음과 같이 typedef를 사용하여 선언을 다시 작성할 수 있습니다.

      ( T * )( ( char * )&obj + sizeof( obj ) )
      
      

      int a[] = {0, 1, 2, 3};  이 경우 typedef int T[4]; T a = { 0, 1, 2, 3 }; 와 같습니다  그리고 차례로 sizeof( T ) 와 같습니다

      표현 sizeof( int[4] )  배열이 차지하는 메모리 범위의 주소를 제공합니다. 표현 4 * sizeof( int )  배열 뒤에 메모리의 주소를 제공하고 표현식의 값은 &a 와 같습니다.

      반면에 표현식에 사용되는 배열 이름-드문 예외는 있지만, 예를 들어 &a + 1 에서 배열 이름 사용  연산자-암시 적으로 첫 번째 요소에 대한 포인터로 변환됩니다.

      그래서, 표현 &a + sizeof( int[4] )   sizeof 유형의 상상 된 요소를 가리킴  실제 첫 번째 요소 &a + 1 후 . 표현 int[4]  이 상상 요소를 제공합니다. 그러나 요소가 a 유형의 배열이므로  이 표현식은 *(&a + 1) 유형의 첫 번째 요소에 대한 포인터로 변환됩니다.

      이 첫 번째 요소는 배열 int[4] 의 마지막 요소를 따릅니다. . 그리고이 경우, 표현 int *  배열의 마지막 요소 a 의 주소를 제공합니다.

      *(&a + 1) - 1 에서 역 참조하여  당신은 배열 a 의 마지막 요소의 값을 얻을 이므로 숫자 *(*(&a + 1) - 1)  출력됩니다.

      a

    관련 자료

  • 이전 powershell - xls를 xlsx로 변환하기 위해 ConvertTo-ExcelXlsx (ImportExcel) 사용
  • 다음 swing - 업데이트 쿼리가 성공적으로 실행되었지만 netbeans의 Java 데이터베이스에있는 테이블이 update 문으로도 업데이트되지 않습니다