programing

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

prostudy 2022. 8. 22. 21:21
반응형

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

C에 대한 평판이 좋은 소식통에서, 다음 정보는 논의 후에 제공됩니다.&연산자:

...주소라는 용어가 남아 있는 것은 조금 유감입니다.주소를 모르는 사람은 혼란스러워하고, 주소를 알고 있는 사람은 오해하기 때문입니다.포인터를 주소라고 생각하는 것은 보통 슬픔으로 이어집니다.

내가 읽은 다른 자료들은 (평등하게 평판이 좋은 출처로부터) 항상 뻔뻔하게 포인터와 그 정보를 언급해 왔다.&메모리 주소를 지정하는 연산자.저는 그 문제의 실체를 계속 찾고 싶지만, 평판이 좋은 출처가 동의하지 않을 때는 좀 어렵습니다.

메모리 주소가 아니라면 포인터가 정확히 무엇입니까?

추신.

저자는 나중에 이렇게 말한다.다른 용어를 발명하는 것 더 나쁘기 때문에,주소라는 용어를 계속 사용합니다.

C 표준에서는 포인터가 내부적으로 무엇이며 어떻게 작동하는지 정의하지 않습니다.이는 C를 컴파일 언어 또는 인터프리터 언어로 구현할 수 있는 플랫폼의 수를 제한하지 않기 위한 의도적인 것입니다.

포인터 값은 일종의 ID 또는 핸들 또는 여러 ID(x86 세그먼트 및 오프셋에 대해 인사)의 조합일 수 있으며, 반드시 실제 메모리 주소는 아닙니다.이 ID는 임의의 것이 될 수 있습니다.고정 크기의 텍스트 문자열도 마찬가지입니다.주소 이외의 표현은 C 인터프리터에게 특히 도움이 됩니다.

포인터를 주소로 생각하는 것은 근사치입니다.모든 근사치와 마찬가지로 때로는 유용하게 쓰이기에 충분하지만, 그것에 의존하는 것이 문제를 일으킨다는 의미도 정확하지 않습니다.

포인터는 객체의 위치를 나타내는 주소와 같습니다.이 유추의 직접적인 제한 중 하나는 모든 포인터에 실제로 주소가 포함되어 있지 않다는 것입니다. NULL는 주소가 아닌 포인터입니다.포인터 변수의 내용은 실제로 다음 세 가지 유형 중 하나입니다.

  • 참조할 수 있는 객체의 주소(경우에 따라)p주소가 포함되어 있습니다.x그러면 그 표현은*p와 같은 가치가 있다x);
  • 중 하나의 늘 포인터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?(리틀엔디안) 또는65536 = 1?(빅엔디안).단, 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언어의 규칙이 복잡하고, 후드 아래에서 무슨 일이 일어나는지 모르는 한 직관적인 느낌을 받기 어렵다는 것이다.또, 포인터와 주소의 접속이, 「특수한」프로세서 아키텍쳐를 서포트하는 것과 컴파일러의 최적화를 서포트하는 것 모두, 다소 느슨한 것이 특징입니다.

그래서 포인터가 주소라고 이해의 첫 단계로 생각하되, 그 직관을 너무 많이 따르지 마세요.

당신의 출처는 확실하지 않지만, 당신이 설명하는 언어의 유형은 C 표준에서 나온 것입니다.

6.5.3.2 주소 및 간접 연산자
[...]
3. 단항연산자는 피연산자의 주소를 제공한다.[...]

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

좀 더 명확하게 말하면, 포인터는 어떤 주소의 값을 유지하는 변수입니다.오브젝트 주소(포인터에 저장 가능)는 유니리와 함께 반환됩니다.&교환입니다.

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

편집: Alexey Frunze의 코멘트를 자세히 설명하겠습니다.

포인터가 정확히 뭐죠?C 표준을 살펴보겠습니다.

6.2.5 타입
[...]
20. [...]
포인터 유형은 참조 유형이라고 하는 함수 유형 또는 개체 유형에서 파생될 수 있습니다.포인터 유형은 참조된 유형의 엔티티에 대한 참조를 제공하는 값을 가진 개체를 나타냅니다.참조된 타입 T에서 파생된 포인터 타입은 "T로의 포인터"라고 불리기도 합니다.참조된 형식에서 포인터 유형을 구성하는 것을 "pointer type derivation"이라고 합니다.포인터 유형은 완전한 개체 유형입니다.

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

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

위의 인용문에 따르면 정수를 포인터로 바꿀 수 있습니다.이렇게 하면(즉, 객체나 함수에 대한 특정 참조 대신 정수 값을 포인터에 넣는 경우), 포인터는 "참조 유형의 엔티티를 가리키지 않을 수 있습니다"(즉, 객체나 함수에 대한 참조를 제공하지 않을 수 있습니다).뭔가 다른 걸 얻을 수 있을지도 몰라포인터에 핸들이나 ID를 붙일 수 있는 장소입니다(즉, 포인터는 오브젝트를 가리키고 있지 않고, 무언가를 나타내는 값을 격납하고 있습니다만, 그 값은 주소가 아닐 수 있습니다.

Alexey Frunze가 말했듯이 포인터가 객체나 함수에 대한 주소를 저장하지 않을 수 있습니다.포인터가 대신 일종의 "핸들" 또는 ID를 저장할 수 있으며, 포인터에 임의의 정수 값을 할당하여 이를 수행할 수 있습니다.이 핸들 또는 ID가 나타내는 것은 시스템/환경/콘텍스트에 따라 달라집니다.시스템/실장이 가치를 이해할 수 있는 한, 상태는 양호합니다(단, 이는 특정 값과 특정 시스템/실장에 따라 다릅니다).

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

내가 생각했던 것보다 더 길어졌어.

당신이 옳고 제정신이에요.일반적으로 포인터는 주소일 뿐이므로 정수로 캐스팅하여 임의의 산술을 수행할 수 있습니다.

그러나 때때로 포인터는 주소의 일부일 뿐입니다.일부 아키텍처에서는 포인터가 베이스가 추가된 주소로 변환되거나 다른 CPU 레지스터가 사용됩니다.

그러나 오늘날 플랫 메모리 모델과 C 언어가 네이티브하게 컴파일된 PC 및 ARM 아키텍처에서는 포인터가 1차원 주소 지정 가능한 RAM 내의 어떤 장소에 대한 정수 주소라고 생각해도 괜찮습니다.

포인터는 기억의 위치를 나타내는 추상화입니다.이 인용문은 포인터에 대해 마치 메모리 주소인 것처럼 생각하는 것이 잘못되었다고 말하는 것이 아니라 "보통 슬픔으로 이어진다"고 말하고 있습니다.즉, 잘못된 기대를 하게 되는 것입니다.

슬픔의 가장 유력한 원인은 확실히 포인터 산술인데, 이것은 사실 C의 강점 중 하나이다.포인터가 주소일 경우 포인터 산술은 주소 산술이 될 것으로 예상되지만 그렇지 않습니다.예를 들어, 주소에 10을 추가하면 10개의 주소 단위로 더 큰 주소를 얻을 수 있습니다.단, 포인터에 10을 추가하면 포인터는 포인터가 가리키는 오브젝트 종류의 10배 크기(실제 크기는 아니지만 정렬 경계로 반올림)만큼 증가합니다.를 사용하여int *32비트 정수를 가진 일반 아키텍처에서는 10을 더하면 40개의 주소 단위(바이트)가 증가합니다.경험 많은 C 프로그래머들은 이것을 알고 있고, 그것을 모든 종류의 좋은 용도로 사용하지만, 당신의 작가는 분명 엉성한 은유를 좋아하지 않는다.

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

요약(위에도 기재합니다) :

(0) 포인터를 주소로 생각하는 것은 좋은 학습 도구이며, 통상적인 데이터 타입에 대한 포인터의 실제 구현이 되는 경우가 많습니다.

(1) 그러나 대부분의 경우, 함수에 대한 컴파일러 포인터는 주소가 아니라 주소보다 크거나(일반적으로 2배 이상), 또는 실제로는 함수의 주소나 상수 풀과 같은 것을 포함하는 것보다 메모리 내의 구조에 대한 포인터입니다.

(2) 데이터 멤버에 대한 포인터 및 메서드에 대한 포인터는 더 낯선 경우가 많습니다.

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

(4) 안전한 "팻 포인터"가 있는 IBM AS/400을 비롯한 여러 가지 예.

더 찾을 수 있을 거예요.

상세:

UMPPHHH!!!지금까지의 답변의 대부분은 상당히 전형적인 "프로그래머 위니" 답변이지만 컴파일러 위니나 하드웨어 위니는 아닙니다.저는 하드웨어 위니인 척하고 컴파일러 위니에서 작업하는 경우가 많기 때문에 제 의견을 말씀드리겠습니다.

대부분의 경우 C 컴파일러는 데이터 타입에 대한 포인터입니다.T사실, 그 주소는T.

좋아.

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

예를 들어 함수에 대한 포인터는 보통 주소보다 훨씬 클 수 있습니다.또는 간접적인 수준일 수 있습니다.이 문서에서는 인텔 Itanium 프로세서에 관한 설명을 하나 소개하고 있습니다만, 그 외의 설명도 있습니다.일반적으로 함수를 호출하려면 함수 코드의 주소뿐만 아니라 함수의 상수 풀의 주소도 알아야 합니다.이것은 컴파일러가 여러 로드 즉시 명령과 시프트 및 OR 명령에서 64비트 상수를 생성해야 하는 것이 아니라 단일 로드 명령으로 상수가 로드되는 메모리 영역입니다.따라서 단일 64비트주소가 아닌 2개의 64비트주소가 필요합니다.일부 ABI(Application Binary Interfaces)는 이것을 128비트로 이동하는 반면, 다른 ABI(Application Binary Interfaces)는 간접적인 수준을 사용합니다. 함수 포인터는 실제로 앞서 언급한 2개의 실제 주소를 포함하는 함수 기술자의 주소입니다.어떤 게 더 나아요?성능, 코드 크기 및 일부 호환성 문제 등 사용자의 관점에 따라 다릅니다. 대부분의 경우 코드는 포인터를 긴 길이 또는 긴 길이로 캐스팅할 수 있다고 가정하지만 긴 길이도 정확히 64비트라고 가정할 수 있습니다.이러한 코드는 표준 규격에 준거하고 있지 않을 수 있지만, 고객은 이 코드가 기능하기를 원할 수 있습니다.

많은 사람들은 NAR POINTER와 FAR POINTER를 사용한 오래된 인텔 x86 세그먼트 아키텍처에 대한 아픈 기억을 가지고 있습니다.다행히 이것들은 현재 거의 멸종되어 있기 때문에 간단한 요약만 하겠습니다.16비트 리얼 모드에서는 실제 리니어 주소는 다음과 같습니다

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

반면 보호 모드에서는

LinearAddress = SegmentRegister[SegNum].base + offset

세그먼트에 설정된 제한과 대조하여 결과 주소를 확인합니다.일부 프로그램에서는 실제로 표준 C/C++ FAR 및 NEAR 포인터 선언을 사용하지 않았지만 많은 프로그램들은 단지 다음과 같이 말했습니다.*T--- 단, 컴파일러와 링커 스위치가 있기 때문에 예를 들어 코드 포인터는 포인터 근처에 있을 수 있습니다.CS(코드 세그먼트) 레지스터에 있는 것과 대조하여 32비트 오프셋이 되는 반면 데이터 포인터는 FAR 포인터가 되어 16비트 세그먼트 번호와 48비트 값의 32비트 오프셋을 모두 지정할 수 있습니다.이 두 수량은 모두 주소와 관련이 있습니다만, 같은 사이즈가 아니기 때문에 어느 쪽이 주소입니까?게다가 세그먼트(segment)는, 실제의 주소에 관련하는 것 외에, 읽기 전용, 읽기-쓰기, 실행 가능 파일등의 권한도 가지고 있었습니다.

더 흥미로운 예로는 IMHO가 IBM AS/400 제품군입니다.이 컴퓨터는 C++에서 OS를 최초로 구현한 컴퓨터 중 하나입니다.이 머신의 포인터는 보통 실제 주소 크기의 2배였습니다.예를 들어, 이 프레젠테이션에서 말하는 것처럼 128비트 포인터입니다만, 실제 주소는 48-64비트였습니다.또, 기능이라고 불리는 추가 정보에는, 읽기, 쓰기, 버퍼 오버플로를 막기 위한 제한등이 있습니다.네, C/C++와 호환성이 있습니다.그리고 이것이 어디에나 존재한다면 중국 인민해방군과 슬라브족 마피아는 그렇게 많은 서양 컴퓨터 시스템을 해킹하지 않을 것입니다.그러나 역사적으로 대부분의 C/C++ 프로그래밍은 성능에 대한 보안을 소홀히 했습니다.가장 흥미로운 것은 AS400 패밀리는 운영체제가 권한 없는 코드에 부여될 수 있지만 권한 없는 코드에 의해 위조 또는 조작할 수 없는 안전한 포인터를 만들 수 있게 했다는 것입니다.보안은 표준규격에 준거하고 있지만 표준규격에 준거하지 않은 C/C++ 코드는 이러한 보안시스템에서는 동작하지 않습니다.다시 말하지만, 공식적인 기준과 사실상의 기준이 있습니다.

이제 보안 비누박스에서 벗어나 (다양한 유형의) 포인터가 실제로 주소가 아닌 경우가 많은 몇 가지 다른 방법에 대해 설명하겠습니다.데이터 멤버에 대한 포인터, 멤버 함수 메서드에 대한 포인터 및 그 정적 버전은 일반 주소보다 크다. 투고에서는, 다음과 같이 기술하고 있습니다.

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

제가 보안에 대해 언급하고 있는 것을 보면 아시겠지만, 저는 C/C++ 하드웨어/소프트웨어 프로젝트에 관여해 왔습니다.그 프로젝트에서는 포인터가 원시 주소라기보다는 기능에 가깝습니다.

계속 할 수는 있지만 이해하시길 바랍니다.

요약(위에도 기재합니다) :

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

(1) 그러나 대부분의 경우, 함수에 대한 컴파일러 포인터는 주소가 아니라 주소보다 크거나(일반적으로 2배 이상), 또는 실제로는 함수의 주소나 상수 풀과 같은 것을 포함하는 것보다 메모리 내의 구조에 대한 포인터입니다.

(2) 데이터 멤버에 대한 포인터 및 메서드에 대한 포인터는 더 낯선 경우가 많습니다.

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

(4) 안전한 "팻 포인터"가 있는 IBM AS/400을 비롯한 여러 가지 예.

더 찾을 수 있을 거예요.

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

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

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*/

바로 그겁니다.그렇게 간단하다.

enter image description here

내가 말하는 내용과 그 결과를 보여주는 프로그램은 다음과 같습니다.

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

마크 베시는 이미 말했지만, 이해될 때까지 다시 강조할 필요가 있다.

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

포인터는 (주소의) 값과 (읽기 전용 등의 추가 속성이 있는) 유형의 튜플입니다.유형(및 추가 파라미터가 있는 경우)은 컨텍스트를 더 정의하거나 제한할 수 있습니다. __far ptr, __near ptr: 주소의 컨텍스트: 스택, 힙, 선형 주소, 어딘가에서 오프셋, 물리 메모리 등.

포인터 산술이 정수 산술과 약간 다른 것은 유형의 특성입니다.

변수가 아닌 포인터의 카운터 예는 무시할 수 없을 정도로 많습니다.

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

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

    *(int *)0x1231330 = 13;-- 임의의 정수값을 pointer_of_inter 유형에 캐스팅하여 변수를 도입하지 않고 정수를 쓰고 읽습니다.

C-프로그램의 라이프 타임에는 주소가 없는 임시 포인터의 인스턴스가 많이 있습니다.따라서 그것들은 변수가 아니라 컴파일 시간과 관련된 식/값입니다.

C 포인터는 메모리 주소와 매우 유사하지만 머신 의존적인 세부 정보는 추상화되어 있을 뿐만 아니라 하위 수준의 명령 집합에서 찾을 수 없는 일부 기능이 있습니다.

예를 들어 C 포인터는 비교적 풍부한 타입입니다.구조물 배열을 통해 포인터를 증가시키면 한 구조에서 다른 구조체로 원활하게 이동합니다.

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

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

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

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

유형 시스템을 전복시키는 포인터를 사용하면 정의되지 않은 동작이 발생하지만 기계 언어의 유사한 코드가 잘 정의될 수 있습니다.어셈블러는 사용자가 작성한 명령을 조립하지만 C 컴파일러는 사용자가 잘못한 것이 없다는 가정에 따라 최적화합니다.만약 a가float *p포인터가 가리키다long n변수 및*p = 0.0실행되면 컴파일러가 이 처리를 수행할 필요가 없습니다.의 후속 사용nfloat 값의 비트패턴을 읽을 필요는 없지만, 아마도 다음과 같은 "float aliasing" 가정에 기초한 최적화된 액세스가 될 것입니다.n건드리지 않았습니다!즉, 프로그램이 올바르게 동작하고 있다는 가정입니다.p을 가리키면 안 된다n.

C에서 코드에 대한 포인터와 데이터에 대한 포인터는 다르지만, 많은 아키텍처에서 주소는 동일합니다.C 컴파일러는 대상 아키텍처가 아닌 경우에도 "팻" 포인터를 가진 컴파일러를 개발할 수 있습니다.fat pointers는 포인터가 단순히 머신주소일 뿐만 아니라 경계체크를 위해 가리키는 오브젝트의 크기에 대한 정보 등 다른 정보를 포함하고 있음을 의미합니다.포터블하게 작성된 프로그램은 이러한 컴파일러에 쉽게 이식할 수 있습니다.

보시다시피 머신 주소와 C 포인터 사이에는 많은 의미상의 차이가 있습니다.

Pointer vs Variable

이 사진에서는

pointer_p는 0x12345에 있는 포인터로 0x34567에 있는 변수 variable_v를 가리키고 있습니다.

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

기재된 모든 답변으로 미루어 볼 때, 어떤 사람들은 (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*)5555+1은 크기(int)의 관점에서 55559(+/-)입니다.

다음은 제가 과거에 혼란스러운 사람들에게 설명한 방법입니다.포인터에는 동작에 영향을 주는2개의 속성이 있습니다.이 값에는 메모리 주소(일반적인 환경에서는)와 가리키는 개체의 유형과 크기를 알려주는 유형이 있습니다.

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

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둘 다 증가된 후에.

포인터는 C의 다른 변수와 마찬가지로 기본적으로 하나 이상의 연결된 비트로 표현될 수 있는 비트 집합입니다.unsigned char값(다른 유형의 이동 가능과 마찬가지로)sizeof(some_variable)의 수를 나타냅니다.unsigned char값)을 참조해 주세요.포인터를 다른 변수와 다르게 만드는 것은 C 컴파일러가 포인터의 비트를 어떤 식으로든 변수가 저장될 수 있는 장소를 식별하는 것으로 해석한다는 것입니다.C에서는 다른 일부 언어와는 달리 여러 변수에 대한 공간을 요청한 후 해당 집합의 값으로 포인터를 해당 집합 내의 다른 변수에 대한 포인터로 변환할 수 있습니다.

많은 컴파일러가 실제 머신 주소를 저장하는 비트를 사용하여 포인터를 구현하지만, 이것이 유일한 구현은 아닙니다.구현은 프로그램이 사용하고 있는 모든 메모리 오브젝트(변수 세트)의 하드웨어 주소와 할당된 크기를 나열하는 사용자 코드에 접근할 수 없는 하나의 어레이를 유지할 수 있으며 각 포인터는 인덱스의 오프셋과 함께 하나의 어레이에 인덱스를 포함할 수 있습니다.이러한 설계에 의해, 시스템은, 소유하고 있는 메모리상에서 동작하는 코드를 제한할 수 있을 뿐만 아니라, 메모리 아이템에의 포인터가 실수로 다른 메모리 아이템에의 포인터로 변환되는 일이 없어집니다(하드웨어 주소를 사용하는 시스템에서는,foo그리고.bar메모리에 연속적으로 격납되는 10개의 항목의 배열로, 의 "실행" 항목에 대한 포인터입니다.foo대신 첫 번째 항목을 가리킬 수 있다bar단, 각 포인터가 오브젝트 ID 및 오프셋인 시스템에서는 코드가 포인터의 인덱스를 작성하려고 하면 시스템이 트랩할 수 있습니다.foo할당 범위를 벗어납니다).이러한 시스템에서는 포인터와 관련된 물리 주소를 이동할 수 있기 때문에 메모리 플래그멘테이션 문제를 제거할 수도 있습니다.

포인터는 다소 추상적이지만 완전히 표준 규격에 준거한 C 컴파일러가 가비지 컬렉터를 구현할 수 있을 만큼 추상적이지 않습니다.C 컴파일러는 포인터를 포함한 모든 변수가 일련의 것으로 표현되는 것을 지정합니다.unsigned char가치.어떤 변수든 그것을 일련의 숫자로 분해하고 나중에 그 숫자의 시퀀스를 원래 유형의 변수로 변환할 수 있습니다.그 결과, 프로그램이 할 수고를 덜 수 있게 됩니다.calloc일부 스토리지(포인터를 포인터로 이동), 거기에 저장, 포인터를 일련의 바이트로 분해하여 화면에 표시한 다음 모든 참조를 지웁니다.프로그램이 키보드로부터 몇 개의 숫자를 받아들여 그것들을 포인터로 재구성하고, 그 포인터로부터 데이터를 읽어내려고 하면, 그리고 사용자가 이전에 표시한 것과 같은 숫자를 입력했을 경우, 프로그램은 저장된 데이터를 출력해야 합니다.calloc'에드메모리'사용자가 표시된 숫자의 복사본을 만들었는지 여부를 컴퓨터가 알 수 있는 방법은 없기 때문에, 컴퓨터는 앞서 말한 메모리에 나중에 액세스할 수 있는지 여부를 알 수 없을 것입니다.

포인터는 C/C++에서 네이티브로 사용할 수 있는 변수 유형으로 메모리 주소를 포함합니다.다른 변수와 마찬가지로 자체 주소를 가지며 메모리를 사용합니다(플랫폼에 따라 다릅니다).

혼동의 결과로 볼 수 있는 문제 중 하나는 단순히 포인터를 값으로 전달함으로써 함수 내에서 참조를 변경하려는 것입니다.이렇게 하면 함수 범위에서 포인터의 복사본이 생성되고 이 새 포인터가 "포인팅"되는 위치를 변경해도 함수를 호출한 범위에서 포인터의 참조가 변경되지 않습니다.함수 내에서 실제 포인터를 수정하려면 보통 포인터에 포인터를 전달합니다.

포인터는 메모리 위치 주소(일반적으로 다른 변수의 메모리 주소)를 유지하는 데 사용되는 다른 변수일 뿐입니다.

이렇게 보시면 됩니다.포인터는 주소 지정 가능한 메모리 공간의 주소를 나타내는 값입니다.

포인터는 보통 다른 변수의 메모리 주소를 포함할 수 있는 다른 변수일 뿐입니다.변수인 포인터에는 메모리 주소도 있습니다.

포인터를 이해하기 전에 오브젝트를 이해할 필요가 있습니다.오브젝트는 존재하는 엔티티로 주소라고 불리는 위치 지정자를 가집니다.포인터는 의 다른 변수와 마찬가지로 변수일 뿐입니다.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`

포인터는 현재 참조하고 있는 오브젝트의 종류에 따라 분류됩니다.이 정보가 중요한 유일한 부분은 개체의 크기입니다.

모든 개체는 작업을 지원합니다.&(address of). 포인터 오브젝트 유형으로 오브젝트의 위치 지정자(주소)를 가져옵니다.이렇게 하면 명명법을 둘러싼 혼동을 줄일 수 있습니다.이렇게 하면 전화하는 것이 타당하기 때문입니다.&결과 유형이 개체 유형의 포인터인 포인터가 아닌 개체의 작업으로 사용됩니다.

비고 이 설명 내내 나는 기억의 개념을 생략했다.

주소는 고정 크기 저장소의 일부(일반적으로 각 바이트)를 정수로 식별하기 위해 사용됩니다.이것은 엄밀하게는 바이트 주소라고 불리며 ISO C에서도 사용됩니다.예를 들어 각 비트에 대해 주소를 구성하는 다른 방법이 있을 수 있습니다.단, 바이트 주소만 너무 자주 사용되기 때문에 "바이트"는 생략됩니다.

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

특정 유형을 가진 것으로 해석될 때 사물의 내용에 대한 정확한 의미

(저에 의해 강조됩니다.)단, C에는 이러한 「주소 타입」이 없습니다.

포인터가 동일하지 않습니다.포인터는 C언어의 한 종류입니다.몇 가지 다른 포인터 유형이 있습니다.그들은 언어의 동일한 규칙 세트를 반드시 준수하지는 않는다. 예를 들어, 다음과 같은 효과++활자 값으로int*대.char*.

C 의 값은 포인터 타입일 수 있습니다.이를 포인터 값이라고 합니다.명확하게 하기 위해 포인터 값은 C 언어의 포인터가 아닙니다.그러나 우리는 그것들을 함께 섞는 것에 익숙하다. 왜냐하면 C에서는 그것이 애매할 것 같지 않기 때문이다: 우리가 식을 호출하면pC의 이름 있는 유형은 에 의해 표현되지 않고 type-name 또는 typedef-name에 의해 표현되기 때문에 "pointinter"로서 이것은 단순히 포인터 값일 뿐 유형이 아닙니다.

다른 몇 가지는 미묘합니다.C 사용자로서 우선 알아야 할 것은object다음을 의미합니다.

실행 환경에서 데이터 스토리지 영역. 내용이 값을 나타낼 수 있습니다.

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

표준에 의해 규범적으로 정의된 "변수"는 없다(실제로 규범 텍스트에서 ISO C에 의해 명사로 사용되지 않는다).그러나 비공식적으로 우리는 다른 언어들이 그러하듯이 객체를 변수라고 부른다.(그러나 C++에서는 변수가 규범적으로 참조 유형일 수 있지만 객체가 아니다.)point object 또는 point inter variable이라는 문구는 위와 같이 "point value"로 취급되며 약간의 차이가 있을 수 있습니다(또 하나의 예는 "array"입니다).

포인터는 타입이고 주소는 사실상 C에서 타입이 아니기 때문에 포인터 값은 대략 주소를 포함합니다.포인터 타입의 식에서는, 예를 들면 주소를 생성할 수 있습니다.

ISO C11 6.5.2.3

3 단항&연산자는 오퍼랜드의 주소를 제공합니다.

이 문구는 WG14/N1256(ISO C99:TC3. C99에는

3 단항&연산자는 오퍼랜드의 주소를 반환합니다.

그것은 위원회의 의견을 반영한다: 주소는 단항이 반환하는 포인터 값이 아니다.&교환입니다.

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

ISO C11 6.6

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

ISO C++11 5.19

3... 주소 상수식은 정적 저장 기간이 있는 객체의 주소, 함수의 주소, 늘 포인터 값 또는 유형의 prvalue 코어 상수 표현식입니다.std::nullptr_t. ...

(최근의 C++ 표준 초안에서는 다른 표현을 사용하고 있기 때문에, 이 문제는 없습니다).

실제로 C의 "주소 상수"와 C++의 "주소 상수 식"은 모두 포인터 유형(또는 적어도 C++11 이후의 "포인트와 같은" 유형)의 상수 표현입니다.

그리고 내장된 유니언리는&연산자는 C 및 C++에서 "address-of"로 불리며 마찬가지로std::addressof는 C++11에서 도입되었습니다.

이러한 이름은 오해를 불러일으킬 수 있습니다.결과 표현식은 포인터 유형이기 때문에 결과가 주소가 아니라 주소를 포함하거나 반환하는 것으로 해석됩니다.

「주소가 무엇인지 모르는 사람에게 혼란을 주기 때문에」라고 쓰여져 있습니다.또, 주소의 의미를 알면, 혼란스럽지 않습니다.이론적으로 포인터는 다른 것을 가리키는 변수이며, 실질적으로 그것이 가리키는 변수의 주소인 주소를 보유하고 있습니다.나는 왜 이 사실을 숨겨야 하는지 모르겠다. 그것은 로켓 과학이 아니다.포인터를 이해하면 컴퓨터가 어떻게 작동하는지 이해하기 위해 한 발짝 더 다가설 수 있습니다.어서요!

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.

Simply to say pointers are actually offset part of the segmentation mechanism which translate to Linear Address after segmentation and then to Physical address after paging. Physical Addresses are actually addressed from you ram.

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

ReferenceURL : https://stackoverflow.com/questions/15151377/what-exactly-is-a-c-pointer-if-not-a-memory-address

반응형