포인터(컴퓨터 프로그래밍)
Pointer (computer programming)Donald Knuth, Structured Programming, with go to Statements[1]
컴퓨터 과학에서 포인터는 메모리 주소를 저장하는 많은 프로그래밍 언어의 객체다.이것은 컴퓨터 메모리에 위치한 또 다른 값일 수도 있고, 경우에 따라서는 메모리 매핑된 컴퓨터 하드웨어의 값일 수도 있다.포인터는 메모리의 위치를 참조하며, 해당 위치에 저장된 값을 얻는 것을 포인터를 비참조하는 것으로 알려져 있다.유사하게, 책의 색인에서 페이지 번호는 해당 페이지의 포인터로 간주될 수 있다; 그러한 포인터는 주어진 페이지 번호로 페이지를 넘기고 그 페이지에서 찾은 텍스트를 읽음으로써 폐기될 수 있다.포인터 변수의 실제 형식과 내용은 기본 컴퓨터 구조에 따라 달라진다.
포인터를 사용하면 반복적인 작업(예: 문자열, 룩업 테이블, 제어 테이블 및 트리 구조)의 성능을 크게 향상시킬 수 있다.특히 포인터들이 가리키는 데이터를 복사하고 접근하는 것보다 포인터들을 복사하고 폐기하는 것이 시공간적으로 훨씬 저렴할 때가 많다.
또한 포인터는 절차 프로그래밍에서 호출된 서브루틴에 대한 진입점 주소와 동적 링크 라이브러리(DLLs)에 대한 런타임에 대한 주소를 보유하는 데 사용된다.객체 지향 프로그래밍에서는 바인딩 메서드에 함수의 포인터가 사용되며, 종종 가상 메서드 테이블을 사용한다.
포인터는 보다 추상적인 참조 데이터 유형의 단순하고 구체적인 구현이다.몇몇 언어, 특히 낮은 수준의 언어는 어떤 종류의 포인터를 지원하지만, 어떤 언어들은 다른 언어들보다 사용에 더 많은 제약을 가지고 있다.반면"포인터"참조에 대해 언급할 일반적으로 이용되어 왔다, 더 제대로의 인터페이스가 명확하게 마술 쿠키나 capabil에 반대하는 포인터(산술적으로만 .mw-parser-output .vanchor>을 통해;:target~.vanchor-text{background-color:#b1d2ff}포인터 산술)기억 주소로, 조종당할 수 있도록 데이터 구조에 적용된다.Ity은 다음과 같은 허용하지 않는다.[표창 필요한]포인터는 메모리 주소에 대한 보호되는 액세스와 보호되지 않는 액세스를 모두 허용하기 때문에 특히 후자의 경우 포인터를 사용하는 것과 관련된 위험이 있다.원시 포인터들은 종종 정수와 유사한 형식으로 저장되지만, 값이 유효한 메모리 주소가 아닌 포인터를 폐기하거나 "찾아보기"를 시도하면 프로그램이 중단될 수 있다(또는 잘못된 데이터를 포함).이러한 잠재적 문제를 완화하기 위해, 포인터들은 유형 안전성의 문제로서, 비록 기초적인 표현이 정수라 할지라도 그들이 가리키는 데이터 유형에 의해 매개되는 별도의 유형으로 간주된다.포인터 변수가 유효한 메모리 주소와 프로세서가 처리할 수 있는 숫자 범위 내에 모두 포함된 값을 포함하는지 확인하기 위해 다른 조치(예: 검증 및 경계 검사)도 취할 수 있다.
역사
1955년 소련의 컴퓨터 과학자 카테리나 유셴코는 포인터와 유사하게 가장 높은 계급의 간접 주소와 주소를 가능하게 하는 주소 프로그래밍 언어를 발명했다.이 언어는 소비에트 연방 컴퓨터에서 널리 사용되었다.그러나 소련 밖에서는 알려지지 않았으며, 보통 해롤드 로슨은 1964년에 포인터 발명의 공로를 인정받았다.[2]2000년에, 로슨은 IEEE "[f]or 포인터 변수를 발명하여 이 개념을 PL/I에 도입함으로써, 처음으로 범용 고수준 언어로 링크된 목록을 유연하게 다룰 수 있는 기능을 제공했다."[3]개념에 대한 그의 논문은 1967년 6월 CACM의 "PL/I List Processing"에 실렸다.옥스퍼드 영어 사전에 따르면 시스템 개발 공사의 기술 비망록에서 단어 포인터가 스택 포인터로 인쇄에 처음 등장했다.
형식 설명
데이터 원시(또는 그냥 원시)는 하나의 메모리 액세스를 사용하여 컴퓨터 메모리에서 읽거나 쓸 수 있는 기준(예: 바이트와 단어 모두 원시 요소)이다.
데이터 집합(또는 단지 집합)은 논리적으로 연속적이며 하나의 기준점으로 집합적으로 보이는 원시적 양의 그룹이다(예를 들어 집합은 3개의 논리적으로 연속된 바이트일 수 있으며, 값은 공간 내 점의 3개의 좌표를 나타낼 수 있다.집합이 완전히 같은 종류의 원시적인 것으로 구성되었을 때 집합은 배열이라고 할 수 있다. 어떤 의미에서 원시적인 다중 바이트 단어는 바이트 배열이며, 어떤 프로그램들은 이런 방식으로 단어를 사용한다.
이러한 정의의 맥락에서 바이트는 가장 작은 원시 바이트로, 각 메모리 주소는 다른 바이트를 지정한다.기준점의 초기 바이트의 메모리 주소는 전체 기준점의 메모리 주소(또는 기본 메모리 주소)로 간주된다.
메모리 포인터(또는 그냥 포인터)는 원시적인 것으로, 그 값은 메모리 주소로 사용되도록 되어 있다. 포인터(pointer)는 메모리 주소를 가리킨다고 한다.또한 포인터의 값이 기준점의 메모리 주소일 때 포인터가 기준점[메모리 내]을 가리킨다고 한다.
보다 일반적으로 포인터는 일종의 참고 자료로, 포인터가 메모리의 어딘가에 저장된 기준점을 참조한다고 한다. 그 기준점을 얻는 것은 포인터를 무시하는 것이다.포인터를 다른 종류의 참조와 구분하는 특징은 포인터의 값이 메모리 어드레스로 해석되도록 되어 있다는 것인데, 이것은 다소 낮은 수준의 개념이다.
참고문헌은 양방향 수준 역할을 한다.포인터의 값은 계산에 사용할 메모리 주소(즉, 기준)를 결정한다.양방향은 알고리즘의 기본적인 측면이기 때문에, 포인터는 프로그래밍 언어에서 기본 데이터 유형으로 표현되는 경우가 많다; 정적으로(또는 강하게) 타입 프로그래밍 언어에서 포인터의 유형이 포인터가 가리키는 기준점의 유형을 결정한다.
건축적 뿌리
포인터는 대부분의 현대 아키텍처가 제공하는 주소 지정 기능 위에 매우 얇은 추상화다.가장 간단한 체계에서, 주소 또는 숫자 인덱스는 시스템의 각 메모리 단위에 할당되며, 여기서 단위는 전형적으로 바이트 주소 또는 단어로 구성되며, 아키텍처가 바이트 주소인지 워드 주소 지정인지에 따라 모든 메모리를 효과적으로 매우 큰 배열로 변환한다.그런 다음 시스템은 지정된 주소에서 메모리 장치에 저장된 값을 검색하는 연산도 제공할 것이다(일반적으로 기계의 일반 목적 레지스터를 활용한다).
일반적인 경우 포인터는 시스템에 메모리 단위가 있는 것보다 더 많은 주소를 저장할 수 있을 만큼 충분히 크다.이것은 충분한 메모리가 설치되지 않았거나(즉, 사용 가능한 메모리의 범위를 벗어난) 아키텍처가 그러한 주소를 지원하지 않기 때문에 프로그램이 메모리의 어떤 단위도 아닌 주소에 접근하려고 시도할 가능성을 도입한다.첫 번째 경우는 Intel x86 아키텍처와 같은 특정 플랫폼에서 분할 결함(segfault)이라고 불릴 수 있다.두 번째 사례는 AMD64의 현재 구현에서 가능하다. 여기서 포인터의 길이는 64비트이고 주소는 48비트까지만 확장된다.포인터는 특정 규칙(캐논어 주소)을 준수해야 하므로 비캐논 포인터가 참조되지 않으면 프로세서가 일반적인 보호 오류를 발생시킨다.
반면에, 어떤 시스템들은 주소보다 더 많은 메모리 단위를 가지고 있다.이 경우 메모리 분할이나 페이징과 같은 보다 복잡한 체계를 사용하여 다른 시간에 메모리의 다른 부분을 사용한다.x86 아키텍처의 마지막 형성은 PAE 페이징 메커니즘을 통해 32비트 선형 주소 공간에 매핑된 물리적 메모리 주소의 최대 36비트를 지원한다.따라서 한 번에 가능한 총 메모리의 1/16만 액세스할 수 있다.동일한 컴퓨터 제품군의 또 다른 예는 16비트 보호 모드인 80286 프로세서를 사용했는데, 이 프로세서는 16MB의 물리적 메모리만 지원하지만, 최대 1GB의 가상 메모리에 액세스할 수 있었지만, 16비트 주소와 세그먼트 레지스터의 결합으로 인해 하나의 데이터 구조에서 64KB 이상에 접근하는 것이 번거로웠다.
일관된 인터페이스를 제공하기 위해, 일부 아키텍처는 메모리 매핑된 I/O를 제공하는 반면, 다른 아키텍처는 컴퓨터에 있는 다른 장치의 장치 레지스터를 가리키도록 한다.파일 오프셋, 배열 인덱스, 원격 객체 참조와 같은 유사한 개념들이 존재하며, 다른 유형의 객체에 대한 주소와 동일한 목적을 제공한다.
사용하다
PL/I, C, C++, Pascal, FreeB 등의 언어에서 포인터들은 제한 없이 직접 지원된다.ASIC, 그리고 대부분의 어셈블리 언어로 암시적으로.그것들은 주로 참조를 구성하는데 사용되며, 이는 프로그램의 다른 부분들 간에 데이터를 전달하기 뿐만 아니라 거의 모든 데이터 구조를 구성하는데 기본적이다.
리스트에 크게 의존하는 기능 프로그래밍 언어에서 데이터 참조는 cons와 같은 원시 구성물과 cdr과 상응하는 요소들을 사용하여 추상적으로 관리된다. cs-cell의 첫 번째와 두 번째 구성요소에 대한 전문화된 포인터로 생각할 수 있다.이것은 기능 프로그래밍의 관용적인 "유행"의 일부를 발생시킨다.이러한 합의목록의 데이터를 구조화함으로써, 이들 언어는 예를 들어, 목록 목록의 머리와 꼬리 요소에 재귀적으로 접근함으로써, 데이터를 구축하고 처리하기 위한 재귀적 수단을 용이하게 한다(예: "cdr의 cdr의 자동차를 가져간다").이와는 대조적으로, 메모리 주소 배열의 일부 근사치에서 포인터 비참조를 기반으로 하는 메모리 관리는 변수를 데이터를 반드시 할당할 수 있는 슬롯으로 취급하는 것을 용이하게 한다.
어레이를 처리할 때, 일반적으로 중요 조회 연산에는 어레이에서 원하는 데이터 요소에 대한 포인터를 구성하는 주소 계산이라는 단계가 포함된다.링크된 목록과 같은 다른 데이터 구조에서 포인터는 구조의 한 부분을 다른 구조와 명시적으로 연결하기 위한 참조로 사용된다.
포인터는 참조로 매개 변수를 전달하는 데 사용된다.이것은 프로그래머가 매개 변수에 대한 함수의 수정을 함수의 호출자에게 보이기를 원하는 경우에 유용하다.이것은 함수에서 여러 값을 반환할 때도 유용하다.
포인터는 또한 메모리의 동적 변수와 배열을 할당하고 할당을 해제하는 데 사용될 수 있다.변수는 그 목적을 다한 후에 중복되는 경우가 많으므로, 그것을 유지하는 것은 기억력의 낭비이므로, 더 이상 필요하지 않을 때는 (원래 포인터 참조를 사용하여) 할당을 해제하는 것이 좋다.그렇지 않으면 메모리 누수가 발생할 수 있다(사용 가능한 여유 메모리가 점진적으로 또는 심각한 경우에는 수많은 중복 메모리 블록의 누적으로 인해 빠르게 감소한다).
C 포인터
인트로 *삐걱삐걱거리다;
이것이 선언하다.ptr
다음 유형의 개체의 식별자로 사용:
- 활자의 물체를 가리키는 포인터
int
이것은 보통 더 간결하게 "라고 언급된다.ptr
에 대한 포인터다int
."
C 언어는 자동 저장 기간 객체에 대한 암묵적 초기화를 명시하지 않기 때문에,[5] 종종 다음 주소를 확인할 수 있도록 주의해야 한다.ptr
포인트는 유효하다. 따라서 포인터를 Null 포인터 값으로 명시적으로 초기화해야 한다는 제안이 종종 제시되는데, 이는 전통적으로 표준화된 매크로와 함께 C에 지정되어 있다.NULL
:[6]
인트로 *삐걱삐걱거리다 = NULL;
C에서 null 포인터를 폐기하면 정의되지 않은 동작이 발생하며 이는 치명적인 결과를 초래할 수 있다.[7]그러나 대부분의 구현은[citation needed] 일반적으로 분할 결함으로 인해 해당 프로그램의 실행을 중지할 뿐이다.
그러나 불필요하게 포인터를 초기화하면 프로그램 분석을 방해해 버그를 숨길 수 있다.
어떤 경우든 포인터가 선언되면 다음 논리적 단계는 포인터가 다음을 가리키는 것이다.
인트로 a = 5; 인트로 *삐걱삐걱거리다 = NULL; 삐걱삐걱거리다 = , &a;
이것은 주소의 값을 할당한다.a
로ptr
. 예를 들면 다음과 같다.a
0x8130의 메모리 위치에 저장되며, 그 다음 값은ptr
과제 후에 0x8130이 될 것이다.포인터를 폐기하려면 별표를 다시 사용하십시오.
*삐걱삐걱거리다 = 8;
의 내용을 가져간다는 뜻이다.ptr
(이것은 0x8130), 메모리에 주소를 두고 그 값을 8로 설정하는 "message"이다.만약a
나중에 다시 접속하면, 새로운 가치는 8이 될 것이다.
기억을 직접 검사할 경우 이 예가 더 명확해질 수 있다.라고 가정하다.a
메모리의 주소 0x8130에 위치하며ptr
0x8134에서, 또한 int의 폭이 32비트인 32비트 기계라고 가정한다.다음은 다음 코드 조각이 실행된 후 메모리에 저장되는 사항이다.
인트로 a = 5; 인트로 *삐걱삐걱거리다 = NULL;
주소 내용물 0x8130 0x00000005 0x8134 0x00000000
(여기 표시된 NULL 포인터는 0x0000000000)주소를 할당하여a
로ptr
:
삐걱삐걱거리다 = , &a;
다음과 같은 메모리 값을 산출한다.
주소 내용물 0x8130 0x00000005 0x8134 0x00008130
그런 다음 비참조를 통해ptr
코딩:
*삐걱삐걱거리다 = 8;
컴퓨터는 의 내용을 가져갈 것이다.ptr
(즉, 0x8130), 해당 주소를 '지정'하고 8을 해당 위치에 할당하여 다음과 같은 메모리를 산출한다.
주소 내용물 0x8130 0x00000008 0x8134 0x00008130
확실히, 접근하기a
이전의 지침이 의 내용을 수정했기 때문에 8의 값을 산출할 것이다.a
포인터로ptr
.
데이터 구조에서 사용
목록, 대기열, 나무와 같은 데이터 구조를 설정할 때는 구조가 구현되고 제어되는 방법을 관리하는 데 도움이 되는 포인터가 필요하다.대표적인 포인터의 예로는 시작 포인터, 끝 포인터, 스택 포인터가 있다.이러한 포인터는 절대(가상 메모리의 실제 물리적 주소 또는 가상 주소) 또는 상대(일반적으로 전체 주소보다 적은 비트를 사용하지만 일반적으로 해결하려면 하나의 추가 산술 연산이 필요한 절대 시작 주소("베이스")로부터의 오프셋)이 될 수 있다.
상대적 주소는 수동 메모리 분할의 한 형태로, 많은 장단점을 공유한다.16비트, 부호 없는 정수를 포함하는 2바이트 오프셋을 사용하여 데이터 구조의 최대 64KiB(2바이트16)에 대한 상대적 주소 지정을 제공할 수 있다.이는 주소가 반단어, 워드 또는 이중어 경계로 정렬될 수밖에 없는 경우 128, 256 또는 512 KiB까지 쉽게 확장할 수 있다(단, 기본 어드레스에 추가되기 전에 오프셋을 2, 4, 8의 비율로 조정하기 위해 1, 2 또는 3비트의 추가 "좌측 이동" 비트 연산 필요).일반적으로, 그러한 계획은 많은 문제가 되고, 프로그래머의 편의를 위해 절대 주소(그리고 그 기초는 평평한 주소 공간)를 선호한다.
문자의 16진수 ASCII 값(예: X'29')과 같은 1바이트 오프셋을 사용하여 배열(예: X'01')에서 대체 정수 값(또는 지수)을 가리킬 수 있다.이런 식으로 문자를 '원시 데이터'에서 사용 가능한 순차 인덱스로, 그리고 조회 테이블 없이 절대 주소로 매우 효율적으로 번역할 수 있다.
C 배열
C에서 배열 인덱싱은 포인터 산술의 관점에서 공식적으로 정의된다. 즉, 언어 규격은 다음을 요구한다.array[i]
와 동등하다*(array + i)
따라서 [8]C에서 배열은 연속적인 메모리 영역에 대한 포인터(격차 없음)로 생각할 수 있으며,[8] 어레이에 접근하기 위한 구문은 포인터 참조에 사용할 수 있는 것과 동일하다.예를 들어 배열array
다음과 같은 방법으로 선언하고 사용할 수 있다.
인트로 배열하다[5]; /* 연속 정수 5개 선언 */ 인트로 *삐걱삐걱거리다 = 배열하다; /* 어레이를 포인터로 사용할 수 있음 */ 삐걱삐걱거리다[0] = 1; /* 포인터들은 어레이 구문 */로 색인화할 수 있다. *(배열하다 + 1) = 2; /* 포인터 구문과 함께 배열을 참조 해제할 수 있음 */ *(1 + 배열하다) = 2; /* 포인터 추가는 역순 */ 배열하다[2] = 4; /* 첨자 연산자는 역순 */
이렇게 하면 다섯 개의 정수로 이루어진 블록이 할당되고 블록의 이름이 지정된다.array
블록에 대한 포인터 역할을 하는.포인터들의 또 다른 일반적인 용도는 어레이로 사용할 수 있는 요청된 크기의 연속된 메모리 블록을 반환하는 말로크로부터 동적으로 할당된 메모리를 가리키는 것이다.
어레이와 포인터의 대부분의 연산자는 동일하지만, 그 결과sizeof
운영자가 다르다.이 예에서는sizeof(array)
에 대해 평가할 것이다.5*sizeof(int)
(배열 크기),sizeof(ptr)
에 대해 평가할 것이다.sizeof(int*)
, 포인터 자체의 크기.
배열의 기본값은 다음과 같이 선언할 수 있다.
인트로 배열하다[5] = {2, 4, 3, 1, 5};
만약array
32비트 리틀엔디안 머신의 주소 0x1000에서 시작하는 메모리에 위치하면 메모리는 다음을 포함한다(주소와 같이 16진수로 표시됨).
0 1 2 3 1000 2 0 0 0 1004 4 0 0 0 1008 3 0 0 0 100C 1 0 0 0 1010 5 0 0 0
여기에 표시된 정수는 2, 4, 3, 1, 5이다.이 다섯 개의 정수는 각각 32비트(4바이트)를 차지하며, 가장 작은 바이트(이것은 리틀 엔디안 CPU 아키텍처)가 먼저 저장되며, 주소 0x1000에서 시작하여 연속적으로 저장된다.
포인터가 있는 C의 구문은 다음과 같다.
array
0x1000을 의미한다.array + 1
0x1004: "1"의 크기를 추가하는 "+ 1"의 의미int
, 즉 4바이트.*array
의 내용을 무시하는 수단array
. 내용을 메모리 주소(0x1000)로 간주하여 해당 위치(0x0002)에서 값을 조회한다.array[i]
평균 요소 번호i
, 0 기반,array
로 번역된.*(array + i)
.
마지막 예는 의 내용에 접근하는 방법이다.array
. 분해:
array + i
(i)th 요소의 메모리 위치array
, i=0에서 시작;*(array + i)
값에 액세스하기 위해 해당 메모리 주소를 가져다가 참조하지 마십시오.
C 연결 목록
아래는 C에서 링크된 목록의 정의 예다.
/* 빈 링크 목록이 NULL로 표시됨 * 또는 다른 Sentinel 값 */ #define BLEART_LIST NULL 구조상의 연결하다 { 공허하게 하다 *자료; /* 이 링크의 데이터 */ 구조상의 연결하다 *다음에; /* 다음 링크, 비어있는_LIST가 없는 경우 */ };
이 포인터-재귀적 정의는 기본적으로 Haskell 프로그래밍 언어의 참조-재귀적 정의와 동일하다.
자료 링크 a = 닐 단점 a (링크 a)
Nil
빈 목록이고Cons a (Link a)
타입의 양세포다.a
다른 종류의 링크와 함께a
.
그러나 참조가 있는 정의는 유형 확인되며 잠재적으로 혼란스러운 신호 값을 사용하지 않는다.이 때문에 C의 데이터 구조는 보통 포장 기능을 통해 처리되는데, 이 기능들은 정확성을 주의 깊게 점검한다.
포인터를 사용한 Pass-by-address(주소별 통과)
포인터는 변수를 주소별로 전달하여 값을 변경할 수 있도록 하는 데 사용할 수 있다.예를 들어 다음 C 코드를 고려하십시오.
/* int n의 복사본은 호출 코드에 영향을 주지 않고 함수 내에서 변경될 수 있음 */ 공허하게 하다 passByValue(인트로 n) { n = 12; } /* 대신 포인터 m이 전달된다.m이 가리키는 값의 복사본이 생성되지 않음 */ 공허하게 하다 passByAddress(인트로 *m) { *m = 14; } 인트로 본래의(공허하게 하다) { 인트로 x = 3; /* x 값 사본을 인수로 전달 */ passByValue(x); // 함수 내부에서 값이 변경되었지만 x는 여기서부터 여전히 3이다. /* x의 주소를 인수로 전달 */ passByAddress(, &x); // x는 실제로 함수에 의해 변경되었으며 현재 여기서 14와 같다. 돌아오다 0; }
동적 메모리 할당
일부 프로그램에서는 사용자가 무엇을 입력하느냐에 따라 필요한 메모리가 달라진다.그러한 경우 프로그래머는 동적으로 메모리를 할당할 필요가 있다.이것은 보통 변수가 저장되는 스택이 아닌 힙에 메모리를 할당함으로써 이루어진다(변수는 CPU 레지스터에도 저장될 수 있지만 그것은 또 다른 문제다).동적 메모리 할당은 포인터를 통해서만 할 수 있고, (공통 변수처럼) 이름을 줄 수 없다.
포인터는 동적으로 할당된 메모리 블록의 주소를 저장하고 관리하는 데 사용된다.이러한 블록은 데이터 객체 또는 객체 배열을 저장하는 데 사용된다.가장 구조화되고 객체 지향적인 언어는 객체들이 동적으로 할당되는 힙이나 프리 스토어라고 불리는 메모리 영역을 제공한다.
아래의 사례 C 코드는 구조 객체가 동적으로 할당되고 참조되는 방법을 보여준다.표준 C 라이브러리는 힙에서 메모리 블록을 할당하는 기능을 제공한다.매개 변수로 할당할 개체의 크기를 잡아 개체 저장에 적합한 새로 할당된 메모리 블록에 포인터를 반환하거나 할당에 실패하면 null 포인터를 반환한다.
/* 부품 재고 항목 */ 구조상의 항목 { 인트로 id; /* 부품 번호 */ 마를 뜨다 * 이름을 붙이다; /* 부품 이름 */ 둥둥 뜨다 비용이 들다; /* 비용 */ }; /* 새 항목 개체 할당 및 초기화 */ 구조상의 항목 * make_make(경시하다 마를 뜨다 *이름을 붙이다) { 구조상의 항목 * 항목,; /* 새 Item 개체에 메모리 블록 할당 */ 항목, = 만록의(의 크기(구조상의 항목)); 만일 (항목, == NULL) 돌아오다 NULL; /* 새 항목 멤버 초기화 */ 암셋(항목,, 0, 의 크기(구조상의 항목)); 항목,->id = -1; 항목,->이름을 붙이다 = NULL; 항목,->비용이 들다 = 0.0; /* 새 항목 */에 이름 사본 저장 항목,->이름을 붙이다 = 만록의(끈적끈적하게 하다(이름을 붙이다) + 1); 만일 (항목,->이름을 붙이다 == NULL) { 무료의(항목,); 돌아오다 NULL; } 층층적인(항목,->이름을 붙이다, 이름을 붙이다); /* 새로 생성한 Item 개체 반환 */ 돌아오다 항목,; }
아래 코드는 메모리 객체가 동적으로 할당 해제되는 방식, 즉 힙 또는 프리 스토어로 반환되는 방식을 보여준다.표준 C 라이브러리는 이전에 할당된 메모리 블록의 할당을 해제하고 이를 다시 힙으로 되돌리는 기능을 제공한다.
/* Item 객체 할당 해제 */ 공허하게 하다 파괴_파괴(구조상의 항목 *항목,) { /* null 개체 포인터 확인 */ 만일 (항목, == NULL) 돌아오다; /* 항목 내에 저장된 이름 문자열의 할당 해제 */ 만일 (항목,->이름을 붙이다 != NULL) { 무료의(항목,->이름을 붙이다); 항목,->이름을 붙이다 = NULL; } /* Item 객체 자체의 할당 해제 */ 무료의(항목,); }
메모리 맵 하드웨어
일부 컴퓨팅 아키텍처에서 포인터는 메모리나 메모리 매핑된 장치를 직접 조작하는 데 사용될 수 있다.
마이크로컨트롤러를 프로그래밍할 때 포인터에 주소를 할당하는 것은 매우 귀중한 도구다.아래는 int 형식의 포인터를 선언하고 이 예에서 상수 0x7FFFF:
인트로 *hardware_address = (인트로 *)0x7FFF;
80년대 중반, PC의 비디오 기능에 접근하기 위해 BIOS를 사용하는 것은 느렸다.일반적으로 16진수 상수 0xB8000을 80개의 서명되지 않은 16비트 int 값 배열에 대한 포인터로 캐스팅하여 CGA 비디오 메모리에 직접 액세스하는 데 디스플레이 집약적 애플리케이션.각 값은 낮은 바이트의 ASCII 코드와 높은 바이트의 색상으로 구성되었다.따라서 'A'자를 5행, 2열은 청색에 밝은 흰색, 2열은 다음과 같은 코드를 쓰곤 했다.
#Define VID((부호화되지 않은 (*)[80])0xB8000) 공허하게 하다 foo(공허하게 하다) { VID[4][1] = 0x1F00 'A'; }
제어 테이블에서 사용
프로그램 흐름을 제어하기 위해 사용되는 제어 테이블은 일반적으로 포인터를 광범위하게 사용한다.예를 들어, 일반적으로 테이블 항목에 포함된 포인터는 동일한 테이블 항목에 정의된 특정 조건에 기초하여 실행할 서브루틴의 진입점을 고정하는 데 사용될 수 있다.그러나 포인터는 단순히 실제 주소 또는 주소 자체를 구성하는 다른 분리된 테이블 또는 관련 테이블로 인덱싱될 수 있다(사용 가능한 프로그래밍 언어 구조에 따라 다름).또한 초기 테이블 항목(루프 처리 시)을 가리키거나 일부 테이블 항목(스위치 또는 루프에서 "조기" 출구)을 건너뛰기 위해 사용할 수 있다.이 후자의 목적을 위해, "점자"는 단순히 테이블 항목 번호 그 자체일 수도 있고 간단한 산술에 의해 실제 주소로 변환될 수도 있다.
입력된 포인터 및 주조
많은 언어에서 포인터는 포인터가 가리키는 객체가 특정 유형을 갖는 추가적인 제약을 가지고 있다.예를 들어, 포인터는 정수를 가리키도록 선언될 수 있다. 그런 다음 언어는 프로그래머가 부동 소수점 번호와 같이 정수가 아닌 물체를 가리키지 못하도록 하여 일부 오류를 제거한다.
예를 들어, C에서
인트로 *돈; 마를 뜨다 *가방들;
money
정수 포인터가 될 것이고bags
문자 포인터가 될 거야다음은 GCC에서 "호환되지 않는 포인터 유형에서 할당"에 대한 컴파일러 경고를 발생시킬 것이다.
가방들 = 돈;
때문에money
그리고bags
다른 유형으로 선언되었다.컴파일러 경고를 억제하려면, 당신이 정말로 그것을 타이프로 지정하여 할당하기를 원한다는 것을 분명히 해야 한다.
가방들 = (마를 뜨다 *)돈;
'정수 포인터를 던져야 한다'는 뜻이지money
에 할당하다.bags
.
2005년 C 표준 초안은 한 유형에서 다른 유형으로 파생된 포인터를 주조하는 것이 두 유형의 정렬 정확도를 유지해야 한다고 요구한다([9]6.3.2.3 포인터, 파7):
마를 뜨다 *외적_외적 = "abcdef"; 인트로 *internal_data; internal_data = (인트로 *)외적_외적; // "결과 포인터"인 경우 정의되지 않은 동작 // 올바르게 정렬되지 않음"
포인터 산술을 허용하는 언어에서 포인터의 산술은 활자의 크기를 고려한다.예를 들어, 포인터에 정수 번호를 추가하면 해당 숫자의 곱인 형식 크기만큼 더 높은 주소를 가리키는 다른 포인터가 생성된다.이를 통해 위의 C 배열 예제에서 보듯이 특정 유형의 배열 요소 주소를 쉽게 계산할 수 있다.한 유형의 포인터를 다른 크기의 다른 유형에 캐스팅할 때 프로그래머는 포인터 산술 계산이 다르게 계산될 것으로 예상해야 한다.예를 들어, C에서 다음과 같은 경우money
어레이는 0x2000에서 시작되며sizeof(int)
4바이트인 반면sizeof(char)
1바이트, 그러면money + 1
0x2004를 가리키겠지만bags + 1
0x2001을 가리킬 것이다.주물의 다른 위험은 "넓은" 데이터가 "좁은" 위치(예: "좁은" 위치)에 기록될 때 데이터 손실을 포함한다.bags[0] = 65537;
)), 특히 서명된 값과 서명되지 않은 값에서 비트 전송 값과 비교 문제가 발생할 때 예상치 못한 결과.
일반적으로 어떤 깁스가 안전한지 컴파일 시간에 결정하는 것은 불가능하지만, 일부 언어는 런타임 유형 정보를 저장하여 이러한 위험한 깁스가 런타임에 유효한지 확인할 수 있다.다른 언어들은 단지 안전캐스트의 보수적인 근사치를 받아들일 뿐, 전혀 그렇지 않다.
포인터의 값
C와 C++에서는 포인터의 비교 결과가 정의되지 않는다.이러한 언어와 ELLVM에서 규칙은 "두 포인터가 동일한 주소를 가리킨다고 해서 그것들이 동일하다는 것을 의미하지 않으며 서로 교환하여 사용할 수 있다"는 뜻으로 해석되는데, 이는 포인터의 차이를 증명이라고 한다.[10]다음과 같은 정수 유형으로 주조하더라도uintptr_t
비교를 제공하고, 출연자 자체가 구현 정의된다.또한 바이트와 산술로 더 변환하면 포인터의 사용을 추적하려는 최적기가 사라지게 되는데, 이는 학술 연구에서 여전히 해명되고 있는 문제다.[11]
포인터를 안전하게 만들기
포인터가 프로그램이 정의되지 않을 수도 있는 물체에 접근을 시도하도록 허용하기 때문에, 포인터는 다양한 프로그래밍 오류의 원점이 될 수 있다.그러나 포인터가 없으면 프로그래밍 작업을 수행하기 어려울 정도로 포인터의 유용성이 크다.결과적으로, 많은 언어는 포인터 위험이라고도 불리는 함정의 일부 없이 포인터의 유용한 특징의 일부를 제공하도록 설계된 구조를 만들었다.이러한 맥락에서 (이 글에서 사용되는) 메모리를 직접 다루는 포인터를 스마트 포인터 또는 다른 변종과 대조적으로 원시 포인터라고 한다.
포인터의 한 가지 주요 문제점은 포인터를 숫자로 직접 조작할 수 있는 한 사용하지 않는 주소나 다른 용도로 사용되는 데이터를 가리키도록 할 수 있다는 점이다.대부분의 기능적 프로그래밍 언어와 자바와 같은 최근의 필수 언어를 포함한 많은 언어는 포인터를 일반적으로 단순히 참조라고 하는 보다 불투명한 유형의 참조로 대체하는데, 이것은 단지 객체를 지칭하는 데만 사용할 수 있고 숫자로 조작되지 않아 이러한 유형의 오류를 방지한다.어레이 인덱싱은 특별한 케이스로 처리된다.
주소가 할당되지 않은 포인터를 와일드 포인터라고 한다.초기화되지 않은 포인터를 사용하려는 시도는 초기 값이 유효한 주소가 아니거나 프로그램의 다른 부분을 손상시킬 수 있기 때문에 예기치 않은 동작을 유발할 수 있다.그 결과는 종종 분할 결함, 저장 위반 또는 와일드 분기(기능 포인터 또는 분기 주소로 사용되는 경우)가 된다.
명시적 메모리 할당이 있는 시스템에서는, 그것이 가리키는 메모리 영역을 디할당하여 매달린 포인터를 만들 수 있다.할당 해제된 메모리 영역이 할당 취소되기 전과 동일한 데이터를 포함할 수 있지만, 이전 코드에서 알 수 없는 관련 없는 코드로 재할당 및 덮어쓸 수 있기 때문에 이러한 유형의 포인터는 위험하고 미묘하다.가비지 수집 언어는 범위에 더 이상의 참조가 없을 때 자동으로 할당 해제되기 때문에 이러한 유형의 오류를 방지한다.
C++와 같은 일부 언어는 참조로 작용하는 것 외에 동적 메모리의 할당을 추적하기 위해 간단한 형태의 참조 카운트를 사용하는 스마트 포인터를 지원한다.물체가 일련의 스마트 포인터를 통해 간접적으로 자신을 지칭하는 참조 사이클이 없는 경우, 이는 매달려 있는 포인터와 메모리 누수의 가능성을 제거한다.델파이 문자열은 기본적으로 참조 카운트를 지원한다.
러스트 프로그래밍 언어는 가비지 수집에 의존하지 않고, 포인터 버그를 제거하기 위해 null 포인터의 선택적 유형을 중심으로 차용 체커, 포인터 수명 및 최적화를 도입한다.
특별한 종류의 포인터
가치에 의해 정의된 종류
Null 포인터
null 포인터에는 포인터가 유효한 개체를 참조하지 않음을 나타내기 위해 예약된 값이 있다.Null 포인터는 알 수 없는 길이의 목록 끝이나 일부 작업을 수행하지 못하는 등의 조건을 나타내기 위해 일상적으로 사용된다. 이러한 Null 포인터의 사용은 Nullable 유형 및 옵션 유형의 Nothing 값과 비교할 수 있다.
매달린 포인터
매달린 포인터란 유효한 물체를 가리키지 않는 포인터로, 결과적으로 프로그램이 충돌하거나 이상하게 동작할 수 있다.파스칼 또는 C 프로그래밍 언어에서, 특별히 초기화되지 않은 포인터는 메모리의 예측할 수 없는 주소를 가리킬 수 있다.
다음 예제 코드는 매달린 포인터를 보여준다.
인트로 펑크(공허하게 하다) { 마를 뜨다 *p1 = 만록의(의 크기(마를 뜨다)); /*(정의되지 않음) 힙의 일부 위치 값 */ 마를 뜨다 *p2; /* 매달린(직렬화) 포인터 */ *p1 = 'a'; /* malloc()가 NULL을 반환하지 않았다고 가정할 때, 이것은 괜찮다. */ *p2 = 'b'; /* 정의되지 않은 동작을 불러온다 */ }
여기,p2
메모리의 아무 곳이나 가리킬 수 있으므로 할당을 수행하십시오.*p2 = 'b';
알 수 없는 메모리 영역을 손상시키거나 분할 오류를 트리거할 수 있음.
야생가지
포인터를 프로그램의 진입점 주소 또는 아무것도 반환하지 않고 초기화되지 않거나 손상된 기능의 시작 주소로 사용하는 경우, 그럼에도 불구하고 호출이나 점프를 이 주소로 하면 "와일드 분기"가 발생했다고 한다.즉, 야생 가지는 야생(댕글링)인 함수 포인터다.
결과는 대개 예측할 수 없으며, 오차는 포인터가 "유효한" 주소인지 여부와 해당 주소에 유효한 지침(오코드)이 있는지 여부에 따라 몇 가지 다른 방법으로 나타날 수 있다.야생 지점의 탐지는 많은 증거가 이미 사전에 파괴되었거나 지점 위치에서 하나 이상의 부적절한 지시를 실행함으로써 가장 어렵고 좌절스러운 디버깅 연습 중 하나를 나타낼 수 있다.사용 가능하다면, 명령 집합 시뮬레이터는 보통 그것이 발효되기 전에 야생 가지를 감지할 수 있을 뿐만 아니라, 그것의 역사에 대한 완전하거나 부분적인 흔적을 제공할 수 있다.
구조로 정의된 종류
자동 포인터
자동상호 포인터란 포인터 자체의 주소에서 오프셋된 값으로 해석되는 포인터로서, 데이터 구조 자체의 일부를 가리키는 자동상호 포인터 멤버가 있는 경우, 자동상호 포인터 값을 업데이트할 필요 없이 데이터 구조를 메모리에 재배치할 수 있다..[12]
인용된 특허는 또한 같은 것을 의미하기 위해 자기 관계 포인터라는 용어를 사용한다.그러나 그 용어의 의미는 다른 방식으로 사용되어 왔다.
- 포인터 자체의 주소가 아닌 구조물의 주소로부터 간격띄우기를 의미한다.[citation needed]
- 자체 주소를 포함하는 포인터를 의미하며, 이는 메모리 임의 영역에서 서로 가리키는 데이터 구조의 컬렉션을 재구성하는 데 유용할 수 있다.[13]
기본 포인터
기본 포인터란 다른 포인터 값에서 오프셋인 포인터를 말한다.이것은 데이터 블록을 저장하고 로드하는 데 사용할 수 있으며, 블록 시작의 주소를 기본 포인터에 할당할 수 있다.[14]
사용 또는 데이터 형식으로 정의된 종류
다중 방향
일부 언어에서는 포인터가 다른 포인터를 참조할 수 있어 원래 값에 도달하기 위해 여러 개의 참조 취소 연산이 필요하다.각 단계의 방향성은 성능 비용을 추가할 수 있지만, 복잡한 데이터 구조에 대해 정확한 동작을 제공하기 위해 필요한 경우가 있다.예를 들어, C에서 링크된 리스트를 목록의 다음 요소에 대한 포인터를 포함하는 요소 측면에서 정의하는 것이 일반적이다.
구조상의 요소 { 구조상의 요소 *다음에; 인트로 가치를 매기다; }; 구조상의 요소 *머리 = NULL;
이 구현에서는 전체 목록의 대리인으로 목록의 첫 번째 요소에 대한 포인터를 사용한다.목록 시작 부분에 새 값이 추가되면head
새로운 요소를 가리키도록 변경해야 한다.C 인수는 항상 값을 통해 전달되기 때문에 이중 양방향으로 사용하면 삽입이 올바르게 구현될 수 있으며, 목록 앞부분의 삽입을 처리하기 위해 특수 사례 코드를 제거하는 바람직한 부작용이 있다.
// *head에 정렬된 목록을 지정하여 첫 번째에 요소 항목을 삽입 // 이전의 모든 요소가 더 작거나 같은 값을 갖는 위치. 공허하게 하다 삽입하다(구조상의 요소 **머리, 구조상의 요소 *항목,) { 구조상의 요소 **p; // p는 요소에 대한 포인터를 가리킨다. 을 위해 (p = 머리; *p != NULL; p = &(*p)->다음에) { 만일 (항목,->가치를 매기다 <= (*p)->가치를 매기다) 부숴뜨리다; } 항목,->다음에 = *p; *p = 항목,; } // 호출자가 다음 작업을 수행하십시오. 삽입하다(&머리, 항목,);
이 경우, 이 값이item
의 그것보다 적다head
, 발신자의 전화head
새 항목의 주소로 올바르게 업데이트됨.
기본 예는 C(및 C++)의 주함수에 대한 argv 인수에 있으며, 이 인수는 프로토타입에서 다음과 같이 제시된다.char **argv
—변수가argv
그 자체가 문자열 배열(배열 배열)에 대한 포인터여서*argv
0번째 문자열(프로그램 이름)에 대한 포인터로,**argv
0번째 문자열의 0번째 문자 입니다.
함수 포인터
일부 언어에서 포인터는 실행 가능한 코드를 참조할 수 있다. 즉, 기능, 방법 또는 절차를 가리킬 수 있다.함수 포인터는 호출할 함수의 주소를 저장한다.이 설비는 기능을 역동적으로 부르기 위해 사용될 수 있지만, 바이러스나 다른 악성 소프트웨어 작성자들이 즐겨 쓰는 기술인 경우가 많다.
인트로 합계를 내다(인트로 n1, 인트로 n2) { // 정수 값을 반환하는 두 개의 정수 매개변수로 함수 돌아오다 n1 + n2; } 인트로 본래의(공허하게 하다) { 인트로 a, b, x, y; 인트로 (*fp)(인트로, 인트로); // 합과 같은 함수를 가리킬 수 있는 함수 포인터 fp = &합계를 내다; // fp 이제 함수 합계를 가리킴 x = (*fp)(a, b); // 인수 a와 b를 사용하여 함수 합을 호출 y = 합계를 내다(a, b); // 인수 a와 b를 사용하여 함수 합을 호출 }
백 포인터
이중으로 연결된 목록 또는 트리 구조에서 요소에 고정된 백 포인터는 현재 요소를 참조하는 항목을 '뒤로 가리킨다'.이것들은 더 많은 메모리 사용을 희생하면서 탐색과 조작에 유용하다.
배열 인덱스를 사용한 시뮬레이션
인덱스를 사용하여 포인터 동작을 (보통 1차원) 배열로 시뮬레이션할 수 있다.
주로 포인터를 명시적으로 지원하지 않지만 어레이를 지원하는 언어의 경우 어레이는 전체 메모리 범위(특정 어레이의 범위 내)인 것처럼 생각하고 처리할 수 있으며, 모든 인덱스는 어셈블리 언어의 범용 레지스터(개별 바이트를 가리키는)와 동등하다고 생각할 수 있다.그러나 실제 값이 배열의 시작에 상대적이며 메모리의 절대 주소가 아니다.어레이가 연속적인 16메가바이트 문자 데이터 구조라고 가정하면, 개별 바이트(또는 어레이 내의 연속 바이트 문자열)는 31비트 미부호 정수를 시뮬레이션 포인터로 하여 어레이의 이름을 사용하여 직접 어드레싱 및 조작할 수 있다(이것은 위에 나온 C 어레이 예와 상당히 유사하다).포인터 산술은 실제 포인터 산술에 비해 최소한의 추가 오버헤드로 지수에서 추가 또는 빼서 시뮬레이션할 수 있다.
위의 기법과 함께 포인터(예: Java/JavaScript)를 전혀 지원하지 않는 다른 언어로 어떠한 기계 코드나 프로세서/언어의 중간(바이트 코드)도 시뮬레이션할 수 있는 적절한 명령 집합 시뮬레이터와 함께 이론적으로도 가능하다.이를 달성하기 위해, 바이너리 코드는 시뮬레이터가 동일한 어레이에 포함된 메모리 내에서 완전히 "읽고", 해석하고 작용하도록 초기에 어레이의 연속 바이트로 로드될 수 있다.필요한 경우 버퍼 오버플로 문제를 완전히 방지하기 위해 보통 컴파일러(또는 그렇지 않을 경우 시뮬레이터에서 손으로 코딩)에 대해 한계 확인을 수행할 수 있다.
다양한 프로그래밍 언어 지원
에이다
에이다님은 모든 포인터가 입력되고 안전한 타입의 변환만 허용되는 강력한 타입의 언어다.모든 포인터는 기본적으로 다음으로 초기화됨null
, 및 를 통해 데이터에 액세스하려는 모든 시도null
포인터가 예외를 발생시킨다.에이다의 포인터는 접속형이라고 불린다.에이다 83은 액세스 유형에 대한 산술을 허용하지 않았지만(많은 컴파일러 벤더가 비표준 기능으로 제공했지만), 에이다 95는 패키지를 통해 액세스 유형에 대한 "안전한" 산술을 지원한다.System.Storage_Elements
.
기본
Windows 플랫폼용 BASIC의 여러 이전 버전은 문자열 주소를 반환하는 STRPTR()과 변수 주소를 반환하는 VARPTR()를 지원했다.비주얼 베이직 5는 오브젝트 인터페이스의 주소를 반환하는 OBJPTR()과, ADDRESSOF 운영자가 함수의 주소를 반환하는 것도 지원했다.이 모든 유형의 정수는 정수지만, 그 값은 포인터 유형에 의해 보유되는 정수와 동일하다.
FreeB와 같은 BASIC의 새로운 방언들그러나 ASIC 또는 BlitzMax는 완전한 포인터 구현을 가지고 있다.In FreeBASIC, 산술 켜기ANY
포인터(C와 동일)void*
)은 마치 ...인 것처럼 취급된다.ANY
포인터는 바이트 폭이었다. ANY
포인터는 C와 같이 참조 해제할 수 없다.또, 사이사이의 캐스팅.ANY
다른 유형의 포인터는 경고를 생성하지 않는다.
어슴푸레한 로서 정수의 f = 257 어슴푸레한 로서 아무 것이나 삐걱삐걱거리다 g = @f 어슴푸레한 로서 정수의 삐걱삐걱거리다 i = g 주장하다(*i = 257) 주장하다( (g + 4) = (@f + 1) )
C와 C++
C와 C++ 포인터는 주소를 저장하는 변수로서 null일 수 있다.각 포인터에는 그것이 가리키는 유형이 있지만, 포인터 유형 사이에 자유롭게 캐스팅할 수 있다(그러나 함수 포인터와 객체 포인터 사이에는 캐스트할 수 없다)."void pointer"라고 불리는 특별한 포인터 유형은 어떤 (비기능) 물체를 가리키도록 허용하지만, 직접적으로 참조할 수 없다는 사실에 의해 제한된다(주조되어야 한다).비록 그 결과가 구현 정의되고 실제로 정의되지 않은 행동을 야기할 수 있지만, 주소 자체는 충분한 크기의 적분형식에 포인터를 던져 직접 조작될 수 있다. 반면, 이전의 C 표준은 충분히 클 것으로 보장된 적분형태를 가지고 있지 않았다. C99는 다음과 같이 명시한다.uintptr_t
typeedef 이름 정의<stdint.h>
, 그러나 구현이 그것을 제공할 필요는 없다.
C++는 C 포인터와 C 타입캐스팅을 완벽하게 지원한다.그것은 또한 컴파일 시간에 의도하지 않은 몇몇 위험한 캐스팅을 잡는 것을 돕기 위해 새로운 타입 캐스팅 운영자 그룹을 지원한다.C++11 이후, C++ 표준 라이브러리에서도 스마트 포인터를 제공한다.unique_ptr
,shared_ptr
그리고weak_ptr
원시 C 포인터의 안전한 대안으로 일부 상황에서 사용할 수 있는 것.C++는 또한 단순히 참조 또는 참조 유형이라고 불리는 포인터와는 상당히 다른 또 다른 형태의 참조를 지원한다.
포인터 산술, 즉 크기 비교뿐만 아니라 산술 연산을 통해 포인터 목표 주소를 수정할 수 있는 기능은 언어 표준에 의해 단일 배열 개체의 범위 내에 있도록 제한되며(또는 그 직후) 그렇지 않으면 정의되지 않은 동작을 발생시킨다.포인터를 추가하거나 포인터에서 빼면 데이터 유형 크기의 배수로 포인터가 이동한다.예를 들어 포인터에 4바이트 정수 값에 1을 추가하면 포인터의 포인트 투 바이트 주소가 4씩 증가한다.이것은 연속적인 정수의 배열에서 다음 원소를 가리키도록 포인터를 증가시키는 효과가 있다. 이는 종종 의도된 결과인 것이다.포인터 산술을 수행할 수 없음void
gcc와 다른 컴파일러가 바이트 산술을 수행하지만 보이드 타입은 크기가 없으므로 포인터를 추가할 수 없다.void*
비표준연장으로서, 마치 비표준연장으로 취급한다.char *
.
포인터 산술은 프로그래머에게 실제 오프셋(바이트 단위) 대신 필요한 요소 수를 더하고 빼는 단일한 유형의 처리 방법을 제공한다. (점자 산술:char *
포인터는 바이트 오프셋을 사용한다.sizeof(char)
정의로 1이다.)특히 C 정의는 구문을 명시적으로 선언한다.a[n]
, 어느 것이 더 좋은가?n
배열의 -번째 요소a
와 동일하다.*(a + n)
, 어느 것이 가리키는 원소의 내용이다.a + n
…라는 것을 암시하는 말이다.n[a]
와 같다a[n]
, 그리고 글씨를 쓸 수 있다, 예를 들면,a[3]
또는3[a]
배열의 네 번째 요소에 접근할 수 있는 정도a
.
강력한 반면에 포인터 산술은 컴퓨터 버그의 원천이 될 수 있다.초보 프로그래머를 혼동해 다른 문맥으로 억지로 끌어들이는 경향이 있는데, 표현은 평범한 산술일 수도 있고 포인터 산술일 수도 있고, 때로는 하나를 다른 것으로 착각하기 쉽다.이에 대응하여 현대의 많은 고급 컴퓨터 언어(예: 자바)는 주소를 사용하여 메모리에 직접 접속하는 것을 허용하지 않는다.또한 안전한 C 사투리 사이클론은 많은 문제들을 포인터로 해결한다.자세한 내용은 C 프로그래밍 언어를 참조하십시오.
포인터 또는 는 ANSI C와 C++에서 일반 포인터 유형으로 지원된다.에 대한 포인터.void
(기능이 아닌) 물체의 주소를 저장할 수 있으며, C에서 할당 시 다른 물체 포인터 유형으로 암묵적으로 변환되지만 참조 해제된 경우 명시적으로 캐스트해야 한다.사용된 K&R Cchar*
(ANSI C 이전)의 "유형 불가지론 포인터" 용도.
인트로 x = 4; 공허하게 하다* p1 = &x; 인트로* p2 = p1; // void* 암묵적으로 int*로 변환됨: 유효한 C(C++는 아님) 인트로 a = *p2; 인트로 b = *(인트로*)p1; // 인라인 비참조 시 암시적 변환이 없음
C++는 의 암묵적 변환을 허용하지 않는다.void*
다른 포인터 유형으로, 심지어 할당에서도.대부분의 컴파일러는 다른 캐스트와 마주쳤을 때 오류가 아닌 경고만 출력하지만 이는 부주의하고 심지어 의도하지 않은 캐스트를 피하기 위한 설계 결정이었다.
인트로 x = 4; 공허하게 하다* p1 = &x; 인트로* p2 = p1; // C++에서 실패: 보이드에서 암시적 변환이 없음* 인트로* p3 = (인트로*)p1; // C형 주조물 인트로* p4 = 정적_캐스트<인트로*>(p1); // C++ 캐스트
C++에는 없다.void&
(공극에 대한 참조) 보완void*
참조는 자신이 가리키는 변수에 대한 별칭처럼 작용하며, 유형이 다음과 같은 변수는 결코 있을 수 없기 때문이다.void
.
포인터 선언 구문 개요
이러한 포인터 선언은 대부분의 포인터 선언 변형을 포함한다.물론 트리플 포인터가 있을 수도 있지만, 트리플 포인터의 이면에 있는 주요 원리는 이미 더블 포인터에 존재한다.
마를 뜨다 CFF [5][5]; /* 문자 배열 */ 마를 뜨다 *cfp [5]; /* chars에 대한 포인터 배열 */ 마를 뜨다 **cpp; /* char("더블 포인터") */ 마를 뜨다 (*cpf) [5]; /* 문자 배열 */에 대한 포인터 마를 뜨다 *CPF(); /* char에 포인터를 반환하는 함수 */ 마를 뜨다 (*CFP)(); /* 문자를 반환하는 함수에 대한 포인터 */ 마를 뜨다 (*cfpF())[5]; /* chars 배열로 포인터를 되돌리는 함수 */ 마를 뜨다 (*CPFF[5])(); /* 문자를 반환하는 기능에 대한 포인터 배열 */
()와 []는 *보다 우선순위가 높다.
C#
C# 프로그래밍 언어에서 포인터는 특정 조건에서만 지원된다: 포인터를 포함한 모든 코드 블록에는 다음이 표시되어야 한다.unsafe
키워드이러한 블록은 일반적으로 더 높은 보안 권한이 있어야 실행할 수 있다.구문은 C++와 본질적으로 동일하며, 주소 포인트가 관리되거나 관리되지 않는 메모리가 될 수 있다.그러나 관리되는 메모리에 대한 포인터(관리된 개체에 대한 모든 포인터)는 다음을 사용하여 선언해야 한다.fixed
키워드 - 포인터가 범위 내에 있는 동안 가비지 수집기가 메모리 관리의 일부로 뾰족한 개체를 이동하지 못하도록 하여 포인터 주소를 유효하게 유지한다.
이에 대한 예외는 다음과 같다.IntPtr
다음과 동등한 수준으로 안전하게 관리되는 구조물int*
안전하지 않은 코드를 필요로 하지 않는다.이 유형은 에서 메소드를 사용할 때 반환되는 경우가 많다.System.Runtime.InteropServices
, 예를 들어,
// 프로세스의 관리되지 않는 메모리에서 16바이트의 메모리 가져오기 IntPtr 포인터 = 시스템.런타임.인터롭서비스.보안관.allocationHGlobal(16); // 할당된 메모리로 작업 수행 // 할당된 메모리를 확보하십시오. 시스템.런타임.인터롭서비스.보안관.프리HGlobal(포인터);
.NET Framework는 에 많은 클래스와 방법을 포함한다.System
그리고System.Runtime.InteropServices
네임스페이스(예:Marshal
(class)를 변환하는 class).NET 유형(예:System.String
관리되지 않는 여러 유형 및 포인터(예:LPWSTR
또는void*
관리되지 않는 코드와의 통신을 허용한다.대부분의 그러한 방법은 메모리의 임의 위치에 영향을 줄 수 있기 때문에 관리되지 않는 코드와 동일한 보안 권한 요구 사항을 가지고 있다.
코볼
COBOL 프로그래밍 언어는 변수에 대한 포인터를 지원한다.에 선언된 원시 또는 그룹(기록) 데이터 개체LINKAGE SECTION
프로그램의 유일한 메모리는 본질적으로 포인터 기반이며, 여기서 프로그램 내에서 할당된 유일한 메모리는 데이터 항목의 주소를 위한 공간(단일 메모리 워드 사용)이다.프로그램 소스 코드에서 이러한 데이터 항목은 다른 항목과 마찬가지로 사용된다.WORKING-STORAGE
가변적이지만, 그들의 컨텐츠는 그들의 컨텐츠를 통해 간접적으로 접근된다.LINKAGE
포인터
각 포인트 투 데이터 객체의 메모리 공간은 일반적으로 외부 문이나 또는 문과 같은 내장형 확장 언어 구조를 통해 동적으로 할당된다.
COBOL의 확장 버전은 또한 다음과 같이 선언된 포인터 변수를 제공한다.USAGE
IS
POINTER
조항들그러한 포인터 변수의 값은 다음을 사용하여 설정되고 수정된다.SET
그리고SET
ADDRESS
진술들
COBOL의 일부 확장 버전은 또한PROCEDURE-POINTER
실행 가능한 코드의 주소를 저장할 수 있는 변수.
PL/I
PL/I 언어는 모든 데이터 유형(구조물에 대한 포인터 포함), 재귀, 멀티태스킹, 문자열 처리 및 광범위한 내장 기능에 대한 완전한 지원을 제공한다.PL/I는 그 시대의 프로그래밍 언어에 비하면 상당히 비약적인 발전이었다.[citation needed]PL/I 포인터가 유형화되지 않았으므로 포인터 비참조나 할당에는 주물이 필요하지 않다.포인터 선언 구문은DECLARE xxx POINTER;
, "xxx"라는 이름의 포인터를 선언한다.포인터는 다음과 함께 사용된다.BASED
변수들기본 변수는 기본 로케이터로 선언할 수 있다.DECLARE xxx BASED(ppp);
또는 (없음)DECLARE xxx BASED;
여기서 xxx는 요소 변수, 구조 또는 배열일 수 있는 기반 변수이며 ppp는 기본 포인터).그러한 변수는 명시적 포인터 참조 없이 주소가 될 수 있다.xxx=1;
또는 기본 로케이터(ppp) 또는 다른 포인터(pointer)에 대한 명시적인 참조로 어드레싱될 수 있다.qqq->xxx=1;
).
포인터 산술은 PL/I 표준의 일부가 아니지만 많은 컴파일러가 폼의 식을 허용한다.ptr = ptr±expression
. IBM PL/I도 기본 제공 기능PTRADD
산수를 하다포인터 산술은 항상 바이트 단위로 수행된다.
IBM Enterprise PL/I 컴파일러에는 a라고 하는 새로운 형태의 타이핑 포인터가 있다.HANDLE
.
D
D 프로그래밍 언어는 C와 C++의 파생어로, C 포인터와 C 타입캐스팅을 완벽하게 지원한다.
에펠
에펠 객체 지향 언어는 포인터 산술 없이 값과 참조 의미를 사용한다.그럼에도 불구하고 포인터 클래스가 제공된다.포인터 산술, 타이프캐스팅, 명시적 메모리 관리, 비 Eiffel 소프트웨어와의 인터페이스 및 기타 기능을 제공한다.
포트란
포트란-90은 강력한 타입의 포인터 기능을 도입했다.포트란 포인터는 단순한 메모리 주소 이상의 것을 포함하고 있다.또한 배열 치수의 하한과 상한을 캡슐화하여(예: 임의 배열 섹션 지원), 기타 메타데이터를 지원한다.연결 연산자,=>
a를 연상시키는 데 사용된다.POINTER
다음과 같은 변수가 있는.TARGET
기여하다포트란-90ALLOCATE
또한 명령문은 메모리의 블록에 포인터를 연결하는데 사용될 수 있다.예를 들어 링크된 목록 구조를 정의하고 생성하기 위해 다음 코드를 사용할 수 있다.
타자를 치다real_list_t 진짜 :: sample_data(100) 타자를 치다 (real_list_t), 포인터 :: 다음에 => 무효의 () 끝형 타자를 치다 (real_list_t), 표적으로 삼다 :: my_real_list 타자를 치다 (real_list_t), 포인터 :: real_list_list_list real_list_list_list => my_real_list 하다 읽다 (1,이오스타트=oerr) real_list_list_list%sample_data 만일 (oerr /= 0) 퇴장하다 할당하다 (real_list_list_list%다음에) real_list_list_list => real_list_list_list%다음에 끝내다
Fortran-2003은 프로시저 포인터에 대한 지원을 추가했다.또한, C 상호운용성 기능의 일부로서, Fortran-2003은 C형 포인터를 Fortran 포인터 및 백으로 변환하기 위한 내재적 기능을 지원한다.
가다
바둑은 포인터가 있다.선언문 구문은 C와 동일하지만 반대로 쓰여 유형으로 끝난다.C와 달리 Go는 가비지 수집이 있고 포인터 산수를 허용하지 않는다.C++와 같은 참조 유형은 존재하지 않는다.지도와 채널과 같은 일부 내장형 유형은 박스 형태로 되어 있으며(즉, 내부적으로는 변이 가능한 구조의 포인터임), 를 사용하여 초기화된다.make
기능을 하다포인터와 비포인터 사이의 통일된 구문에 대한 접근방식에서 화살표 ()->
) 연산자가 삭제됨: 포인터의 도트 연산자는 참조 해제된 객체의 필드 또는 메서드를 가리킨다.그러나 이것은 오직 1단계 방향성으로만 작동한다.
자바
자바에는 포인터의 명시적인 표현이 없다.대신, 객체나 배열과 같은 보다 복잡한 데이터 구조가 참조를 사용하여 구현된다.언어는 명시적인 포인터 조작 연산자를 제공하지 않는다.그러나 코드는 여전히 null 참조(null pointer)를 무시하려고 시도할 수 있으며, 이는 런타임 예외를 발생시킨다.참조되지 않은 메모리 개체가 사용한 공간은 런타임의 가비지 수집에 의해 자동으로 복구된다.[16]
모둘라-2
포인터는 파스칼과 마찬가지로 매우 많이 구현된다.VAR
절차 호출의 매개 변수모둘라-2는 파스칼보다 훨씬 더 강하게 타이핑되어 있으며, 타이프 시스템을 벗어날 수 있는 방법이 적다.Modula-2(Modula-3)의 일부 변형에는 가비지 수집이 포함된다.
오베론
모둘라-2와 마찬가지로 포인터를 사용할 수 있다.여전히 형식 시스템을 피할 수 있는 방법이 더 적기 때문에 오베론과 그 변종은 모둘라-2나 그 변종보다 포인터에 관해서도 여전히 안전하다.모둘라-3와 마찬가지로, 쓰레기 수거는 언어 규격의 일부분이다.
파스칼
포인터를 특징으로 하는 많은 언어와 달리, 표준 아이소파스칼은 포인터가 익명으로 동적으로 생성된 변수를 참조할 수 있을 뿐 표준 정적 또는 로컬 변수를 참조할 수 없도록 한다.[17]그것은 포인터 산수를 가지고 있지 않다.포인터는 또한 관련 유형을 가지고 있어야 하며 한 유형에 대한 포인터는 다른 유형에 대한 포인터와 호환되지 않는다(예: 문자에 대한 포인터는 정수에 대한 포인터와 호환되지 않음).이는 다른 포인터 구현, 특히 PL/I 또는 C에 사용되는 포인터 구현에 내재된 유형의 보안 문제를 제거하는 데 도움이 된다.또한 매달린 포인터로 인한 일부 위험은 제거하지만, 이를 이용해 참조된 공간을 역동적으로 풀어주는 기능도 있다.dispose
표준 절차(과 동일한 효과를 나타냄)free
C)에서 발견되는 라이브러리 함수는 매달린 포인터의 위험이 완전히 제거되지 않았음을 의미한다.[18]
그러나 일부 상용 및 오픈 소스 Pascal(또는 파생 모델) 컴파일러 구현([19]예: Free Pascal)에서는Turbo Pascal 또는 Embarcadero Delphi의 Object Pascal - 포인터는 표준 정적 또는 국소 변수를 참조할 수 있으며 한 포인터 유형에서 다른 포인터 유형으로 캐스팅할 수 있다.또한 포인터 산술은 제한되지 않는다. 포인터에서 추가 또는 빼는 것은 어느 방향으로든 해당 바이트 수만큼 포인터 수를 이동시키지만, 포인터 산술은Inc
또는Dec
표준 절차를 사용하여 포인터가 가리키는 것으로 선언된 데이터 유형의 크기로 포인터를 이동시킨다.또한 이름 아래에 유형화되지 않은 포인터가 제공된다.Pointer
, 다른 포인터 유형과 호환되는.
펄
펄 프로그래밍 언어는 거의 사용되지 않지만 팩과 언팩 기능의 형태로 포인터를 지원한다.이는 컴파일된 OS 라이브러리와의 간단한 상호 작용만을 위한 것이다.다른 모든 경우 Perl은 참조를 사용하며, 이 참조는 타이핑되어 어떤 형태의 포인터 산술도 허용하지 않는다.그것들은 복잡한 데이터 구조를 만드는 데 사용된다.[20]
참고 항목
참조
- ^ Donald Knuth (1974). "Structured Programming with go to Statements" (PDF). Computing Surveys. 6 (5): 261–301. CiteSeerX 10.1.1.103.6084. doi:10.1145/356635.356640. S2CID 207630080. Archived from the original (PDF) on August 24, 2009.
- ^ Reilly, Edwin D. (2003). Milestones in Computer Science and Information Technology. Greenwood Publishing Group. p. 204. ISBN 9781573565219. Retrieved 2018-04-13.
Harold Lawson pointer.
- ^ "IEEE Computer Society awards list". Awards.computer.org. Archived from the original on 2011-03-22. Retrieved 2018-04-13.
- ^ ISO/IEC 9899, 조항 6.7.5.1, 단락 1.
- ^ ISO/IEC 9899, 조항 6.7.8, 단락 10.
- ^ ISO/IEC 9899, 조항 7.17, 단락 3: NULL... 구현 정의 Null 포인터 상수로 확장...
- ^ ISO/IEC 9899, 조항 6.5.3.2, 단락 4, 각주 87: 잘못된 값을 포인터에 할당한 경우, 단항 * 연산자의 동작은 정의되지 않는다... 단항 * 연산자에 의한 포인터 비참조를 위한 잘못된 값 중 null 포인터...
- ^ a b Plauger, P J; Brodie, Jim (1992). ANSI and ISO Standard C Programmer's Reference. Redmond, WA: Microsoft Press. pp. 108, 51. ISBN 978-1-55615-359-4.
An array type does not contain additional holes because all other types pack tightly when composed into arrays [at page 51]
- ^ WG14 N1124, C – 승인된 표준: ISO/IEC 9899 – 프로그래밍 언어 – C, 2005-05-06
- ^ Jung, Ralf. "Pointers Are Complicated II, or: We need better language specs".
- ^ Jung, Ralf. "Pointers Are Complicated, or: What's in a Byte?".
- ^ 특허 6625718, Steiner, Robert C. (Broomfield, CO), "자신의 현재 위치에 상대적인 포인터"는 아바야 테크놀로지 주식회사(Basking Ridge, NJ)에 배정된 2003-09-23을 발행했다.
- ^ 특허권 6115721, Nagy, Michael(Tampa, FL), "자체 포인터를 사용한 데이터베이스 저장 및 복원을 위한 시스템 및 방법"이 IBM(Armonk, NY)에 할당되어 2000-09-05를 발행했다.
- ^ "Based Pointers". Msdn.microsoft.com. Retrieved 2018-04-13.
- ^ 울프 빌팅, 얀 스칸스홀름, "Végen to C"(The Road to C), 3판, 169페이지, ISBN 91-44-01468-6
- ^ Nick Parlante, [1], Stanford Computer Science Education Library, 페이지 9–10(2000)
- ^ ISO 7185 Pascal Standard(비공식 사본), 섹션 6.4.4 Pointer-type 2017-04-24 Wayback Machine에 보관된 이후.
- ^ J. 웨일스, W. J. 스니링거, C.A. R. Hoare, "Pascal의 불확실성과 불안", 소프트웨어: 연습 및 경험 7, 페이지 685–696(1977)
- ^ 무료 Pascal 언어 참조 가이드, 섹션 3.4 포인터
- ^ Contact details. "// Making References (Perl References and nested data structures)". Perldoc.perl.org. Retrieved 2018-04-13.
외부 링크
![]() | 위키미디어 커먼즈에는 포인터(컴퓨팅)와 관련된 미디어가 있다. |
![]() | 위키다양성은 포인터에 대한 학습 자원을 가지고 있다. |
![]() | 위키북 C 프로그래밍에는 다음과 같은 주제에 대한 페이지가 있다. |
- 1967년 6월호 CACM의 PL/I List Processing Paper
- cdecl.org 포인터 선언을 일반 영어로 변환하는 도구
- IQ.com 이상. 쉬운 영어로 포인터를 설명하는 초급 수준의 안내서.
- 포인터에 대한 포인터 및 메모리 소개 – Stanford Computer Science Education Library
- C 프로그래밍의 포인터 2019-06-09년 웨이백 머신에 보관 C 프로그래밍 초보자를 위한 시각적 모델
- 0pointer.de 여러 프로그래밍 언어에서 null 포인터를 참조하지 않는 최소 길이의 소스 코드 목록
- "The C book" – ANSI C의 포인터 예 포함
- Joint Technical Committee ISO/IEC JTC 1, Subcommittee SC 22, Working Group WG 14 (2007-09-08). International Standard ISO/IEC 9899 (PDF). Committee Draft.
{{cite book}}
: CS1 maint : 복수이름 : 작성자 목록(링크)