Programing

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

lottogame 2020. 5. 6. 20:52
반응형

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


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

... 주소가 무엇인지 모르는 사람들과 혼동하기 때문에 용어 [주소] 가 남아 있다는 것은 불행한 일입니다. 포인터를 주소처럼 생각하는 것은 보통 슬픔으로 이어집니다 .. .

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

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

추신

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


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

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


나는 당신의 출처에 대해 잘 모르겠지만, 당신이 묘사하는 언어의 유형은 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 일 수 있음) 특정 시스템이지만 다른 시스템에서 동일한 코드 / 값을 사용하면 프로그램이 중단 될 수 있습니다).

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


포인터 대 변수

이 사진에서,

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


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

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

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

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

특히 포인터에는 유형이 있습니다. 대부분의 플랫폼에서 포인터 유형은 런타임에 영향을 미치지 않지만 컴파일시 유형을 넘어서는 영향을 미칩니다. 경우 p에 대한 포인터이다 int( int *p;) 다음 p + 1의 정수 포인트 sizeof(int)이후 바이트 p(가정 p + 1여전히 유효한 포인터이다). 경우 q에 대한 포인터 char와 같은 주소로 그 점 p( char *q = p;), 다음 q + 1과 같은 주소가 아닌 p + 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 언어의 규칙이 복잡하고 후드 아래에서 어떤 일이 발생하는지 알지 못하면 직관적 인 느낌을 얻는 것이 어렵다는 것입니다. 또한“이국적인”프로세서 아키텍처를 지원하고 컴파일러 최적화를 지원하기 위해 포인터와 주소 사이의 연결이 다소 느슨합니다.

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


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

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

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;
}

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

작성된 모든 답변에서 판단 할 때 일부 사람들은 (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)의 차이)입니다.


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

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

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


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

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

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.


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

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

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

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

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

  • FILE 포인터를 반환하는 fopen. (변수는 어디에 있습니까)
  • 일반적으로 주소 지정이 불가능한 스택 포인터 또는 프레임 포인터

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

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


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

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

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


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

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

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


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

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


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

(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.

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


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


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


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


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

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

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

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

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

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

Uses of pointers which subvert the type system lead to undefined behavior, whereas similar code in machine language might be well defined. Assemblers will assemble the instructions you have written, but C compilers will optimize based on the assumption that you haven't done anything wrong. If a float *p pointer points to a long n variable, and *p = 0.0 is executed, the compiler is not required to handle this. A subsequent use of n will not necessary read the bit pattern of the float value, but perhaps, it will be an optimized access which is based on the "strict aliasing" assumption that n has not been touched! That is, the assumption that the program is well-behaved, and so p should not be pointing at n.

In C, pointers to code and pointers to data are different, but on many architectures, the addresses are the same. C compilers can be developed which have "fat" pointers, even though the target architecture does not. Fat pointers means that pointers are not just machine addresses, but contain other info, such as information about the size of the object being pointed at, for bounds checking. Portably written programs will easily port to such compilers.

So you can see, there are many semantic differences between machine addresses and C pointers.


Before understanding pointers we need to understand objects. Objects are entities which exist and has a location specifier called an address. A pointer is just a variable like any other variables in C with a type called pointer whose content is interpreted as the address of an object which supports the following operation.

+ : 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`

A pointer is classified based on the type of object it is currently referring. The only part of the information it matters is the size of the object.

Any object supports an operation, & (address of), which retrieves the location specifier (address) of the object as a pointer object type. This should abate the confusion surrounding the nomenclature as this would make sense to call & as an operation of an object rather than a pointer whose resultant type is a pointer of the object type.

Note Throughout this explanation, I have left out the concept of memory.


An address is used to identify a piece of fixed-size storage, usually for each bytes, as an integer. This is precisely called as byte address, which is also used by the ISO C. There can be some other methods to construct an address, e.g. for each bit. However, only byte address is so often used, we usually omit "byte".

Technically, an address is never a value in C, because the definition of term "value" in (ISO) C is:

precise meaning of the contents of an object when interpreted as having a specific type

(Emphasized by me.) However, there is no such "address type" in C.

Pointer is not the same. Pointer is a kind of type in the C language. There are several distinct pointer types. They does not necessarily obey to identical set of rules of the language, e.g. the effect of ++ on a value of type int* vs. char*.

A value in C can be of a pointer type. This is called a pointer value. To be clear, a pointer value is not a pointer in the C language. But we are accustomed to mix them together, because in C it is not likely to be ambiguous: if we call an expression p as a "pointer", it is merely a pointer value but not a type, since a named type in C is not expressed by an expression, but by a type-name or a typedef-name.

Some other things are subtle. As a C user, firstly, one should know what object means:

region of data storage in the execution environment, the contents of which can represent values

An object is an entity to represent values, which are of a specific type. A pointer is an object type. So if we declare int* p;, then p means "an object of pointer type", or an "pointer object".

Note there is no "variable" normatively defined by the standard (in fact it is never being used as a noun by ISO C in normative text). However, informally, we call an object a variable, as some other language does. (But still not so exactly, e.g. in C++ a variable can be of reference type normatively, which is not an object.) The phrases "pointer object" or "pointer variable" are sometimes treated like "pointer value" as above, with a probable slight difference. (One more set of examples is "array".)

Since pointer is a type, and address is effectively "typeless" in C, a pointer value roughly "contains" an address. And an expression of pointer type can yield an address, e.g.

ISO C11 6.5.2.3

3 The unary & operator yields the address of its operand.

Note this wording is introduced by WG14/N1256, i.e. ISO C99:TC3. In C99 there is

3 The unary & operator returns the address of its operand.

It reflects the committee's opinion: an address is not a pointer value returned by the unary & operator.

Despite the wording above, there are still some mess even in the standards.

ISO C11 6.6

9 An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator

ISO C++11 5.19

3 ... An address constant expression is a prvalue core constant expression of pointer type that evaluates to the address of an object with static storage duration, to the address of a function, or to a null pointer value, or a prvalue core constant expression of type std::nullptr_t. ...

(Recent C++ standard draft uses another wording so there is no this problem.)

Actually both "address constant" in C and "address constant expression" in C++ are constant expression of pointer types (or at least "pointer-like" types since C++11).

And the builtin unary & operator is called as "address-of" in C and C++; similarily, std::addressof is introduced in C++11.

These naming may bring misconception. The resulted expression is of pointer type, so they'd be interpreted as: the result contains/yields an address, rather than is an address.


It says "because it confuses those who don't know what addresses are about" - also, it's true: if you learn what addresses are about, you'll be not confused. Theoretically, pointer is a variable which points to another, practically holds an address, which is the address of the variable it points to. I don't know why should hide this fact, it's not a rocket science. If you understand pointers, you'll one step closer to understand how computers work. Go ahead!


Come to think about it, I think it's a matter of semantics. I don't think the author is right, since the C standard refers to a pointer as holding an address to the referenced object as others have already mentioned here. However, address!=memory address. An address can be really anything as per C standard although it will eventually lead to a memory address, the pointer itself can be an id, an offset + selector (x86), really anything as long as it can describe (after mapping) any memory address in the addressable space.


One other way in which a C or C++ pointer differs from a simple memory address due to the different pointer types I haven't seen in the other answers (altrhough given their total size, I may have overlooked it). But it is probably the most important one, because even experienced C/C++ programmers can trip over it:

The compiler may assume that pointers of incompatible types do not point to the same address even if they clearly do, which may give behaviour that would no be possible with a simple pointer==address model. Consider the following code (assuming 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
}

Note that there's an exception for char*, so manipulating values using char* is possible (although not very portable).


Quick summary: A C address is a value, typically represented as a machine-level memory address, with a specific type.

The unqualified word "pointer" is ambiguous. C has pointer objects (variables), pointer types, pointer expressions, and pointer values.

It's very common to use the word "pointer" to mean "pointer object", and that can lead to some confusion -- which is why I try to use "pointer" as an adjective rather than as a noun.

The C standard, at least in some cases, uses the word "pointer" to mean "pointer value". For example, the description of malloc says it "returns either a null pointer or a pointer to the allocated space".

So what's an address in C? It's a pointer value, i.e., a value of some particular pointer type. (Except that a null pointer value is not necessarily referred to as an "address", since it isn't the address of anything).

The standard's description of the unary & operator says it "yields the address of its operand". Outside the C standard, the word "address" is commonly used to refer to a (physical or virtual) memory address, typically one word in size (whatever a "word" is on a given system).

A C "address" is typically implemented as a machine address -- just as a C int value is typically implemented as a machine word. But a C address (pointer value) is more than just a machine address. It's a value typically represented as a machine address, and it's a value with some specific type.


A pointer value is an address. A pointer variable is an object that can store an address. This is true because that's what the standard defines a pointer to be. It's important to tell it to C novices because C novices are often unclear on the difference between a pointer and the thing it points to (that is to say, they don't know the difference between an envelope and a building). The notion of an address (every object has an address and that's what a pointer stores) is important because it sorts that out.

However, the standard talks at a particular level of abstraction. Those people the author talks about who "know what addresses are about", but who are new to C, must necessarily have learned about addresses at a different level of abstraction -- perhaps by programming assembly language. There is no guarantee that the C implementation uses the same representation for addresses as the CPUs opcodes use (referred to as "the store address" in this passage), that these people already know about.

He goes on to talk about "perfectly reasonable address manipulation". As far as the C standard is concerned there's basically no such thing as "perfectly reasonable address manipulation". Addition is defined on pointers and that is basically it. Sure, you can convert a pointer to integer, do some bitwise or arithmetic ops, and then convert it back. This is not guaranteed to work by the standard, so before writing that code you'd better know how your particular C implementation represents pointers and performs that conversion. It probably uses the address representation you expect, but it it doesn't that's your fault because you didn't read the manual. That's not confusion, it's incorrect programming procedure ;-)

In short, C uses a more abstract concept of an address than the author does.

The author's concept of an address of course is also not the lowest-level word on the matter. What with virtual memory maps and physical RAM addressing across multiple chips, the number that you tell the CPU is "the store address" you want to access has basically nothing to do with where the data you want is actually located in hardware. It's all layers of indirection and representation, but the author has chosen one to privilege. If you're going to do that when talking about C, choose the C level to privilege!

Personally I don't think the author's remarks are all that helpful, except in the context of introducing C to assembly programmers. It's certainly not helpful to those coming from higher level languages to say that pointer values aren't addresses. It would be far better to acknowledge the complexity than it is to say that the CPU has the monopoly on saying what an address is and thus that C pointer values "are not" addresses. They are addresses, but they may be written in a different language from the addresses he means. Distinguishing the two things in the context of C as "address" and "store address" would be adequate, I think.


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

       Selector  +--------------+         +-----------+
      ---------->|              |         |           |
                 | Segmentation | ------->|  Paging   |
        Offset   |  Mechanism   |         | Mechanism |
      ---------->|              |         |           |
                 +--------------+         +-----------+
        Virtual                   Linear                Physical

참고 URL : https://stackoverflow.com/questions/15151377/what-exactly-is-ac-pointer-if-not-a-memory-address

반응형