포인터가 언제 그리고 왜 위험한 것으로 보이기 시작 했습니까?


18

프로그래밍 언어에서 포인터를 사용하는 것에 대해 생각하는 데있어 점진적인 변화가 있었는데, 포인터가 위험한 것으로 간주되었다는 것이 일반적으로 받아 들여졌다 (올바른 "사악한"또는 유사한 어 그리 게이션이 아닌 경우).

이러한 사고의 변화를위한 역사적 발전은 무엇입니까? 구체적이고 중요한 행사, 연구 또는 기타 개발이 있었습니까?

예를 들어 C에서 C ++로 Java 로의 전환을 피상적으로 살펴보면 포인터를 참조로 보완 한 다음 완전히 대체하려는 경향이있는 것 같습니다. 그러나 실제 이벤트 체인은 아마도 이것보다 훨씬 미묘하고 복잡했을 것입니다. 주류 언어로 만든 기능은 아마도 오래 전에 다른 곳에서 시작되었을 수 있습니다.

참고 : 포인터 대 참조의 실제 장점에 대해 묻지 않습니다. 나는이 명백한 변화에 대한 이론적 근거에 중점을 둔다.


1
교양 교육의 쇠퇴로 인한 것이었다. 사람들은 더 이상 모든 CPU에 포함 된 컴퓨팅 기술의 가장 기본적인 아이디어 중 하나 인 간접 참조를 이해할 수 없었습니다.

10
포인터 위험합니다. 왜 생각이 바뀌 었다고 생각합니까? 성능 저하없이 포인터를 사용하지 않고 소프트웨어를 작성할 수있는 언어 기능 및 하드웨어가 개선되었습니다.
Monica의

4
@DaveInCaz 특정 개발이 ​​포인터의 발명이라는 것을 알고 있습니다.
정지 모니카 해치지

5
@ nocomprende : 방금 쓴 것은 사실이나 증거가 아니라 의견입니다. 1970 년에는 프로그래머가 훨씬 적었고, 오늘날의 "간접 참조"에서 인구가 더 나아 졌다는 증거는 없습니다.
whatsisname

3
포인터는 처음부터 항상 위험한 것으로 간주되었습니다. 그것들을 어셈블리에서 더 높은 수준의 언어로 옮기는 것은 타협이었습니다.
Frank Hileman

답변:


21

이론적 근거는 포인터에 대한 대안의 개발이었다.

후드 아래에서 모든 포인터 / 참조 등은 메모리 주소 (일명 포인터)를 포함하는 정수로 구현됩니다. 때 C가 나와서,이 기능은 포인터로 노출되었다. 이는 기본 하드웨어가 메모리를 처리하기 위해 할 수있는 모든 작업을 포인터로 수행 할 수 있음을 의미했습니다.

이것은 항상 "위험한"것이었지만 위험은 상대적입니다. 1000 라인 프로그램을 작성하거나 IBM 등급 소프트웨어 품질 절차가있는 경우이 위험을 쉽게 해결할 수 있습니다. 그러나 모든 소프트웨어가 그런 식으로 개발 된 것은 아닙니다. 이와 같이,보다 단순한 구조에 대한 요구가 생겨났다.

당신이 그것에 대해 생각한다면, an int&과 a는 int* const실제로 같은 수준의 안전을 가지고 있지만, 하나는 다른 것보다 훨씬 좋은 구문을 가지고 있습니다. int&레지스터에 저장된 int를 참조 할 수 있기 때문에 더 효율적 일 수 있습니다 (비동기 : 과거에는 사실이지만 현대 컴파일러는 최적화하는 데 너무 뛰어나서 레지스터의 정수에 대한 포인터를 가질 수있는 한) 당신은 같은 실제 주소를 필요로 그 기능 중 하나를 사용하지 않습니다 ++)

Java 로 전환하면 보안을 보장하는 언어로 이동합니다. CC ++ 은 아무 것도 제공하지 않았습니다. Java는 합법적 인 작업 만 실행되도록 보장합니다. 이를 위해 java는 포인터를 완전히 없애 버렸습니다. 그들이 찾은 것은 실제 코드에서 수행되는 대부분의 포인터 / 참조 작업은 참조가 충분했던 것입니다. 배열을 통한 빠른 반복과 같은 소수의 경우에만 포인터가 정말로 필요했습니다. 그러한 경우, java는 런타임 히트를 사용하여 사용하지 않습니다.

움직임은 단조롭지 않았습니다. C #에서는 포인터가 매우 제한적 이지만 다시 도입되었습니다 . " unsafe "로 표시되어 신뢰할 수없는 코드로는 사용할 수 없습니다. 또한 지시 할 수있는 것과 지시 할 수없는 것에 대한 명확한 규칙이 있습니다 (예를 들어, 배열 끝을지나 포인터를 증가시키는 것은 유효하지 않습니다 ). 그러나 그들은 고성능 포인터가 필요한 몇 가지 사례를 발견하여 다시 넣었습니다.

또한 그러한 개념이 전혀없는 기능적 언어가 흥미로울 것입니다. 그러나 그것은 매우 다른 토론입니다.


3
Java에 포인터가 없다고 말하는 것이 옳지 않습니다. 포인터가 무엇이고 포인터가 아닌지에 대한 긴 토론에 들어가고 싶지 않지만 JLS는 "참조의 가치는 포인터"라고 말합니다. 포인터에 직접 액세스하거나 수정할 수는 없습니다. 이것은 보안뿐만 아니라 사람들이 물건의 위치를 ​​추적하지 못하게하는 것이 GC에 도움이됩니다.
JimmyJames

6
@JimmyJames True. 이 답변의 목적 상 포인터와 포인터가 아닌 구분선은 포인터 산술 연산을 지원하는지 여부이며 일반적으로 참조에서 지원하지 않습니다.
Cort Ammon-복원 모니카

8
@JimmyJames 나는 포인터 가 산술 연산을 수행 할 수 있고 참조 는 아니지만 Cort의 주장과 일치 합니다. Java와 같은 언어로 참조를 구현하는 실제 메커니즘은 구현 세부 사항입니다.
Robert Harvey

3
일반적으로 C와 C ++는 사양에 많은 "정의되지 않은 행동"을 허용함으로써이 위험한 클럽의 멤버쉽을 자발적으로 수용했습니다.
rwong

2
그런데 포인터와 숫자를 구별하는 CPU 가 있습니다 . 예를 들어, IBM AS / 400의 원래 48 비트 CISC CPU가이를 수행합니다. 실제로 OS 아래에 추상화 계층이 있습니다. 즉, CPU는 숫자와 포인터를 구별하고 포인터에 대한 산술을 금지 할뿐만 아니라 OS 자체도 포인터 대해 전혀 알지 못하며 언어도하지 않습니다. . 흥미롭게도 이것은 원래 AS / 400을 하나의 시스템으로 만들며, C의 고급 스크립팅 언어에서 코드를 다시 작성하면 크기가 느려집니다 .
Jörg W Mittag

10

복잡한 프로그램 (예 : 재귀 또는 가변 크기 데이터 구조)에는 어떤 종류의 간접적 인 지시가 필요합니다. 그러나 포인터를 통해이 간접 명령을 구현할 필요는 없습니다.

대부분의 고급 프로그래밍 언어 (예 : 어셈블리가 아님)는 상당히 메모리 안전하며 무제한 포인터 액세스를 허용하지 않습니다. C 가족은 여기에서 이상합니다.

C는 원시 어셈블리에 대한 매우 얇은 추상화였던 B에서 진화했습니다. B는 단일 유형 인 단어입니다. 단어는 정수 또는 포인터로 사용될 수 있습니다. 이 두 가지는 전체 메모리가 하나의 연속 배열로 볼 때 동일합니다. C는이 유연한 접근 방식을 유지했으며 본질적으로 안전하지 않은 포인터 산술을 계속 지원했습니다. C의 전체 유형 시스템은 나중에 생각할 것입니다. 메모리 액세스에 대한 이러한 유연성 덕분에 C는 Unix 운영 체제 프로토 타이핑이라는 주요 목적에 매우 적합했습니다. 물론 유닉스와 C는 꽤 인기가 있었기 때문에 메모리에 대한 저수준 접근이 실제로 필요하지 않은 응용 프로그램에서도 C가 사용됩니다.

C 이전의 프로그래밍 언어 (예 : Fortran, Algol dialects in Pascal, Cobol, Lisp 등)를 보면 C와 같은 포인터를 지원하는 언어가 있습니다. 특히, 널 포인터 개념은 1965 년 Algol W에 대해 발명되었습니다. 그러나 그러한 언어 중 어느 것도 C와 같은 효율적인 저 추출 시스템 언어로 시도하지 않았습니다. Cobol은 업계 수준의 언어보다 더 많은 연구 프로젝트를 수행했으며 비즈니스 응용 프로그램에 중점을 두었습니다.

가비지 콜렉션은 50 년대 후반, 즉 C 이전 (70 년대 초) 이전에 존재했습니다. GC가 제대로 작동하려면 메모리 안전이 필요합니다. C 이전과 이후의 언어는 GC를 일반 기능으로 사용했습니다. 물론 이것은 언어를 훨씬 더 복잡하고 느리게 만들어 주는데, 이는 특히 메인 프레임 시대에 두드러졌습니다. GC 언어는 연구 중심적 (예 : Lisp, Simula, ML)이거나 강력한 워크 스테이션 (예 : 스몰 토크)이 필요한 경향이있었습니다.

일반적으로 컴퓨터가 작고 강력한 컴퓨터를 사용하고 GC 언어가 더 대중화되었습니다. 비 실시간 응용 프로그램의 경우 (때로는 때때로) GC가 선호되는 방식입니다. 그러나 GC 알고리즘은 또한 강렬한 연구의 주제였습니다. 대안으로, 특히 지난 30 년 동안 GC가없는 더 나은 메모리 안전성이 추가로 개발되었습니다. 주목할만한 혁신은 C ++의 RAII 및 스마트 포인터 및 Rust의 평생 시스템 / 빌러 검사기입니다.

Java는 메모리 안전 프로그래밍 언어로 혁신을 이루지 못했습니다. 기본적으로 GCed, 메모리 안전 스몰 토크 언어의 의미를 취하고이를 C ++의 구문 및 정적 형식과 결합했습니다. 그런 다음 더 좋고 간단한 C / C ++로 판매되었습니다. 그러나 그것은 피상적으로 C ++ 자손입니다. Java의 포인터 부족은 C ++ 데이터 모델을 거부하는 것보다 스몰 토크 객체 모델에 더 많이 있습니다.

따라서 Java, Ruby 및 C #과 같은 "현대"언어는 C에서와 같이 원시 포인터의 문제를 극복하는 것으로 해석되어서는 안되며 C를 포함한 많은 전통에서 유래 한 것뿐만 아니라 Smalltalk, Simula, 또는 Lisp.


4

내 경험상, 포인터는 항상 많은 사람들에게 도전적인 개념이었습니다. 1970 년에 제가 다녔던 대학에는 Burroughs B5500이 있었고 우리는 프로그래밍 프로젝트에 Extended Algol을 사용했습니다. 하드웨어 아키텍처는 디스크립터와 데이터 워드 상단의 일부 코드를 기반으로했습니다. 이것들은 배열이 끝을 벗어나지 않고 포인터를 사용할 수 있도록 명시 적으로 설계되었습니다.

우리는 이름 대 가치 참조 및 B5500 어레이의 작동 방식에 대해 강의실 토론을 진행했습니다. 우리 중 일부는 즉시 설명을 받았습니다. 다른 사람들은 그렇지 않았습니다.

나중에, 하드웨어가 날 뛰는 포인터, 특히 어셈블리 언어에서 나를 보호하지 못하는 것은 다소 충격적이었습니다. 졸업 후 첫 직장에서 운영 체제 문제를 해결하는 데 도움이되었습니다. 종종 우리가 가진 유일한 문서는 인쇄 된 크래시 덤프였습니다. 메모리 덤프에서 런 어웨이 포인터의 소스를 찾기위한 요령을 개발했기 때문에 모든 사람들이 "불가능한"덤프를 내놓았습니다. 우리가 겪은 많은 문제는 다른 유형의 오류보다 포인터 오류로 인해 발생했습니다.

내가 함께 일한 많은 사람들이 FORTRAN을 작성하기 시작한 다음 C로 옮겨 FORTRAN과 비슷한 C를 작성하고 포인터를 피했습니다. 포인터와 참조를 내재화하지 않았기 때문에 Java에 문제가 있습니다. 종종 FORTRAN 프로그래머에게는 객체 할당이 실제로 어떻게 작동하는지 이해하기가 어렵습니다.

현대 언어는 오타 및 기타 오류로부터 우리를 안전하게 유지하면서 "후드"아래에 포인터가 필요한 작업을 훨씬 쉽게 수행 할 수 있도록했습니다.

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