배열의 주소는 C의 값과 어떻게 다릅니 까?


189

다음 코드 비트에서 포인터 값과 포인터 주소가 예상과 다릅니다.

그러나 배열 값과 주소는 그렇지 않습니다!

어떻게 이럴 수있어?

산출

my_array = 0022FF00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>

int main()
{
  char my_array[100] = "some cool string";
  printf("my_array = %p\n", my_array);
  printf("&my_array = %p\n", &my_array);

  char *pointer_to_array = my_array;
  printf("pointer_to_array = %p\n", pointer_to_array);
  printf("&pointer_to_array = %p\n", &pointer_to_array);

  printf("Press ENTER to continue...\n");
  getchar();
  return 0;
}

comp.lang.c FAQ에서 :-[C의``포인터와 배열의 동등성 ''은 무엇을 의미합니까? ] ( c-faq.com/aryptr/aryptrequiv.html)-[arr이 배열이면 배열 참조가 포인터로 붕괴되기 때문에 arr과 & arr의 차이점은 무엇입니까? ] ( c-faq.com/aryptr/aryvsadr.html ) 또는 전체 배열 및 포인터 섹션을 읽으십시오 .
jamesdlin

3
내가 다시 여기 이년이 질문에 다이어그램 답변을 추가했다 무엇 sizeof(&array)수익을?
Grijesh Chauhan

답변:


214

배열의 이름은 일반적으로 어레이의 첫 번째 요소의 주소로 평가되므로 array하고 &array있으므로, 동일한 값 (단, 다른 타입을 가지고 array+1그리고 &array+1하지 어레이가 1 개 이상의 요소가 길면 동일).

이에 대한 두 가지 예외가 있습니다. 배열 이름이 피연산자 sizeof이거나 단항 &(address-of) 인 경우 이름은 배열 객체 자체를 나타냅니다. 그러므로sizeof array 포인터 크기가 아닌 전체 배열의 크기를 바이트 단위로 제공합니다.

로 정의 된 배열의 T array[size]경우 유형이 T *입니다. 증가시킬 때, 배열의 다음 요소로 이동합니다.

&array동일한 주소로 평가되지만 동일한 정의가 주어지면 유형의 포인터를 만듭니다. T(*)[size]즉, 단일 요소가 아닌 배열에 대한 포인터입니다. 이 포인터를 늘리면 단일 요소의 크기가 아닌 전체 배열의 크기가 추가됩니다. 예를 들어 다음과 같은 코드를 사용하십시오.

char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));

우리는 두 번째 포인터가 첫 번째 포인터보다 16 더 클 것으로 예상 할 수 있습니다 (16 문자 배열이므로). % p는 일반적으로 포인터를 16 진수로 변환하므로 다음과 같이 보일 수 있습니다.

0x12341000    0x12341010

3
@Alexandre : &array는 배열의 첫 번째 요소에 대한 포인터이며 여기서 array전체 배열 을 나타냅니다. 근본적인 차이점은 비교함으로써 관찰 할 수 sizeof(array)sizeof(&array). 그러나 array함수에 인수로 전달하면 &array실제로 전달됩니다. 배열을 a로 캡슐화하지 않으면 값으로 배열을 전달할 수 없습니다 struct.
Clifford

14
@Clifford : 배열을 함수에 전달하면 첫 번째 요소에 대한 포인터로 쇠약하므로 배열에 대한 포인터가 &array[0]아닌 효과적으로 전달 &array됩니다. 엄밀한 선택 일 수도 있지만 명확하게하는 것이 중요하다고 생각합니다. 컴파일러는 함수에 전달 된 포인터 유형과 일치하는 프로토 타입이 있는지 경고합니다.
CB Bailey

2
@Jerry Coffin 예를 들어 int * p = & a, int 포인터 p의 메모리 주소를 원하면 & p를 수행 할 수 있습니다. & array는 전체 배열의 주소로 변환하기 때문에 (첫 번째 요소의 주소에서 시작). 그런 다음 배열 포인터의 메모리 주소를 어떻게 찾습니까 (어레이에서 첫 번째 요소의 주소를 저장합니까)? 메모리 어딘가에 있어야합니까?
John Lee

2
@JohnLee : 아니요, 메모리의 어느 곳에서나 배열에 대한 포인터가 필요하지 않습니다. 포인터를 만들면 주소를 가져올 수 있습니다 int *p = array; int **pp = &p;.
Jerry Coffin

3
@Clifford 첫 번째 코멘트가 잘못되었습니다. 왜 여전히 그것을 유지합니까? 다음 (@Charles) 답변을 읽지 않는 사람들에게는 오해로 이어질 수 있다고 생각합니다.
Rick

30

배열 이름 ( my_array)이 배열에 대한 포인터와 다르기 때문 입니다. 배열 주소의 별칭이며 해당 주소는 배열 자체의 주소로 정의됩니다.

그러나 포인터는 스택에서 일반적인 C 변수입니다. 따라서 주소를 가져와 내부에있는 주소와 다른 값을 얻을 수 있습니다.

나는이 주제에 대해 여기에 썼다 .


my_array의 값이 스택에없고 my_array [0 ... length] 만 있기 때문에 & my_array가 유효하지 않은 조작이 아니어야합니까? 그렇다면 모든 것이 합리적 일 것입니다.
Alexandre

@ Alexandre : 실제로 그것이 왜 허용되는지 잘 모르겠습니다.
Eli Bendersky

register저장 기간 (정적, 동적 또는 자동)에 관계없이 변수의 주소 (표시되지 않은 경우 )를 사용할 수 있습니다 .
CB Bailey

my_array때문에 자체가 스택에 my_array 있다 전체 어레이.
caf

3
my_array&또는 sizeof연산자 의 주제가 아닌 경우 첫 번째 요소 (예 :)에 대한 포인터로 평가 &my_array[0]되지만 my_array자체는 해당 포인터 가 아닙니다 ( my_array여전히 배열입니다). 그 포인터는 일시적인 rvalue 일뿐입니다 (예 : 주어진 int a;것과 같습니다 a + 1). 개념적으로 최소한 "필요에 따라 계산"됩니다. 실제 "값"은 my_array전체 배열의 내용입니다. C에서이 값을 고정하는 것은 단지 항아리에서 안개를 잡으려고하는 것과 같습니다.
caf

28

식에서 배열의 이름을 사용하는 경우는 어드레스의 (의 피연산자가 아니면 C에서, (a 함수로 전달 포함) &) 오퍼레이터 또는 sizeof조작자가 그 감쇠 는 첫 번째 요소에 대한 포인터.

즉, 대부분의 상황 에서 유형과 가치 모두에 array해당 &array[0]합니다.

귀하의 예에서 printf에 전달할 때 붕괴되는 my_array유형 char[100]char*있습니다.

&my_arraytype char (*)[100](100의 배열을 가리키는 포인터 char)이 있습니다. 의 피연산자 &이므로 이것은 my_array첫 번째 요소에 대한 포인터로 즉시 붕괴되지 않는 경우 중 하나입니다 .

배열에 대한 포인터는 배열의 첫 번째 요소에 대한 포인터와 주소 값이 동일합니다. 배열 객체는 해당 요소의 연속적인 시퀀스이지만 배열에 대한 포인터는 요소에 대한 포인터와 다른 유형입니다. 그 배열. 이것은 두 가지 유형의 포인터에서 포인터 산술을 수행 할 때 중요합니다.

pointer_to_array유형 char *-배열의 첫 번째 요소를 가리 키 my_array도록 초기화되어 초기화 표현식에서 부패되는 &pointer_to_array 유형 - 유형 char **(char ).

이러한 : my_array(부패 후 char*) &my_arraypointer_to_array어레이 또는 어레이 등의 첫 번째 요소 중 하나에 직접 포인트 모두 동일한 어드레스 값을 갖는다.


3

my_array그리고&my_array배열의 메모리 레이아웃을 볼 때 동일한 주소를 갖는 결과를 쉽게 이해할 수 있습니다.

코드에 100이 아닌 10 개의 문자 배열이 있다고 가정 해 봅시다.

char my_array[10];

에 대한 메모리 my_array는 다음과 같습니다.

+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.

C / C ++에서 배열은 다음과 같은 표현식에서 첫 번째 요소에 대한 포인터로 감소합니다.

printf("my_array = %p\n", my_array);

배열의 첫 번째 요소가 어디에 있는지 살펴보면 해당 주소가 배열의 주소와 동일하다는 것을 알 수 있습니다.

my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].

3

C의 전임자였던 B 프로그래밍 언어에서 포인터와 정수는 자유롭게 교환 할 수있었습니다. 시스템은 모든 메모리가 거대한 배열 인 것처럼 작동합니다. 각 변수 이름에는 전역 또는 스택 상대 주소가 있습니다. 각 변수 이름에 대해 컴파일러가 추적해야 할 유일한 것은 전역 변수인지 로컬 변수인지, 그리고 첫 번째 전역 또는 로컬에 상대적인 주소입니다. 변하기 쉬운.

i;[모든 타입이 정수 / 포인터이기 때문에 타입을 지정할 필요가 없다] 와 같은 전역 선언이 주어지면 컴파일러는 다음 address_of_i = next_global++; memory[address_of_i] = 0;과 같이 i++처리하고 다음 과 같은 명령문 은 다음 과 같이 처리합니다.memory[address_of_i] = memory[address_of_i]+1; .

같은 선언 arr[10];은로 처리됩니다 address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;. 선언이 처리 되 자마자 컴파일러는 즉시 arr배열이되는 것을 잊어 버릴 수 있습니다. 같은 문장 arr[i]=6;은로 처리됩니다 memory[memory[address_of_a] + memory[address_of_i]] = 6;. 컴파일러는 arr배열과 i정수를 나타내는 지 또는 그 반대 인지 는 신경 쓰지 않습니다 . 실제로, 둘 다 배열인지 아니면 정수인지 상관하지 않습니다. 결과 동작이 유용한 지 여부에 관계없이 설명대로 코드를 완벽하게 생성합니다.

C 프로그래밍 언어의 목표 중 하나는 B와 크게 호환되는 것이 었습니다. B에서 배열의 이름 (B의 용어에서 "벡터"라고 함)은 처음에 지정된 포인터를 보유한 변수를 식별했습니다. 주어진 크기의 할당의 첫 번째 요소에 해당 이름이 함수의 인수 목록에 나타나면 함수는 벡터에 대한 포인터를받습니다. C가 "실제"배열 유형을 추가했지만 이름이 처음에 할당을 가리키는 포인터 변수가 아니라 할당 주소와 밀접하게 연관되어 있지만 배열이 C 유형 배열을 선언하는 코드로 만든 포인터로 분해되어 배열이 동일하게 동작 함 B 코드에 벡터를 선언 한 다음 주소를 보유하는 변수를 수정하지 않았습니다.


1

사실 &myarraymyarray 모두 기본 주소입니다.

사용하는 대신 차이점을 보려면

printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);

사용하다

printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.