메모리 주소가 아닌 경우 C 포인터는 정확히 무엇입니까?


206

C에 대한 평판이 좋은 소스에서 &운영자를 논의한 후 다음 정보가 제공됩니다 .

... 주소가 무엇인지 모르는 사람들과 혼동하기 때문에 용어 [주소] 가 남아 있다는 것은 불행한 일입니다 . .

내가 읽은 다른 자료 (동일하게 평판 좋은 출처에서)는 항상 포인터와 &연산자를 메모리 주소를 제공 한다고 언급했습니다 . 나는 그 문제의 실제 성을 계속 찾고 싶어하지만 평판이 좋은 출처가 동의하지 않을 때는 어려운 일이다.

메모리 포인터가 아닌 경우 포인터가 정확히 무엇 입니까?

추신

저자는 나중에 다음과 같이 말합니다. ... '주소'라는 용어를 계속 사용할 것입니다. 다른 용어 를 발명하는 것이 더 나빠질 수 있기 때문 입니다.


118
포인터는 주소 를 보유 하는 변수 입니다 . 또한 자체 주소가 있습니다. 이것이 포인터와 배열의 근본적인 차이점입니다. 배열은 효율적 이며 , 어드레스 (그리고 암시 적으로, 그 어드레스는 그 자체 ).
WhozCraig

7
견적에 대한 "유명한 출처"는 무엇입니까?
Cornstalks

22
평판이 좋은 원천은 언어 표준이며,이 책에서 반으로 도출 된 책이나 저자의 반에서 인용 한 책은 아닙니다. 나는 그것을 어려운 방법으로 배웠고, 거의 모든 실수를 저지르고 표준으로 묘사 된 것과 거의 비슷한 C의 정신 모델을 천천히 구축 한 다음 마침내 해당 모델을 표준 모델로 대체했습니다.
Alexey Frunze 2013 년

9
@thang 사람들은 일반적으로 그렇게하기 때문에 (x86 리눅스와 Windows는 우리에게 이것을 "가르쳐"), 사람들은 언어 표준을 잘 모르고 근본적으로 다른 경험이 거의 없기 때문에 일반화를 좋아하기 때문에 pointer = integer라고 생각합니다. 플랫폼. 동일한 사람들은 데이터에 대한 포인터와 함수에 대한 포인터가 서로 변환 될 수 있고 데이터가 코드로 실행될 수 있고 코드가 데이터로 액세스 될 수 있다고 가정합니다. 이것은 폰 노이만 아키텍처 (주소 공간 1 개)에서는 사실이지만 하버드 아키텍처 (코드 및 데이터 공간 포함)에서는 반드시 그런 것은 아닙니다.
Alexey Frunze 2013 년

6
@exebook 표준은 초보자를위한 것이 아닙니다 (특히 완전한 표준). 그들은 부드럽게 소개하고 여러 가지 예를 제공해서는 안됩니다. 그들은 공식적으로 무언가를 정의하므로 전문가가 올바르게 구현할 수 있습니다.
Alexey Frunze 2013 년

답변:


148

C 표준은 포인터가 내부적으로 무엇이고 어떻게 내부적으로 작동하는지 정의하지 않습니다. 이것은 C가 컴파일되거나 해석되는 언어로 구현 될 수있는 플랫폼의 수를 제한하지 않도록 의도 된 것입니다.

포인터 값은 일종의 ID 또는 핸들 또는 여러 ID의 조합 (예 : x86 세그먼트 및 오프셋에 대한 안녕하세요) 일 수 있으며 반드시 실제 메모리 주소는 아닙니다. 이 ID는 고정 크기 텍스트 문자열 일 수도 있습니다. 비 주소 표현은 C 인터프리터에게 특히 유용 할 수 있습니다.


34
설명 할 것이 많지 않습니다. 모든 변수는 메모리에 주소를 가지고 있습니다. 그러나 주소를 포인터로 저장할 필요는 없습니다. 대신 1에서 무엇이든 변수에 번호를 매기고 그 숫자를 포인터에 저장할 수 있습니다. 구현에서 숫자를 주소로 변환하는 방법과 해당 숫자 및 표준에 필요한 다른 모든 항목으로 포인터 산술을 수행하는 방법을 알고 있다면 언어 표준에 따라 완벽하게 합법적입니다.
Alexey Frunze

4
x86에서 메모리 주소는 세그먼트 선택기와 오프셋으로 구성되므로 포인터를 세그먼트로 나타내는 세그먼트를 나타냅니다. 오프셋은 여전히 ​​메모리 주소를 사용하고 있습니다.
thang

6
@Lundin 표준의 일반적인 특성을 무시하는 데 아무런 문제가 없으며 플랫폼과 컴파일러를 알 때 적용 할 수 없습니다. 원래 질문은 일반적이므로 답변 할 때 표준을 무시할 수 없습니다.
Alexey Frunze

8
@ 룬딘 혁명 가나 과학자 일 필요는 없습니다. 실제 16 비트 시스템에서 32 비트 시스템을 에뮬레이트하고 디스크 스토리지를 사용하여 64KB의 RAM을 최대 4GB로 확장하고 32 비트 포인터를 큰 파일의 오프셋으로 구현한다고 가정하십시오. 이러한 포인터는 실제 메모리 주소가 아닙니다.
Alexey Frunze

6
내가 본 것 중 가장 좋은 예는 Symbolics Lisp Machines (1990 년경)의 C 구현이었습니다. 각 C 객체는 Lisp 배열로 구현되었으며 포인터는 배열과 인덱스 쌍으로 구현되었습니다. Lisp의 배열 범위 검사로 인해 한 객체에서 다른 객체로 오버플로 할 수 없습니다.
Barmar

62

나는 당신의 출처에 대해 잘 모르겠지만, 당신이 묘사하는 언어의 유형은 C 표준에서 나옵니다.

6.5.3.2 주소 및 간접 연산자
[...]
3. 단항 및 연산자는 피연산자의 주소를 생성합니다. [...]

그래서 ... 네, 포인터는 메모리 주소를 가리 킵니다. 적어도 그것이 C 표준이 의미하는 바입니다.

좀 더 명확하게 말하면 포인터는 일부 주소 을 보유하는 변수 입니다. 단항 연산자 를 사용하여 객체의 주소 (포인터에 저장 될 수 있음)가 반환됩니다 .&

주소에 "42 Wallaby Way, Sydney"주소를 변수에 저장할 수 있습니다 (그리고 해당 변수는 일종의 "포인터"가 될 수 있지만 메모리 주소가 아니기 때문에 "포인터"라고 부르는 것이 아닙니다). 컴퓨터에는 메모리 버킷에 대한 주소가 있습니다. 포인터는 주소 값을 저장합니다 (포인터는 주소 인 "42 Wallaby Way, Sydney"를 저장합니다).

편집 : Alexey Frunze의 의견을 넓히고 싶습니다.

포인터가 정확히 무엇입니까? C 표준을 보자 :

6.2.5 유형
[...]
(20)은 [...]
포인터 유형은 호출 함수 타입 또는 개체 유형에서 유도 될 수있다 참조 타입 . 포인터 유형은 값이 참조 된 유형의 엔티티에 대한 참조를 제공하는 오브젝트를 설명합니다. 참조 된 유형 T에서 파생 된 포인터 유형을 때때로 ''포인터에 대한 포인터 ''라고합니다. 참조 된 유형으로부터 포인터 유형을 구성하는 것을``포인터 유형 도출 ''이라고합니다. 포인터 유형은 완전한 객체 유형입니다.

기본적으로 포인터는 일부 객체 또는 함수에 대한 참조를 제공하는 값을 저장합니다. 거의. 포인터는 일부 객체 또는 함수에 대한 참조를 제공하는 값을 저장하려고하지만 항상 그런 것은 아닙니다 .

6.3.2.3 포인터
[...]
5. 정수는 모든 포인터 유형으로 변환 될 수 있습니다. 이전에 지정된 경우를 제외하고 결과는 구현에 따라 정의되고 올바르게 정렬되지 않았으며 참조 된 유형의 엔티티를 가리 키지 않을 수 있으며 트랩 표현 일 수 있습니다.

위의 인용문은 정수를 포인터로 바꿀 수 있다고 말합니다. 그렇게하면 (즉, 객체 또는 함수에 대한 특정 참조 대신 포인터에 정수 값을 입력하면) 포인터가 "참조 유형의 엔티티를 가리 키지 않을 수 있습니다"(즉, 객체 또는 함수에 대한 참조). 그것은 우리에게 다른 것을 제공 할 수도 있습니다. 그리고 이것은 포인터에 어떤 종류의 핸들이나 ID를 붙일 수있는 곳입니다 (즉, 포인터가 객체를 가리 키지 않습니다. 무언가를 나타내는 값을 저장하지만 해당 값은 주소가 아닐 수 있습니다).

Alexey Frunze가 말했듯이 포인터가 객체 또는 함수의 주소를 저장하지 않을 수 있습니다. 포인터가 대신 일종의 "핸들"또는 ID를 저장하는 것이 가능할 수 있으며, 포인터에 임의의 정수 값을 할당하여이를 수행 할 수 있습니다. 이 핸들 또는 ID가 나타내는 것은 시스템 / 환경 / 컨텍스트에 따라 다릅니다. 시스템 / 구현이 가치를 이해할 수있는 한, 당신은 좋은 모양입니다 (그러나 그것은 특정 가치와 특정 시스템 / 구현에 달려 있습니다).

일반적으로 포인터는 객체 또는 함수에 대한 주소를 저장합니다. 실제 주소 (객체 또는 함수에 대한)를 저장하지 않으면 결과는 구현이 정의됩니다 (정확하게 발생하는 포인터와 현재 포인터가 나타내는 것은 시스템 및 구현에 따라 다르므로 핸들 또는 ID 일 수 있음) 특정 시스템이지만 다른 시스템에서 동일한 코드 / 값을 사용하면 프로그램이 중단 될 수 있습니다).

결국 내가 생각했던 것보다 길어졌습니다.


3
C 인터프리터에서 포인터는 주소가 아닌 ID / 핸들 / 등을 보유 할 수 있습니다.
Alexey Frunze 2013 년

4
@exebook이 표준은 어쨌든 컴파일 된 C로 제한되지 않습니다.
Alexey Frunze

7
@ 룬딘 브라보! 표준을 더 무시하자! 마치 우리가 그것을 충분히 무시하지 않았고 그것 때문에 버그가 많고 휴대하기 어려운 소프트웨어를 생산하지 않은 것처럼. 또한 원래 질문은 일반적이므로 일반적인 답변이 필요하지 않습니다.
Alexey Frunze

3
다른 사람들이 포인터가 주소가 아닌 핸들 또는 다른 것일 수 있다고 말하면 정수를 포인터로 캐스팅하여 포인터로 데이터를 강제 변환 할 수 있다는 의미는 아닙니다. 이는 컴파일러가 포인터를 구현하기 위해 메모리 주소 이외의 다른 것을 사용하고 있음을 의미합니다. DEC의 ABI가있는 알파 프로세서에서 함수 포인터는 함수의 주소가 아니라 함수의 설명자 주소였으며 설명자는 함수의 주소와 함수 매개 변수에 대한 일부 데이터를 포함했습니다. 요점은 C 표준이 매우 유연하다는 것입니다.
Eric Postpischil

5
@Lundin : 포인터가 실제 컴퓨터 시스템의 100 %에서 정수 주소로 구현되었다는 주장은 사실이 아닙니다. 컴퓨터에는 단어 주소 지정 및 세그먼트 오프셋 주소 지정이 있습니다. 근거리 및 원거리 포인터를 지원하는 컴파일러가 여전히 존재합니다. RSX-11, Task Builder 및 해당 오버레이가있는 PDP-11 컴퓨터가 존재하며,이 컴퓨터에서 포인터는 디스크에서 기능을로드하는 데 필요한 정보를 식별해야합니다. 객체가 메모리에 없으면 포인터는 객체의 메모리 주소를 가질 수 없습니다!
Eric Postpischil

39

포인터 대 변수

이 사진에서,

pointer_p는 0x12345에 위치한 포인터이며 0x34567에서 variable_v 변수를 가리 킵니다.


16
이것은 포인터와 달리 주소 개념을 다루지 않을뿐만 아니라 주소가 정수가 아니라는 점을 완전히 놓친다.
Gilles 'SO- 악마 그만해'

19
-1, 이것은 포인터가 무엇인지 설명합니다. 그것은 질문이 아니 었습니다. 그리고 당신은 그 질문 관한 모든 복잡성을 제쳐두고 있습니다.
Alexis

34

포인터를 주소로 생각하는 것은 근사치 입니다. 모든 근사와 마찬가지로 때로는 유용 할 정도로 충분하지만 정확하지도 않아서 문제가 발생할 수 있습니다.

포인터는 객체를 찾을 위치를 나타내는 점에서 주소와 같습니다. 이 비유의 즉각적인 한계 중 하나는 모든 포인터가 실제로 주소를 포함하지는 않는다는 것입니다. NULL주소가 아닌 포인터입니다. 포인터 변수의 내용은 실제로 다음 세 가지 중 하나 일 수 있습니다.

  • 역 참조 될 수있는 객체 의 주소 (주소p포함 된 경우 x표현식 *p의 값은 x) 와 동일합니다 .
  • NULL 포인터 그중 NULL한 예이고;
  • 유효하지 않은 컨텐츠는 객체를 가리 키지 않습니다 ( p유효한 값을 보유하지 않으면 *p프로그램을 크래시하면서 상당히 일반적인 가능성으로 무언가를 수행 할 수 있음).

또한 포인터 (유효하고 null이 아닌 경우) 에 주소 가 포함되어 있다고 말하는 것이 더 정확 합니다. 포인터는 객체를 찾을 위치를 나타내지 만 더 많은 정보가 있습니다.

특히 포인터에는 유형이 있습니다. 대부분의 플랫폼에서 포인터 유형은 런타임에 영향을 미치지 않지만 컴파일시 유형을 넘어서는 영향을 미칩니다. ( )에 p대한 포인터 인 경우 바이트 뒤 의 정수 를 가리 킵니다 ( 여전히 유효한 포인터 라고 가정 ). 경우 에 대한 포인터 와 같은 주소로 그 점 ( ), 다음 과 같은 주소가 아닌 . 포인터를 주소로 생각하면“다음 주소”가 같은 위치에 대한 다른 포인터마다 다른 것은 매우 직관적이지 않습니다.intint *p;p + 1sizeof(int)pp + 1qcharpchar *q = p;q + 1p + 1

일부 환경에서는 메모리에서 동일한 위치를 가리키는 다른 표현 (메모리의 다른 비트 패턴)을 가진 여러 포인터 값을 가질 수 있습니다. 이것을 같은 주소를 가진 다른 포인터 또는 같은 위치에 대한 다른 주소로 생각할 수 있습니다.이 경우에는 은유가 명확하지 않습니다. ==두 피연산자가 그래서 당신이 가질 수있는 이러한 환경에서, 같은 위치를 가리키는 여부 연산자는 항상 당신을 알려줍니다 p == q에도 불구 p하고 q서로 다른 비트 패턴을 가지고있다.

포인터가 유형 또는 권한 정보와 같이 주소 이외의 다른 정보를 전달하는 환경도 있습니다. 프로그래머로서의 삶을 쉽게 접할 수 있습니다.

다른 종류의 포인터가 다른 표현을 갖는 환경이 있습니다. 이를 다른 표현을 갖는 다른 종류의 주소로 생각할 수 있습니다. 예를 들어, 일부 아키텍처에는 바이트 포인터와 단어 포인터 또는 객체 포인터와 함수 포인터가 있습니다.

대체로 포인터를 주소로 생각하는 것이 당신이 명심하는 한 그렇게 나쁘지 않습니다.

  • 주소가 아닌 유효하고 널이 아닌 포인터입니다.
  • 동일한 위치에 대해 여러 개의 주소를 가질 수 있습니다.
  • 주소에 대해 산술을 할 수 없으며 순서가 없습니다.
  • 포인터는 타입 정보도 가지고 있습니다.

다른 방향으로 나아가는 것은 훨씬 더 번거 롭습니다. 주소처럼 보이는 모든 것이 포인터가 될 수있는 것은 아닙니다 . 어딘가에있는 포인터는 정수로 읽을 수있는 비트 패턴으로 표시되며이 정수는 주소라고 말할 수 있습니다. 그러나 다른 방법으로는 모든 정수가 포인터가 아닙니다.

먼저 몇 가지 잘 알려진 제한 사항이 있습니다. 예를 들어, 프로그램의 주소 공간 외부의 위치를 ​​지정하는 정수는 유효한 포인터가 될 수 없습니다. 잘못 정렬 된 주소는 정렬이 필요한 데이터 유형에 대한 유효한 포인터를 만들지 않습니다. 예를 들어, int4 바이트 정렬이 필요한 플랫폼에서 0x7654321은 유효한 int*값이 될 수 없습니다 .

그러나 포인터를 정수로 만들면 문제가 발생하기 때문에 그 이상입니다. 이 문제의 큰 부분은 대부분의 프로그래머가 기대하는 것보다 컴파일러 최적화가 미시 최적화에 훨씬 뛰어나 프로그램의 작동 방식에 대한 정신 모델이 매우 잘못되었다는 것입니다. 주소가 동일한 포인터를 가지고 있다고해서 이것이 동일한 것은 아닙니다. 예를 들어 다음 스 니펫을 고려하십시오.

unsigned int x = 0;
unsigned short *p = (unsigned short*)&x;
p[0] = 1;
printf("%u = %u\n", x, *p);

sizeof(int)==4그리고 sizeof(short)==2, 그리고이 곳에서 밀링 머신에서 1 = 1?(little-endian) 또는 65536 = 1?(big-endian)을 인쇄 할 것으로 예상 할 수 있습니다 . 그러나 GCC 4.4가 설치된 64 비트 Linux PC에서

$ c99 -O2 -Wall a.c && ./a.out 
a.c: In function main’:
a.c:6: warning: dereferencing pointer p does break strict-aliasing rules
a.c:5: note: initialized from here
0 = 1?

GCC는 이 간단한 예제에서 무엇이 잘못되었는지 경고하기에 충분히 친절합니다 . 더 복잡한 예제에서는 컴파일러가 인식하지 못할 수 있습니다. p와 다른 유형이 있으므로 &x어떤 p점을 변경하면 어떤 점이 영향을받을 수 없는지 &x(일부 잘 정의 된 예외 제외). 따라서 컴파일러는 x레지스터에 값을 유지 하고이 레지스터를 *p변경으로 업데이트하지 않아도 됩니다. 프로그램은 동일한 주소에 대한 두 개의 포인터를 역 참조하고 두 개의 다른 값을 얻습니다!

이 예제의 교훈은 C 언어의 정확한 규칙을 유지하는 한 (널이 아닌 유효) 포인터를 주소로 생각하는 것이 좋습니다. 동전의 반대 측면은 C 언어의 규칙이 복잡하고 후드 아래에서 무슨 일이 발생하는지 알지 못하면 직관적 인 느낌을 얻는 것이 어렵다는 것입니다. 또한“이국적인”프로세서 아키텍처와 컴파일러 최적화를 지원하기 위해 포인터와 주소 사이의 연결이 다소 느슨해졌습니다.

따라서 포인터가 주소를 이해의 첫 단계로 생각하지만 너무 직관을 따르지 마십시오.


5
+1. 다른 답변은 포인터가 유형 정보와 함께 제공되는 것으로 보이지 않습니다. 이것은 주소 / ID / 무엇보다 토론보다 훨씬 중요합니다.
undur_gongor 2016 년

타입 정보에 대한 +1 우수한 점수. 컴파일러 예제가 올바른지 확실하지 않습니다 ... 예를 들어 *p = 3p가 초기화되지 않았을 때 성공할 가능성은 거의 없습니다.
LarsH

@LarsH 네가 맞아, 고마워, 내가 어떻게 썼어? 나는 그것을 내 PC에서 놀라운 행동을 보여주는 예제로 대체했습니다.
Gilles 'SO- 악의를 멈춰라'

1
음, NULL은 ((void *) 0) ..?
Aniket Inge

1
@ gnasher729 널 포인터 포인터입니다. NULL그렇지는 않지만 여기에 필요한 세부 수준에 대해서는 관련이없는 산만입니다. 일상적인 프로그래밍의 경우에도 NULL“포인터”라고하지 않는 것으로 구현 될 수 있는 사실은 자주 나오지 않으며 (주로 NULL가변성 함수로 전달됨) 심지어 캐스팅하지 않는 경우에도 발생합니다 이미 모든 포인터 유형이 동일한 표현을 가지고 있다고 가정하고 있습니다).
Gilles 'SO- 악마 그만해'

19

포인터는 주소 자체가 아니라 메모리 주소를 보유하는 변수입니다. 그러나 포인터를 역 참조하고 메모리 위치에 액세스 할 수 있습니다.

예를 들면 다음과 같습니다.

int q = 10; /*say q is at address 0x10203040*/
int *p = &q; /*means let p contain the address of q, which is 0x10203040*/
*p = 20; /*set whatever is at the address pointed by "p" as 20*/

그게 다야. 그렇게 간단합니다.

여기에 이미지 설명을 입력하십시오

내가 말하고있는 것을 보여주는 프로그램과 그 결과는 다음과 같습니다.

http://ideone.com/rcSUsb

프로그램:

#include <stdio.h>

int main(int argc, char *argv[])
{
  /* POINTER AS AN ADDRESS */
  int q = 10;
  int *p = &q;

  printf("address of q is %p\n", (void *)&q);
  printf("p contains %p\n", (void *)p);

  p = NULL;
  printf("NULL p now contains %p\n", (void *)p);
  return 0;
}

5
더 혼란 스러울 수 있습니다. 앨리스, 고양이 보이죠? 아니, 나는 고양이의 미소 만 볼 수 있습니다. 따라서 포인터가 주소라고 말하거나 포인터가 주소를 보유하는 변수이거나 포인터가 주소의 아이디어를 나타내는 개념의 이름이라고 말하면 책 작성자가 혼란에 빠질 수있는 방법은 무엇입니까?
exebook

@exebook 포인터에 익숙한 사람들에게는 매우 간단합니다. 아마도 사진이 도움이 될까요?
Aniket Inge

5
포인터가 반드시 주소를 가질 필요는 없습니다. C 인터프리터에서는 ID / 핸들과 같은 다른 것이 될 수 있습니다.
Alexey Frunze 2013 년

"라벨"또는 변수 이름은 컴파일러 / 어셈블러이며 컴퓨터 수준에는 존재하지 않으므로 메모리에 표시되어야한다고 생각하지 않습니다.
Ben

1
@Aniket 포인터 변수는 포인터 값을 포함 할 수 있습니다. fopen변수를 두 번 이상 사용해야하는 경우 에만 결과를 변수 에 저장 하면됩니다 (예 : fopen거의 항상).
Gilles 'SO- 악의를 그만두십시오'1

16

그 책의 저자가 정확히 무엇을 의미하는지 말하기는 어렵습니다. 포인터에 주소가 포함되어 있는지 여부는 주소를 정의하는 방법과 포인터를 정의하는 방법에 따라 다릅니다.

작성된 모든 답변에서 판단 할 때 일부 사람들은 (1) 주소가 정수 여야하며 (2) 포인터가 사양에서 그렇게 말하지 않은 가상으로 될 필요는 없다고 가정합니다. 이러한 가정을 통해 명확하게 포인터에 주소가 반드시 포함되는 것은 아닙니다.

그러나 (2)는 사실이지만 (1)은 사실 일 필요는 없습니다. 그리고 @는 @CornStalks의 답변에 따라 &가 운영자 의 주소 라고합니다 . 이것은 스펙 작성자가 포인터가 주소를 포함하려고 함을 의미합니까?

포인터에 주소가 있지만 주소가 정수일 필요는 없다고 말할 수 있습니까? 아마도.

나는이 모든 것이 욕설적인 의미 론적 대화라고 생각한다. 실질적으로 말하면 전혀 쓸모가 없습니다. 포인터의 값이 주소가 아닌 방식으로 코드를 생성하는 컴파일러를 생각할 수 있습니까? 그렇다면 무엇입니까? 그것이 내가 생각했던 거죠...

필자는 책의 저자 (포인터가 반드시 주소가 아니라고 주장하는 첫 번째 발췌 부분)가 언급 한 것은 포인터가 고유 유형 정보와 함께 제공된다는 사실입니다.

예를 들어

 int x;
 int* y = &x;
 char* z = &x;

y와 z는 모두 포인터이지만 y + 1과 z + 1은 다릅니다. 그것들이 메모리 주소라면, 그 표현들이 당신에게 같은 가치를 부여하지 않습니까?

그리고 여기에 주소가 마치 마치 슬픔으로 인도하는 것처럼 포인터에 대한 생각 이 있습니다 . 사람들이 포인터를 마치 주소처럼 생각하기 때문에 버그가 작성되었으며 , 이는 보통 슬픔으로 이어진다 .

주소 일 수도 있지만 55555는 포인터가 아닐 수도 있지만 (int *) 55555는 포인터입니다. 55555 + 1 = 55556이지만 (int *) 55555 + 1은 55559 (+/- sizeof (int)의 차이)입니다.


1
포인터 산술을 지적하는 +1은 주소의 산술과 ​​다릅니다.
kutschkem 2016 년

16 비트 8086의 경우, 메모리 어드레스는 16 비트 모두 세그먼트베이스 + 오프셋에 의해 기술된다. 메모리에서 동일한 주소를 제공하는 세그먼트 기준 + 오프셋 조합이 많이 있습니다. 이 far포인터는 단순히 "정수"가 아닙니다.
vonbrand

@vonbrand 왜 당신이 그 의견을 게시했는지 이해할 수 없습니다. 이 문제는 다른 답변에서 의견으로 논의되었습니다. 거의 모든 다른 대답은 address = integer이고 integer가 아닌 것은 address가 아니라고 가정합니다. 나는 단순히 이것을 지적하고 그것이 정확하거나 정확하지 않을 수 있음을 주목하십시오. 대답의 내 요점은 그것이 관련이 없다는 것입니다. 그것은 모두 단지 pedantic이며, 주요 문제는 다른 답변에서 다루지 않습니다.
thang

@tang, 아이디어 "포인터 == 주소"가 잘못되었습니다 . 모두와 그들이 가장 좋아하는 이모가 계속 말을한다고해서 옳은 것은 아닙니다.
vonbrand

@vonbrand, 왜 내 게시물 아래에 댓글을 달았습니까? 나는 그것이 옳고 그른 것이라고 말하지 않았다. 실제로 특정 시나리오 / 가정에서는 옳지 만 항상 그런 것은 아닙니다. 게시물의 요점을 다시 요약하겠습니다 (두 번째). 대답의 내 요점은 그것이 관련이 없다는 것입니다. 그것은 모두 단지 pedantic이며, 주요 문제는 다른 답변에서 다루지 않습니다. pointer == address 또는 address == integer라고 주장하는 답변에 대해 언급하는 것이 더 적절할 것입니다. segment : offset에 관한 Alexey의 게시물에서 내 의견을 참조하십시오.
thang

15

포인터는 메모리 위치를 나타내는 추상화 입니다. 이 인용문은 포인터가 메모리 주소 인 것처럼 생각하는 것이 잘못되었다고 말하지 않으며, 단지 "보통 슬픔으로 이어진다"고 말합니다. 다시 말해, 잘못된 예상을 갖게됩니다.

가장 큰 슬픔의 근원은 포인터 산술이며, 실제로 C의 강점 중 하나입니다. 포인터가 주소 인 경우 포인터 산술은 주소 산술 일 것으로 예상됩니다. 하지만 그렇지 않습니다. 예를 들어, 주소에 10을 추가하면 10 개의 주소 단위로 더 큰 주소가 제공됩니다. 그러나 포인터에 10을 추가하면 포인터가 가리키는 객체 종류의 크기 (및 실제 크기는 아니지만 정렬 경계까지 반올림)의 10 배가됩니다. 함께 int *그 어드레스 유닛 40 (바이트)하여 증분시킨다 10을 추가 32 비트 정수로 통상 아키텍처. 숙련 된 C 프로그래머는이 사실을 알고 있으며 그와 함께 살고 있지만 저자는 분명히 은유에 대한 팬이 아닙니다.

포인터의 내용이 메모리 위치를 나타내는 방법에 대한 추가 질문이 있습니다. 많은 답변에서 설명했듯이 주소가 항상 int (또는 long)는 아닙니다. 일부 아키텍처에서 주소는 "세그먼트"에 오프셋을 더한 값입니다. 포인터는 현재 세그먼트에 대한 오프셋 ( "인근"포인터) 만 포함 할 수 있으며, 그 자체로는 고유 한 메모리 주소가 아닙니다. 그리고 포인터 내용은 하드웨어가 이해하는 것처럼 메모리 주소와 간접적으로 만 관련 될 수 있습니다. 그러나 인용 된 인용문의 저자는 심지어 표현을 언급하지 않았기 때문에, 나는 그들이 생각했던 표현이 아니라 개념적 동등성이라고 생각합니다.


12

다음은 과거에 혼란 스러웠던 사람들에게 설명한 방법입니다. 포인터에는 동작에 영향을주는 두 가지 속성이 있습니다. 그것은이 (일반 환경)이며, 메모리 어드레스, 및 유형 당신은에서 지적하는 객체의 유형과 크기를 알려줍니다.

예를 들면 다음과 같습니다.

union {
    int i;
    char c;
} u;

이 동일한 객체를 가리키는 세 가지 다른 포인터를 가질 수 있습니다.

void *v = &u;
int *i = &u.i;
char *c = &u.c;

이 포인터의 값을 비교하면 모두 동일합니다.

v==i && i==c

그러나 각 포인터를 늘리면 포인터가 가리키는 유형 이 관련이 있음을 알 수 있습니다.

i++;
c++;
// You can't perform arithmetic on a void pointer, so no v++
i != c

변수는 i하고 c있기 때문에,이 시점에서 다른 값을 가질 것이다 i++원인은 i다음 액세스 정수의 주소를 포함하고, c++원인이 c다음 주소를 지정할 수있는 문자를 가리 키도록. 일반적으로 정수는 문자보다 많은 메모리를 차지하므로 모두 증가한 후 i보다 큰 값으로 끝납니다 c.


2
+1 감사합니다. 포인터를 사용하면 사람의 몸과 영혼을 분리 할 수있는만큼 가치와 유형을 분리 할 수 ​​없습니다.
Aki Suihkonen

i == c형식이 잘못되었습니다 (하나에서 다른 것으로 암시 적 변환이있는 경우에만 포인터를 다른 유형과 비교할 수 있습니다). 또한이를 캐스트로 수정하면 전환을 적용했음을 의미하며, 전환이 값을 변경하는지 여부는 논쟁의 여지가 있습니다. (그렇지 않다고 주장 할 수는 있지만이 예제에서 증명하려고했던 것과 똑같은 주장입니다.)
MM

8

Mark Bessey는 이미 그 말을했지만 이해하기 전에는이를 다시 강조해야합니다.

포인터는 리터럴 3보다 변수와 관련이 있습니다.

포인터 값 (주소)과 유형 (읽기 전용과 같은 추가 속성 있는)의 튜플입니다. 유형 (및 추가 매개 변수가있는 경우)은 컨텍스트를 추가로 정의하거나 제한 할 수 있습니다. 예. __far ptr, __near ptr: 주소의 컨텍스트는 무엇입니까 : 스택, 힙, 선형 주소, 어딘가에서 오프셋, 실제 메모리 또는 무엇.

포인터 산술을 정수 산술과 조금 다르게 만드는 유형 의 속성입니다 .

변수가 아닌 포인터의 카운터 예제는 너무 많아 무시할 수 없습니다.

  • FILE 포인터를 반환하는 fopen. (변수는 어디에 있습니까)

  • 일반적으로 주소 지정이 불가능한 스택 포인터 또는 프레임 포인터

    *(int *)0x1231330 = 13; -임의의 정수 값을 pointer_of_integer 유형으로 캐스팅하고 변수를 도입하지 않고 정수 쓰기 / 읽기

C- 프로그램의 수명에는 주소가없는 다른 많은 임시 포인터 인스턴스가있을 것입니다. 따라서 변수가 아니라 컴파일 시간과 관련된 유형 / 표현식입니다.


8

당신은 옳고 제정신입니다. 일반적으로 포인터는 주소 일 뿐이므로 정수로 캐스트하고 모든 산술을 수행 할 수 있습니다.

그러나 때때로 포인터는 주소의 일부일뿐입니다. 일부 아키텍처에서는 포인터가 기본 또는 다른 CPU 레지스터 를 추가하여 주소로 변환 됩니다.

그러나 요즘 플랫 메모리 모델과 C 언어가 기본적으로 컴파일 된 PC 및 ARM 아키텍처에서는 포인터가 1 차원 주소 지정 가능 RAM의 특정 위치에 대한 정수 주소라고 생각하는 것이 좋습니다.


PC ... 플랫 메모리 모델? 선택기는 무엇입니까?
thang

권리. 그리고 다음 아키텍처 변경이 발생하면 아마도 별도의 코드와 데이터 공간이 있거나 누군가가 유서 깊은 세그먼트 아키텍처로 돌아갈 때 (보안에 많은 의미가 있으므로 세그먼트 번호 + 오프셋에 키를 추가하여 권한을 확인할 수도 있음) 사랑스러운 "포인터는 정수일뿐입니다."
vonbrand

7

C의 다른 변수와 마찬가지로 포인터는 기본적으로 하나 이상의 연결된 unsigned char값 으로 표시 될 수있는 비트 모음입니다 (다른 유형의 캐리어 블과 마찬가지로 값 sizeof(some_variable)의 수를 나타냄 unsigned char). 포인터를 다른 변수와 다른 점은 C 컴파일러가 포인터의 비트를 어떻게 든 변수가 저장 될 수있는 장소를 식별하는 것으로 해석한다는 것입니다. C에서는 다른 언어와 달리 여러 변수에 대한 공간을 요청한 다음 해당 세트의 값에 대한 포인터를 해당 세트 내의 다른 변수에 대한 포인터로 변환 할 수 있습니다.

많은 컴파일러가 비트를 사용하여 포인터를 구현하여 실제 컴퓨터 주소를 저장하지만 이것이 유일한 구현은 아닙니다. 구현은 프로그램이 사용하고있는 모든 메모리 객체 (변수 세트)의 하드웨어 주소와 할당 된 크기를 나열하여 하나의 배열 (사용자 코드로는 액세스 할 수 없음)을 유지하고 각 포인터가 배열에 대한 색인을 포함하게 할 수 있습니다. 해당 인덱스에서 오프셋을 사용합니다. 이러한 설계는 시스템이 코드를 자신이 소유 한 메모리에서만 작동하도록 제한 할뿐만 아니라 한 메모리 항목에 대한 포인터를 실수로 다른 메모리 항목에 대한 포인터로 변환 할 수 없도록합니다 (하드웨어를 사용하는 시스템에서) 주소의 경우 foobar하다의 "열한번째"항목을 메모리에 순차적으로 저장된다 (10 개) 항목의 포인터 배열foo대신의 첫 번째 항목을 가리킬 수 bar있지만 각 "포인터"가 객체 ID 및 오프셋 인 시스템에서 코드 foo가 할당 된 범위 를 넘어 포인터를 색인화하려고 시도하면 시스템이 트랩 될 수 있습니다. 포인터와 관련된 물리적 주소를 이동할 수 있기 때문에 이러한 시스템에서 메모리 조각화 문제를 제거 할 수도 있습니다.

포인터는 다소 추상적이지만 완전히 표준을 준수하는 C 컴파일러가 가비지 수집기를 구현할 수있을 정도로 추상적이지는 않습니다. C 컴파일러는 포인터를 포함한 모든 변수가 일련의 unsigned char값 으로 표시되도록 지정 합니다. 변수가 주어지면 변수를 일련의 숫자로 분해하고 나중에 해당 숫자의 시퀀스를 원래 유형의 변수로 다시 변환 할 수 있습니다. 결과적으로 프로그램이calloc일부 저장소 (포인터를 수신), 거기에 무언가 저장, 포인터를 일련의 바이트로 분해하고 화면에 표시 한 다음 모든 참조를 지우십시오. 프로그램이 키보드에서 일부 숫자를 받아 포인터로 재구성 한 다음 해당 포인터에서 데이터를 읽으려고 시도했으며 사용자가 프로그램이 이전에 표시 한 것과 동일한 숫자를 입력하면 프로그램에서 데이터를 출력해야합니다 calloc'ed 메모리 에 저장되었습니다 . 컴퓨터가 사용자가 표시된 숫자의 사본을 만들 었는지 여부를 컴퓨터가 알 수있는 방법이 없기 때문에, 컴퓨터가 앞서 언급 한 메모리에 액세스 할 수 있는지 여부를 알 수 없을 것입니다.


오버 헤드가 크면 숫자 값을 "누설"할 수있는 포인터 값의 사용을 감지하고 가비지 수집기가 수집하거나 재배치하지 않도록 할당을 고정 할 수 있습니다 ( free물론 명시 적으로 호출 되지 않는 한 ). 결과 구현이 유용한 지 여부는 또 다른 문제입니다. 수집하는 기능이 너무 제한적일 수 있기 때문에 적어도 가비지 수집기라고 부를 수 있습니다. char*알려지지 않은 출처에 대한 액세스를 확인해야합니다.
Steve Jessop

@SteveJessop : 코드가 어떤 포인터를 해제해야하는지 알 수 없기 때문에 그러한 디자인은 쓸모없는 것보다 나쁘다고 생각합니다. 포인터처럼 보이는 것을 가정하는 가비지 콜렉터는 지나치게 보수적 일 수 있지만 일반적으로 포인터처럼 보이지만 그렇지 않은 것은 변경 가능성이 있으므로 "영구적 인"메모리 누수를 피할 수 있습니다. 바이트에 대한 포인터를 영구적으로 정지시키는 것처럼 보이는 작업을 수행하면 포인터가 메모리 누수를 보장 할 수 있습니다.
supercat

성능상의 이유로 어쨌든 실패 할 것이라고 생각합니다. 모든 액세스가 확인되어 코드를 느리게 실행하려면 C로 작성하지 마십시오. ;-) C 프로그래머의 독창성에 대한 기대가 당신보다 높습니다. 불편하다고 생각하기 때문에 할당을 불필요하게 고정하는 것을 피할 수는 없습니다. 어쨌든 C ++은이 문제를 처리하기 위해 "안전하게 파생 된 포인터"를 정확하게 정의하므로 C 포인터의 추상화를 합당한 효과적인 가비지 수집을 지원하는 수준으로 높이려면 어떻게해야하는지 알고 있습니다.
Steve Jessop

@SteveJessop : GC 시스템을 유용하게 사용하려면 free호출되지 않은 메모리를 안정적으로 해제 하거나 해제 된 객체에 대한 참조가 라이브 객체에 대한 참조가되지 않도록해야합니다 (필요한 리소스를 사용하는 경우에도) 명백한 수명 관리, GC는 여전히 후자의 기능을 유용하게 수행 할 수있다]; N 객체가 불필요하게 동시에 고정 될 확률이 N이 클수록 0에 가까워지면 객체를 실시간으로 참조하는 것으로 잘못 간주하는 GC 시스템을 사용할 수 있습니다 . 하나는 컴파일러 오류 ... 플래그 기꺼이하지 않는 한
supercat

... 유효한 C ++이지만 컴파일러가 포인터를 인식 할 수없는 형태로 변환 할 수 없다는 것을 증명할 수없는 코드의 경우 실제로는 결코하지 않는 프로그램의 위험을 피할 수있는 방법을 알지 못합니다 정수는 그렇게하는 것으로 잘못 간주 될 수 있으므로 포인터를 사용합니다.
supercat

6

포인터는 C / C ++에서 기본적으로 사용 가능한 변수 유형이며 메모리 주소를 포함합니다. 다른 변수와 마찬가지로 자체 주소를 가지며 메모리를 차지합니다 (금액은 플랫폼마다 다릅니다).

혼란의 결과로 보게 될 한 가지 문제는 단순히 포인터를 값으로 전달하여 함수 내에서 참조를 변경하려고 시도하는 것입니다. 이렇게하면 함수 범위에서 포인터의 복사본이 만들어지고이 새 포인터가 "지점"인 위치를 변경해도 함수를 호출 한 범위에서 포인터의 참조가 변경되지 않습니다. 함수 내에서 실제 포인터를 수정하려면 일반적으로 포인터를 포인터로 전달합니다.


1
일반적으로 핸들 / ID입니다. 일반적으로 일반 주소입니다.
Alexey Frunze 2013 년

위키 백과 의 Handle 정의에 대한 PC가되도록 답변을 조정했습니다 . 핸들은 단순히 포인터에 대한 참조 일 수 있으므로 포인터를 핸들의 특정 인스턴스로 참조하고 싶습니다.
Matthew Sanders

6

간략한 요약 (또한 맨 위에 두겠습니다) :

(0) 포인터를 주소로 생각하는 것은 종종 좋은 학습 도구이며 종종 일반 데이터 유형에 대한 포인터의 실제 구현입니다.

(1) 그러나 대부분의 경우 함수에 대한 컴파일러 포인터는 주소가 아니지만 주소보다 크거나 (일반적으로 2 배 이상, 때로는 더 많음) 실제로 함수의 주소와 내용을 포함하는 것보다 메모리의 구조체에 대한 포인터입니다 일정한 수영장.

(2) 데이터 멤버에 대한 포인터와 메서드에 대한 포인터는 종종 낯선 것입니다.

FAR 및 NEAR 포인터 문제가있는 레거시 x86 코드

(4) 안전한 "뚱뚱한 포인터"를 가진 몇 가지 예, 특히 IBM AS / 400.

더 많이 찾을 수있을 것입니다.

세부 묘사:

UMMPPHHH !!!!! 지금까지 많은 답변이 상당히 일반적인 "프로그래머 weenie"답변이지만 컴파일러 weenie 또는 하드웨어 weenie는 아닙니다. 나는 하드웨어 weenie 인 척하고 종종 컴파일러 weenies와 함께 일하기 때문에 2 센트를 던져 보자.

아마도 대부분의 C 컴파일러에서 유형의 데이터에 대한 포인터 T는 실제로 주소입니다 T.

좋아.

그러나 이러한 많은 컴파일러에서도 특정 포인터는 주소가 아닙니다. 를 보면 알 수 있습니다 sizeof(ThePointer).

예를 들어, 함수에 대한 포인터는 때때로 일반 주소보다 훨씬 더 큽니다. 또는 간접적 인 수준이 관련 될 수 있습니다. 이 기사Intel Itanium 프로세서와 관련된 하나의 설명을 제공하지만 다른 것을 보았습니다. 일반적으로 함수를 호출하려면 함수 코드의 주소뿐만 아니라 함수의 상수 풀 주소도 알아야합니다. 즉, 컴파일러가 생성하지 않고 상수가 단일로드 명령으로로드되는 메모리 영역입니다. 여러 Load Immediate 및 Shift 및 OR 명령어 중 64 비트 상수 따라서 단일 64 비트 주소가 아니라 2 개의 64 비트 주소가 필요합니다. 일부 ABI (Application Binary Interfaces)는이 비트를 128 비트로 이동하는 반면, 다른 ABI (기능 이진 인터페이스)는 간접적 인 수준을 사용하며 함수 포인터는 실제로 2 개의 실제 주소를 포함하는 함수 설명 자의 주소입니다. 어떤게 더 좋아? 관점, 성능, 코드 크기, 그리고 일부 호환성 문제-종종 코드는 포인터가 길거나 길다고 캐스팅 될 수 있다고 가정하지만, long long이 정확히 64 비트라고 가정 할 수도 있습니다. 이러한 코드는 표준을 준수하지 않을 수 있지만 고객이 작동하기를 원할 수 있습니다.

우리 중 다수는 NEAR POINTER 및 FAR POINTERS와 함께 이전 Intel x86 세그먼트 아키텍처에 대한 고통스러운 기억을 가지고 있습니다. 고맙게도 이것들은 지금까지 거의 멸종되었으므로 간단히 요약하면됩니다. 16 비트 실제 모드에서 실제 선형 주소는

LinearAddress = SegmentRegister[SegNum].base << 4 + Offset

보호 모드 인 경우에는

LinearAddress = SegmentRegister[SegNum].base + offset

결과 주소가 세그먼트에 설정된 한계에 대해 검사됩니다. 일부 프로그램은 실제로 표준 C / C ++ FAR 및 NEAR 포인터 선언을 사용하지 않았지만 대부분은 *T--- 라고 말 했지만 컴파일러와 링커 스위치가 있으므로 코드 포인터가 포인터 근처에있을 수 있습니다. CS (코드 세그먼트) 레지스터, 데이터 포인터는 FAR 포인터 일 수 있으며 48 비트 값에 대해 16 비트 세그먼트 번호와 32 비트 오프셋을 모두 지정합니다. 이제이 두 가지 수량은 모두 주소와 관련이 있지만 같은 크기가 아니기 때문에 어떤 주소입니까? 또한 세그먼트에는 실제 주소와 관련된 항목 외에도 권한 (읽기 전용, 읽기-쓰기, 실행 가능)이 포함되었습니다.

보다 흥미로운 예인 IMHO는 IBM AS / 400 제품군입니다. 이 컴퓨터는 C ++로 OS를 구현 한 최초의 컴퓨터 중 하나였습니다. 이 machime에 대한 포인터는 일반적으로 실제 주소 크기 2X했다 -로 예 프리젠 테이션128 비트 포인터이지만 실제 주소는 48-64 비트였으며 다시 한 번 기능이라고 불리는 추가 정보는 읽기, 쓰기 및 버퍼 오버플로를 방지하기위한 제한과 같은 권한을 제공했습니다. 예 : C / C ++를 사용하여이 작업을 수행 할 수 있으며, 이것이 보편적 인 경우 중국 PLA와 슬라브 마피아는 많은 서양 컴퓨터 시스템을 해킹하지 않을 것입니다. 그러나 역사적으로 대부분의 C / C ++ 프로그래밍은 성능에 대한 보안을 무시했습니다. 가장 흥미롭게도, AS400 제품군은 운영 체제가 권한없는 코드에 제공 될 수 있지만 권한이없는 코드는 위조하거나 조작 할 수없는 보안 포인터를 작성할 수 있도록 허용했습니다. 다시 말하지만 보안은 표준을 준수하지만, 비 규격 비 규격 호환 C / C ++ 코드는 보안 시스템에서 작동하지 않습니다. 다시 한 번 공식 표준이 있습니다.

이제 보안 비누 상자에서 벗어나 다양한 유형의 포인터가 실제로 주소가 아닌 다른 방법을 언급합니다. 데이터 멤버에 대한 포인터, 멤버 함수 메소드에 대한 포인터 및 정적 버전이 보통 주소. 마찬가지로 이 게시물은 말합니다 :

이를 해결하는 방법에는 여러 가지가 있습니다 [단일 대 다중 상속 및 가상 상속과 관련된 문제]. Visual Studio 컴파일러가이를 처리하기로 결정한 방법은 다음과 같습니다. 다중 상속 클래스의 멤버 함수에 대한 포인터는 실제로 구조입니다. "그리고"함수 포인터를 캐스팅하면 크기를 변경할 수 있습니다! "라고 말합니다.

보안에 대한 가능성을 짐작할 수 있듯이 포인터가 원시 주소보다 기능처럼 취급되는 C / C ++ 하드웨어 / 소프트웨어 프로젝트에 참여했습니다.

계속할 수는 있지만 아이디어를 얻길 바랍니다.

간략한 요약 (또한 맨 위에 두겠습니다) :

(0) 포인터를 주소로 생각하는 것은 종종 좋은 학습 도구이며 종종 일반 데이터 유형에 대한 포인터의 실제 구현입니다.

(1) 그러나 대부분의 경우 함수에 대한 컴파일러 포인터는 주소가 아니지만 주소보다 크거나 (일반적으로 2X 이상) 실제로 함수의 주소와 내용을 포함하는 것보다 메모리의 구조체에 대한 포인터입니다 일정한 수영장.

(2) 데이터 멤버에 대한 포인터와 메서드에 대한 포인터는 종종 낯선 것입니다.

FAR 및 NEAR 포인터 문제가있는 레거시 x86 코드

(4) 안전한 "뚱뚱한 포인터"를 가진 몇 가지 예, 특히 IBM AS / 400.

더 많이 찾을 수있을 것입니다.


16 비트 리얼 모드에서 LinearAddress = SegmentRegister.Selector * 16 + Offset(16 번 쉬프트하지 않고 16 번을 기록하십시오). 보호 모드 LinearAddress = SegmentRegister.base + offset(임의의 종류의 승산 상기 세그먼트베이스는 GDT / LDT에 저장되고, 세그먼트 레지스터에 캐시 인 것처럼 ).
Alexey Frunze 10

세그먼트베이스에 대해서도 정확합니다. 나는 잘못 기억했다. 선택적으로 4K의 배수 인 세그먼트 한계입니다. 그는 세그먼트베이스가 메모리에서 세그먼트 레지스터로 세그먼트 디스크립터를로드 할 때 하드웨어에 의해 해독 될 필요가있다.
Krazy Glew

4

포인터는 메모리 위치의 주소 (보통 다른 변수의 메모리 주소)를 보유하는 데 사용되는 또 다른 변수입니다.


그래서 포인트는 실제로 메모리 주소입니까? 저자에 동의하지 않습니까? 이해하려고 노력하는 중이 야
d0rmLife 5

포인터의 주요 기능은 무언가를 가리키는 것입니다. 정확히 어떻게 달성되고 실제 주소가 있는지 여부는 정의되지 않습니다. 포인터는 실제 주소가 아닌 ID / 핸들 일 수 있습니다.
Alexey Frunze 2013 년

4

이 방법으로 볼 수 있습니다. 포인터는 주소 지정 가능한 메모리 공간의 주소를 나타내는 값입니다.


2
포인터가 반드시 실제 메모리 주소를 보유 할 필요는 없습니다. 내 답변과 그 아래의 의견을 참조하십시오.
Alexey Frunze 2013 년

what .... 스택의 첫 번째 변수에 대한 포인터는 0을 인쇄하지 않습니다. 구현 방식에 따라 스택 프레임의 상단 (또는 하단)을 인쇄합니다.
thang

@thang 첫 번째 변수의 경우 상단과 하단이 동일합니다. 그리고이 스택의 경우 상단 또는 하단의 주소는 무엇입니까?
Valentin Radu

@ValentinRadu, 당신은 그것을 시도하지 마십시오 왜 .. 분명히 당신은 그것을 시도하지 않았습니다.
thang

2
@thang 당신이 맞아요, 정말 나쁜 가정을 했어요. 내 방어는 오전 5시입니다.
Valentin Radu

3

포인터는 일반적으로 다른 변수의 메모리 주소를 포함 할 수있는 또 다른 변수입니다. 변수이기도 한 포인터도 메모리 주소를 가지고 있습니다.


1
반드시 주소는 아닙니다. Btw, 답변을 게시하기 전에 기존 답변과 의견을 읽었습니까?
Alexey Frunze 2013 년

3

AC 포인터는 메모리 주소와 매우 유사하지만 시스템 종속 세부 정보가 추상화되어 있고 하위 레벨 명령어 세트에는없는 일부 기능이 있습니다.

예를 들어, C 포인터는 상대적으로 풍부한 유형입니다. 구조체 배열을 통해 포인터를 증가 시키면 한 구조체에서 다른 구조체로 멋지게 점프합니다.

포인터는 변환 규칙을 따르며 컴파일 시간 유형 검사를 제공합니다.

소스 코드 레벨에서 이식 가능하지만 표현이 다를 수있는 특수 "널 포인터"값이 있습니다. 값이 0 인 정수 상수를 포인터에 할당하면 해당 포인터가 널 포인터 값을 사용합니다. 그런 식으로 포인터를 초기화하면 Ditto입니다.

포인터는 부울 변수로 사용할 수 있습니다. null 이외의 경우 true를 테스트하고 null이면 false를 테스트합니다.

기계 언어에서 널 포인터가 0xFFFFFFFF와 같은 재미있는 주소 인 경우 해당 값에 대한 명시 적 테스트가 필요할 수 있습니다. C는 당신에게서 그것을 숨 깁니다. 널 포인터가 0xFFFFFFFF 인 경우에도를 사용하여 테스트 할 수 있습니다 if (ptr != 0) { /* not null! */}.

타입 시스템을 파괴하는 포인터를 사용하면 동작이 정의되지 않지만 기계 언어의 유사한 코드가 잘 정의 될 수 있습니다. 어셈블러는 작성한 명령어를 어셈블하지만 C 컴파일러는 사용자가 잘못한 가정을 기반으로 최적화합니다. 경우 float *p포인터 a를 가리키는 long n변수와이 *p = 0.0실행되는 컴파일러는이 문제를 처리 할 필요가 없습니다. 차후의 사용은 nfloat 값의 비트 패턴을 읽을 필요는 없지만, 손대지 n않은 "엄격한 앨리어싱"가정에 기반한 최적화 된 액세스 일 것입니다 ! 즉, 프로그램이 올바르게 작동한다는 가정하에를 p가리키면 안됩니다 n.

C에서 코드에 대한 포인터와 데이터에 대한 포인터는 다르지만 많은 아키텍처에서 주소는 동일합니다. 타겟 아키텍처가없는 경우에도 "팻"포인터가있는 C 컴파일러를 개발할 수 있습니다. 팻 포인터는 포인터가 머신 주소 일뿐만 아니라 경계 검사를 위해 가리키는 객체의 크기에 대한 정보와 같은 다른 정보를 포함한다는 것을 의미합니다. 이식 가능하게 작성된 프로그램은 그러한 컴파일러로 쉽게 이식됩니다.

따라서 컴퓨터 주소와 C 포인터 사이에는 많은 의미상의 차이가 있습니다.


NULL 포인터는 모든 플랫폼에서 생각하는 방식으로 작동하지 않습니다. 위의 CiscoIPPhone에 대한 답장을 참조하십시오. NULL == 0은 x86 기반 플랫폼에서만 유지되는 가정입니다. 컨벤션에 따르면 새로운 플랫폼은 x86과 일치해야하지만 특히 임베디드 세계에서는 그렇지 않습니다. 편집 : 또한 C는 하드웨어에서 포인터 방식의 값을 추상화하지 않습니다. "ptr! = 0"은 NULL! = 0 인 플랫폼에서 NULL 테스트로 작동하지 않습니다.
DX-MON

1
DX-MON, 그것은 표준 C에서 완전히 잘못되었습니다. NULL은 0으로 고안되었으며 명령문에서 상호 교환 적으로 사용될 수 있습니다. 하드웨어에서 NULL 포인터 표현이 모두 0 비트인지 여부는 소스 코드에서 어떻게 표현되는지와 무관합니다.
Mark Bessey 2014 년

@ DX-MON 나는 당신이 올바른 사실로 일하고 있지 않다는 것을 두려워합니다. C에서, 정수 상수 표현식은 널 포인터가 널 주소인지 여부에 상관없이 널 포인터 상수 역할을합니다. ptr != 0널 (null) 테스트가 아닌 C 컴파일러를 알고있는 경우 해당 ID를 밝히십시오 (그러나이를 수행하기 전에 공급 업체에 버그 보고서를 보내십시오).
Kaz

나는 당신이 무엇을 얻고 있는지 알지만 포인터와 메모리 주소를 혼동 하기 때문에 널 포인터에 대한 귀하의 의견은 일관성이 없습니다 . 질문에 인용 된 인용은 피하는 것이 좋습니다 ! 올바른 설명 : C는 오프셋 0의 메모리 주소가 유효한지 여부에 관계없이 널 포인터를 0으로 정의합니다.
Alexis

1
@alexis 장과 구절을 참조하십시오. C는 널 포인터를 0으로 정의하지 않습니다. C는 널 포인터 상수를 나타내는 구문 으로 0 (또는 값이 0 인 정수 상수 표현식)을 정의합니다 . faqs.org/faqs/C-faq/faq (섹션 5).
Kaz

3

포인터를 이해하기 전에 객체를 이해해야합니다. 객체는 존재하며 주소라는 위치 지정자가있는 엔티티입니다. 포인터는 내용이 다음 작업을 지원하는 객체의 주소로 해석 C되는 유형 을 가진 다른 변수 와 마찬가지로 변수입니다 pointer.

+ : A variable of type integer (usually called offset) can be added to yield a new pointer
- : A variable of type integer (usually called offset) can be subtracted to yield a new pointer
  : A variable of type pointer can be subtracted to yield an integer (usually called offset)
* : De-referencing. Retrieve the value of the variable (called address) and map to the object the address refers to.
++: It's just `+= 1`
--: It's just `-= 1`

포인터는 현재 참조하고있는 객체의 유형에 따라 분류됩니다. 중요한 정보의 유일한 부분은 객체의 크기입니다.

모든 객체는 객체 &의 위치 지정자 (주소)를 포인터 객체 유형으로 검색 하는 작업 (주소)을 지원합니다. &결과 유형이 객체 유형의 포인터 인 포인터가 아닌 객체의 작업 으로 호출 하는 것이 의미가 있기 때문에 명명법을 둘러싼 혼란을 완화해야 합니다.

참고이 설명 전체에서 메모리 개념을 생략했습니다.


나는 일반적인 시스템에서 일반적인 포인터의 추상적 현실에 대한 당신의 설명을 좋아합니다. 그러나 아마도 메모리에 대해 논의하는 것이 도움이 될 것입니다. 사실, 나 자신을 위해 말하면, 나는 그것을 알고 있습니다 ...! 연결을 논의하는 것이 큰 그림을 이해하는 데 매우 도움이 될 수 있다고 생각합니다. 어쨌든 +1 :)
d0rmLife

@ d0rmLife : 더 큰 그림을 다루는 다른 답변에 충분한 설명이 있습니다. 나는 또 다른 견해로서 수학적 추상적 인 설명을하고 싶었다. 또한 IMHO &는 '포인터 주소' 라고 부르는 것이 포인터 자체보다는 객체에 더 묶여 있기 때문에 혼란을 덜 줄입니다.
Abhijit

위반은 없지만 충분한 설명이 무엇인지 스스로 결정합니다. 하나의 교과서 만으로는 데이터 구조와 메모리 할당을 충분히 설명 할 수 없습니다. ;) .... 어쨌든, 당신의 대답은 여전히 도움이 , 이 소설없는 경우에도.
d0rmLife

메모리 개념없이 포인터 를 처리하는 것은 의미가 없습니다 . 객체가 메모리없이 존재하는 경우, 주소가없는 위치 (예 : 레지스터)에 있어야합니다. '&'를 사용하려면 메모리가 필요합니다.
Aki Suihkonen

3

주소는 일반적으로 각 바이트에 대해 고정 크기 저장소를 정수로 식별하는 데 사용됩니다. 이것은 정확하게 byte address 라고하며 ISO C에서도 사용됩니다. 주소를 구성하는 다른 방법 (예 : 각 비트)이있을 수 있습니다. 그러나 바이트 주소 만 자주 사용되므로 일반적으로 "바이트"는 생략합니다.

기술적으로, (ISO) C에서 용어 "value"의 정의는 다음과 같기 때문에 주소는 C의 값이 아닙니다.

특정 유형 을 갖는 것으로 해석 될 때 객체 내용의 정확한 의미

(나에 의해 강조됩니다.) 그러나 C에는 그러한 "주소 유형"이 없습니다.

포인터가 동일하지 않습니다. 포인터는 C 언어에서 일종의 유형 입니다. 몇 가지 다른 포인터 유형이 있습니다. 그것들은 반드시 언어의 동일한 규칙들, 예를 들어 ++타입 int*char*.

C의 값은 포인터 유형일 수 있습니다. 이것을 포인터 값 이라고 합니다 . 분명히, 포인터 값은 C 언어의 포인터가 아닙니다. 그러나 C에서는 모호하지 않기 때문에 함께 혼합하는 데 익숙합니다. 식 p을 "포인터" 라고 부르면 C의 명명 된 형식이 아니기 때문에 포인터 값이지만 형식이 아닙니다. 표현 발현 되지만하여 타입 이름 이나 형식 정의 이름 .

다른 것들도 미묘합니다. C 사용자는 먼저 object다음의 의미를 알아야 합니다.

실행 환경의 데이터 저장 영역 (내용은 값을 나타낼 수 있음)

객체는 특정 유형의 값을 나타내는 엔티티입니다. 포인터는 객체 유형 입니다. 우리가 선언한다면 int* p;, 다음, p"포인터 형식의 개체"또는 "포인터 객체"를 의미한다.

표준에 의해 정의 된 "변수"표준 은 없습니다 (사실 표준에서는 ISO C에 의해 명사로 사용되지 않습니다). 그러나 비공식적으로 다른 언어와 마찬가지로 객체를 변수라고합니다. (그러나 C ++에서는 변수가 표준이 아닌 참조 유형일 수 있습니다 . 이는 객체가 아닙니다.) "포인터 객체"또는 "포인터 변수"라는 문구는 때때로 위의 "포인터 값"처럼 처리됩니다. 약간의 차이가있을 수 있습니다. (하나 이상의 예는 "배열"입니다.)

포인터는 유형이고 C에서 주소는 사실상 "유형"이므로 포인터 값은 대략 주소를 "포함"합니다. 그리고 포인터 타입의 표현은 주소를 산출 할 수 있습니다.

ISO C11 6.5.2.3

3 단항 &연산자는 피연산자의 주소를 산출합니다.

이 문구는 WG14 / N1256, 즉 ISO C99 : TC3에 의해 도입되었습니다. C99에는

3 단항 &연산자는 피연산자의 주소를 반환합니다.

이는위원회의 의견을 반영합니다. 주소는 단항 연산자가 반환 한 포인터 값 이 아닙니다& .

위의 문구에도 불구하고 표준에도 여전히 혼란이 있습니다.

ISO C11 6.6

9 주소 상수 는 널 포인터, 정적 저장 기간의 객체를 지정하는 lvalue에 대한 포인터 또는 함수 지정자에 대한 포인터입니다.

ISO C ++ 11 5.19

3 ... 주소 상수 표현식 은 정적 스토리지 지속 시간을 가진 오브젝트의 주소, 함수 주소 또는 널 포인터 값 또는 prvalue 코어 상수 표현식으로 평가되는 포인터 유형의 prvalue 코어 상수 표현식입니다. 유형의 std::nullptr_t. ...

(최근 C ++ 표준 초안은 다른 표현을 사용하므로이 문제는 없습니다.)

실제로 C의 "address constant"와 C ++의 "address constant expression"은 모두 포인터 유형 (또는 C ++ 11 이후 "포인터와 유사한"유형)의 상수 표현입니다.

내장 단항 &연산자는 C 및 C ++에서 "주소"라고합니다. 마찬가지로 std::addressofC ++ 11에 도입되었습니다.

이러한 이름은 오해를 불러 일으킬 수 있습니다. 대신, / 결과가 포함 된 주소를 산출 : 생성 된 표현은로 해석 될 것입니다 때문에, 포인터 타입 인 것입니다 주소.


2

"주소가 무엇인지 모르는 사람들을 혼동시키기 때문에"또한 사실입니다. 주소가 무엇인지 알게되면 혼동하지 않을 것입니다. 이론적으로 포인터는 다른 것을 가리키는 변수이며 실제로는 그것이 가리키는 변수의 주소 인 주소를 보유합니다. 왜이 사실을 숨겨야 하는지 모르겠습니다 . 로켓 과학이 아닙니다. 포인터를 이해하면 컴퓨터 작동 방식을 이해하는 데 한 걸음 더 다가갑니다. 어서!


2

그것에 대해 생각해 보니 의미의 문제라고 생각합니다. C 표준은 다른 사람들이 이미 언급 한 것처럼 참조 된 객체에 대한 주소를 보유하는 포인터를 참조하기 때문에 저자가 옳다고 생각하지 않습니다. 그러나 address! = 메모리 주소입니다. 그것은 결국, 메모리 주소로 이어질 것입니다 포인터 자체가 아이디, 오프셋 + 선택 (86),은 (매핑 후) 기술 수있는만큼 정말 아무것도 할 수 있지만 C 표준에 따라 어드레스는 정말 아무것도 할 수 있는 메모리 주소 지정 가능한 공간의 주소


포인터 주소를 보유 합니다 (또는 null 인 경우). 그러나 그것은 거리가 멀다입니다 되는 주소는 : 예를 들어, 동일한 주소로하지만, 다른 종류의 두 개의 포인터는 많은 상황에 해당하지 않습니다.
Gilles 'SO- 악의를 멈춰라'

@Gilles- int i=5> i is 5 와 같이 "being"이 표시 되면 포인터는 예 주소입니다. 또한 null에는 주소도 있습니다. 일반적으로 유효하지 않은 쓰기 주소 (그러나 반드시 x86- 리얼 모드 참조는 아님)이지만 주소는 그보다 적습니다. null에 대한 요구 사항은 실제로 두 가지뿐입니다. 실제 개체에 대한 포인터와 같지 않은 것을 비교하는 것이 보장되며 두 개의 null 포인터는 동일하게 비교됩니다.
Valentin Radu

반대로, 널 포인터는 객체의 주소와 같지 않아야합니다. 널 포인터를 역 참조하는 것은 정의되지 않은 동작입니다. "포인터가 주소"라는 큰 문제는 그것들이 다르게 작동한다는 것입니다. p포인터 인 경우 p+1주소가 항상 1 씩 증가하는 것은 아닙니다.
Gilles '악의적 인 존재를 중지하십시오'

의견을 다시 읽으십시오 it's guaranteed to compare unequal to a pointer to an actual object. 포인터 산술에 관해서는, 나는 포인트를 보지 못합니다. "+"연산이 반드시 1 바이트를 추가하지 않아도 포인터의 값은 여전히 ​​주소입니다.
Valentin Radu

1

C 또는 C ++ 포인터가 다른 답변에서 보지 못한 다른 포인터 유형으로 인해 간단한 메모리 주소와 다른 한 가지 방법 (전체 크기를 감안할 때 간과했을 수도 있음). 그러나 경험이 많은 C / C ++ 프로그래머조차도 넘어갈 수 있기 때문에 아마도 가장 중요한 것일 것입니다.

컴파일러는 호환되지 않는 유형의 포인터가 분명한 경우에도 동일한 주소를 가리 키지 않는다고 가정 할 수 있으며 이는 단순한 pointer == address 모델로는 불가능한 동작을 제공 할 수 있습니다. 다음 코드를 고려하십시오 (가정 sizeof(int) = 2*sizeof(short)) :

unsigned int i = 0;
unsigned short* p = (unsigned short*)&i;
p[0]=p[1]=1;

if (i == 2 + (unsigned short)(-1))
{
  // you'd expect this to execute, but it need not
}

if (i == 0)
{
  // you'd expect this not to execute, but it actually may do so
}

에 대한 예외가 char*있으므로를 사용하여 값을 조작하는 char*것이 가능합니다 (매우 이식성이 좋지는 않지만).


0

빠른 요약 : AC 주소는 일반적으로 특정 유형의 컴퓨터 수준 메모리 주소로 표시되는 값입니다.

규정되지 않은 단어 "포인터"는 모호합니다. C에는 포인터 객체 (변수), 포인터 유형 , 포인터 표현식 및 포인터 값이 있습니다.

"포인터 객체"를 의미하기 위해 "포인터"라는 단어를 사용하는 것이 매우 일반적이며, 이는 혼동을 일으킬 수 있습니다. 그래서 "포인터"를 명사보다는 형용사로 사용하려고합니다.

C 표준은 최소한 경우에 따라 "포인터"라는 단어를 사용하여 "포인터 값"을 의미합니다. 예를 들어, malloc 의 설명은 "널 포인터 또는 할당 된 공간에 대한 포인터를 리턴합니다"라고 말합니다.

C의 주소는 무엇입니까? 포인터 값, 즉 특정 포인터 유형의 값입니다. (널 포인터 값은 반드시 주소가 아니기 때문에 반드시 "주소"라고 할 필요는 없습니다).

단항 &연산자에 대한 표준 설명 에서는 "피연산자의 주소를 생성합니다"라고 말합니다. C 표준 이외의 단어 "주소"는 일반적으로 (물리적 또는 가상) 메모리 주소, 일반적으로 한 단어 크기 ( "단어"가 주어진 시스템에 있음)를 나타내는 데 사용됩니다.

AC "주소"는 일반적으로 C int값이 일반적으로 기계어로 구현되는 것처럼 기계 주소로 구현됩니다. 그러나 C 주소 (포인터 값)는 단순한 기계 주소 이상입니다. 일반적으로 컴퓨터 주소로 표시 되는 값이며 특정 유형 의 값입니다 .


0

포인터 값 주소입니다. 포인터 변수 주소를 저장할 수있는 객체입니다. 이것이 표준이 포인터를 정의하는 것이기 때문에 사실입니다. C 초보자는 포인터와 가리키는 항목의 차이 (즉, 봉투와 건물의 차이를 알지 못함)에 대해 명확하지 않기 때문에 C 초보자에게 알려주는 것이 중요합니다. 주소 개념 (모든 객체에는 주소가 있으며 포인터가 저장하는 것)은 그 개념을 분류하기 때문에 중요합니다.

그러나 표준은 특정 추상화 수준에서 이야기합니다. 저자가 "어떤 주소에 대해 알고 있는지"에 대해 이야기하지만 C를 처음 접하는 사람들에 대해서는 반드시 어셈블리 언어를 프로그래밍하여 다른 추상화 수준에서 주소에 대해 배워야합니다. C 구현이 CPU opcode가 사용하는 주소 (이 구절에서 "저장소 주소"라고 함)와 동일한 주소 표현을 사용한다고 보장 할 수 없으며,이 사람들이 이미 알고 있습니다.

그는 "완전히 합리적인 주소 조작"에 대해 이야기합니다. C 표준에 관한 한 기본적으로 "완벽하게 합리적인 주소 조작"과 같은 것은 없습니다. 덧셈은 포인터에 정의되어 있으며 기본적으로 그렇습니다. 물론 포인터를 정수로 변환하고 비트 또는 산술 연산을 한 다음 다시 변환 할 수 있습니다. 이것은 표준에 의해 작동한다고 보장되지 않으므로 해당 코드를 작성하기 전에 특정 C 구현이 포인터를 나타내는 방법과 변환을 수행하는 방법을 더 잘 알 수 있습니다. 그것은 아마도 당신이 기대하는 주소 표현을 사용하지만,이 설명서를 읽어 보지 않았 때문에 그것의 당신 잘못이 있다고하지 않습니다. 혼란스럽지 않고 잘못된 프로그래밍 절차입니다. ;-)

간단히 말해서 C는 저자보다 더 추상적 인 주소 개념을 사용합니다.

물론 주소에 대한 저자의 개념은 그 문제에 대한 최하위 단어가 아닙니다. 가상 메모리 맵과 여러 칩의 물리적 RAM 주소 지정을 통해 CPU에 액세스하려는 "저장소 주소"라고 말한 숫자는 기본적으로 원하는 데이터가 실제로 하드웨어의 위치와 관련이 없습니다. 그것은 모든 간접 및 표현 계층이지만, 저자는 특권을 부여하기 위해 하나를 선택했습니다. C에 대해 이야기 할 때 그렇게 하려면 C 레벨을 선택하여 권한을 부여하십시오 !

개인적으로 필자는 C가 어셈블리 프로그래머에게 소개되는 상황을 제외하고는 저자의 말이 그다지 도움이되지 않는다고 생각합니다. 포인터 값이 주소가 아니라고 말하는 것은 고급 언어 사용자에게는 도움이되지 않습니다. CPU가 주소가 무엇인지를 말하는 독점권을 가지고 있고 따라서 C 포인터 값이 주소가 아님을 말하는 것보다 복잡성을 인정하는 것이 훨씬 낫습니다. 그것들은 주소이지만, 그가 의미하는 주소와 다른 언어로 작성 될 수 있습니다. C의 맥락에서 "주소"와 "주소"로 두 가지를 구별하는 것이 적절할 것이라고 생각합니다.


0

간단히 말해서 포인터는 실제로 세그먼트 화 메커니즘의 일부로 오프셋되어 세그먼트 화 후 선형 주소로, 페이징 후 물리 주소로 변환됩니다. 실제 주소는 실제로 램에서 주소가 지정됩니다.

       Selector  +--------------+         +-----------+
      ---------->|              |         |           |
                 | Segmentation | ------->|  Paging   |
        Offset   |  Mechanism   |         | Mechanism |
      ---------->|              |         |           |
                 +--------------+         +-----------+
        Virtual                   Linear                Physical
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.