2017-01-25 5 views
1

현재 C를 배우고 있으며 메모리 레이아웃과 포인터에 대해 혼란스러워합니다.C에서 스택 대 힙 포인터

다음 코드에서 배열은 스택에 할당된다는 것을 이해합니다.

#include <stdio.h> 

int main() { 
    int x[4]; 
    x[0] = 3; x[1] = 2; x[2] = 1; 
    printf("%p\n",x); 
    printf("%p\n", &x); 
} 

제 질문은 왜 두 개의 출력 호출이 같은 값을 출력합니까?

malloc (힙에 할당)을 사용하여 유사한 스 니펫을 시도했는데 값이 다릅니다.

#include <stdio.h> 
#include <stdlib.h> 

int main() { 
    int *x = malloc(sizeof(int) * 4); 
    x[0] = 3; x[1] = 2; x[2] = 1; 
    printf("%p\n",x); 
    printf("%p\n", &x); 
} 
+3

[배열의 주소가 C의 값과 어떻게 다릅니 까?] (http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to- C 값) – Sneftel

+1

C는 자동 변수에 대해 스택을 사용할 필요가 없습니다. 구현에 따라 다릅니다. 그리고 배열은 포인터가 아닙니다. – Olaf

+0

@Olaf 모든 구현체가 스택에 특별히 할당하는 한 구현에 따라 다릅니다 .-)). –

답변

1

이유는 아마도 당신이 배웠던 것과 달리 배열은 포인터가 아닙니다. 배열 C 포인터에 부패 일부 상황에서. 함수에 배열을 전달하면 첫 번째 요소에 대한 포인터로 붕괴됩니다. 해당 요소의 주소는 전체 배열의 주소와 동일합니다 (주소는 항상 객체의 첫 번째 바이트까지입니다).

malloc에서 얻은 정보는 배열이 아니며 메모리 덩어리의 주소입니다. 주소를 포인터에 지정합니다. 그러나 포인터와 청크는 별개의 개체입니다. 주소와는 달리 포인터의 값을 인쇄하면 결과가 달라집니다.


(1) 붕괴는 암시 적 유형 변환 유형에 대한 환상적인 용어입니다. 배열 표현식이 에서 가장 많이 사용되는 경우 (포인터를 기대하는 함수에 인수로 전달되는 경우와 같이) 첫 번째 요소에 대한 포인터로 자동 전환됩니다. '부패'는 유형 정보, 즉 배열 크기를 잃어 버리기 때문입니다.

+0

"포인터에 부식"이 무슨 뜻인지 조금 더 자세히 설명해 주시겠습니까? 나는 전에이 행동에 대해 들어 본 적이 없다. – macsj200

+0

@ macsj200 - 내 편집을 참조하십시오. – StoryTeller

+0

실제로 변환은 매개 변수로 전달하는 것과 관련이 없지만 배열이 사용되는 표현식은 다음과 같습니다. http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3 – Olaf

2

두 개의 인쇄 호출은 배열에 대한 포인터로 사라지는 배열을 인쇄하려고 시도하고 다른 하나는 배열의 주소를 인쇄하기 때문에 동일한 값을 인쇄합니다. 배열에 대한 포인터는 배열의 주소를 포함하므로 동일한 값입니다.

두 번째 경우는 x의 값을 인쇄하고 다른 하나는 x의 주소를 인쇄합니다. x은 할당 한 메모리 블록에 대한 포인터이기 때문에 다른 값이어야합니다.

첫 번째 경우에는 모두 배열 (x)입니다. 두 번째 경우에는 할당 된 메모리 블록 (이름이 지정되지 않음)과 할당 된 블록 (x)에 대한 포인터가 있습니다.

+0

수정 사항 : arra의 이름은 대부분의 사용법에 대해 포인터 **에서 첫 번째 요소 ** (배열이 아님)까지 감소합니다. – Olaf

+1

이름이 부패한다고 말하는 것은 잘못된 것이라고 생각합니다. 그리고 배열에 대한 포인터와 배열의 첫 번째 요소에 대한 포인터 사이에는 * 값 * 구분이 없습니다. 배열은 모든 객체를 인접한 메모리에 유지합니다. 그러나 * 의미 * 구별이 있으며 여기 전체 배열을 가리키는 의미를 원합니다. –

+0

글쎄, 배열 자체는 부패하지 않습니다. 사실, 표준에 따르면, 그것은 변환되는 "배열 유형"의 표현입니다. 그리고이 표준은 다른 유형에 대해 다른 표현을 사용하는 것을 허용하지 않습니다 (그들은 동일하다고 비교할 필요가 없습니다). 유형은 항상 값과 함께 표현식의 일부입니다. 다시 표준 및 구현 세부 사항을 혼동합니다. 그것들은 질문과 관련이 있지만,이 차이점을 지적하는 것이 좋습니다. 과도하게 단순화하고 모두 함께 매시하는 것은 초보자에게 도움이되지 않습니다. – Olaf

2

일부는 실제로 자주 필요하지 않기 때문에 전체 배열의 주소를 실제로 가져올 수 있다는 것은 놀라운 일입니다. 의미에서 배열은 첫 번째 바이트의 주소 인 하나의 주소를 가진 단일 객체입니다. 모든 객체와 마찬가지로 주소는 주소 연산자 인 &을 통해 얻습니다.

첫 번째 요소의은 모든 요소와 마찬가지로 첫 번째 바이트의 주소 인 주소도 가지고 있습니다. 첫 번째 요소에 대한 포인터는 배열 형식이 함수에 인수로 전달 될 때 "조정"됩니다.

이 두 바이트는 동일하며 동일한 주소입니다. 그러나 그것들은 서로 다른 타입을 가지고 있는데, 그것들에 1을 더하고 다시 프린트하면 분명해진다.

반대로 포인터 y은 고유 한 개체입니다 (크기가 4 바이트 또는 8 바이트로 주소를 저장할 수 있음). 어떤 객체와 마찬가지로 주소는 & 연산자로 얻을 수 있습니다. 아마도 혼란스럽게도 에 주소가 있습니다.이 경우 주소는이며이 경우 배열의 첫 번째 바이트 주소입니다. 물론 두 개는 동일하지 않습니다. 포인터 개체는 배열과 다른 위치에 있습니다 (즉, 스택의 옆에는 올라프가 싫어하더라도).

사소한 오류 : 인쇄 포인터에 %p을 사용합니다. 그렇게하면 엄격하게 말하면 포인터를 무효 포인터로 캐스팅합니다 : printf("%p\n", (void *)x);.