접근 타입
접근 타입 (포인터) — Access types (pointers)
포인터는 잠재적으로 위험한 구조이며, 이는 Ada의 근본 철학과 충돌해요. Ada가 프로그래머를 포인터의 위험에서 보호하는 데 도움을 주는 방법은 두 가지가 있어요.
하나는 이미 본 대안 기능을 제공하는 것이에요. 매개변수 모드, 배열, 가변 크기 타입은 모두 C의 전형적인 포인터 사용을 대체할 수 있는 구조예요. 둘째로, Ada는 포인터를 가능한 한 안전하고 제한적으로 만들되, 프로그래머가 명시적으로 요청할 때(그리고 적절한 주의를 기울여 그 기능을 사용할 거라고 가정할 때) "탈출구"를 허용해요.
출처: 접근 타입 문서
본문
개요 (Overview)
Ada에서 간단한 포인터 타입, 즉 접근 타입(access type) 을 선언하는 방법:
package Dates is
type Months is
(January, February, March, April,
May, June, July, August, September,
October, November, December);
type Date is record
Day : Integer range 1 .. 31;
Month : Months;
Year : Integer;
end record;
end Dates;
with Dates; use Dates;
package Access_Types is
-- Declare an access type
type Date_Acc is access Date;
-- ^ "Designated type"
-- ^ Date_Acc values
-- point to Date
-- objects
D : Date_Acc := null;
-- ^ Literal for
-- "access to nothing"
-- ^ Access to date
end Access_Types;
이 예시는 다음을 보여줘요:
- 값이 특정 타입의 객체를 가리키는("지정하는") 접근 타입을 선언한다.
- 이 접근 타입에서 변수(접근 값)를 선언한다.
null값을 준다.
Ada의 강한 타입 철학에 따라, 지정 타입이 Date인 두 번째 접근 타입을 선언하면 두 접근 타입은 서로 호환되지 않아요:
with Dates; use Dates;
package Access_Types is
-- Declare an access type
type Date_Acc is access Date;
type Date_Acc_2 is access Date;
D : Date_Acc := null;
D2 : Date_Acc_2 := D;
-- ^ Invalid! Different types
end Access_Types;
대부분의 다른 언어에서 포인터 타입은 Ada처럼 명목(구조적) 타입이 아니라 구조적으로 타입이 매겨져요. 즉 두 포인터 타입은 같은 대상 타입과 접근성 규칙을 공유하는 한 같아요.
Ada는 그렇지 않아요. 적응하는 데 시간이 걸려요. 겉보기에 단순한 문제가 있어요. 타입에 대한 표준(canonical) 접근을 원한다면 어디에 선언해야 할까요? 흔히 쓰는 패턴은 "소유한" 특정 타입에 대한 접근 타입이 필요하면 그 타입과 함께 선언하는 것이에요:
package Access_Types is
type Point is record
X, Y : Natural;
end record;
type Point_Access is access Point;
end Access_Types;
(타입에 의한) 할당 (Allocation by type)
접근 타입을 선언했으면 그 타입의 변수에 의미 있는 값을 주는 방법이 필요해요. Ada에서는 new 키워드로 접근 타입의 값을 할당할 수 있어요.
with Dates; use Dates;
package Access_Types is
type Date_Acc is access Date;
D : Date_Acc := new Date;
-- ^ Allocate a new Date record
end Access_Types;
할당하려는 타입에 제약이 필요하면 변수 선언에서 하듯 하위 타입 표시(subtype indication)에 넣을 수 있어요:
with Dates; use Dates;
package Access_Types is
type String_Acc is access String;
-- ^
-- Access to unconstrained array type
Msg : String_Acc;
-- ^ Default value is null
Buffer : String_Acc :=
new String (1 .. 10);
-- ^ Constraint required
end Access_Types;
어떤 경우에는 타입만 지정해 할당하는 게 이상적이지 않을 수 있어요. 그래서 Ada는 할당과 함께 초기화하는 것도 허용해요. 이는 자격 표현식 구문으로 이루어져요:
with Dates; use Dates;
package Access_Types is
type Date_Acc is access Date;
type String_Acc is access String;
D : Date_Acc :=
new Date'(30, November, 2011);
Msg : String_Acc := new String'("Hello");
end Access_Types;
역참조 (Dereferencing)
Ada 접근 타입 기능의 마지막 중요한 부분은 접근 값에서 가리키는 객체로 가는 방법, 즉 포인터를 역참조하는 방법이에요. Ada에서 포인터 역참조는 .all 구문을 사용하지만 자주 필요하진 않아요. 많은 경우 접근 값은 자동으로 역참조되거든요:
with Dates; use Dates;
package Access_Types is
type Date_Acc is access Date;
D : Date_Acc :=
new Date'(30, November, 2011);
Today : Date := D.all;
-- ^ Access value dereference
J : Integer := D.Day;
-- ^ Implicit dereference
-- for record and array
-- components
-- Equivalent to D.all.day
end Access_Types;
그 외 기능 (Other features)
C/C++에서 포인터를 사용해 봤다면 알겠지만, 우리는 여전히 포인터 사용의 기초로 여겨지는 기능들이 빠져 있어요. 예를 들어:
- 포인터 산술 — 다음/이전 객체를 가리키도록 포인터를 증가·감소시키는 것.
- 수동 해제 — C에서
free또는delete라고 하는 것. 이는 잠재적으로 안전하지 않은 연산이에요. 안전한 Ada 영역에 머물려면 절대 수동으로 해제하면 안 돼요.
이 기능들은 Ada에 존재하지만, 특정 표준 라이브러리 API를 통해서만 사용할 수 있어요.
주의: Ada의 지침은 대부분의 경우 수동 할당을 피할 수 있고, 피해야 한다는 것이에요. 수동 할당을 피하는 방법은 많고, 그중 일부는 이미 다뤘어요(매개변수 모드 등). 언어는 포인터를 피할 라이브러리 추상화도 제공해요:
- 하나는 컨테이너 사용이에요. 컨테이너는 메모리가 자동으로 관리되므로 포인터를 피하는 데 도움을 줘요. 이 맥락에서 주목할 컨테이너는 Indefinite holder예요. 이 컨테이너는 String 같은 무한(indefinite) 타입의 값을 저장할 수 있게 해줘요.
- GNATCOLL에는 Refcount라는 스마트 포인터용 라이브러리가 있어요. 이 포인터들의 메모리는 자동으로 관리되어, 할당된 객체에 더 이상 참조가 없으면 메모리가 자동으로 해제돼요.
상호 재귀 타입 (Mutually recursive types)
연결 리스트는 자료 구조에서 흔한 관용구예요. Ada에서는 이를 가장 자연스럽게 레코드 타입과 접근 타입의 두 타입으로 정의할 수 있는데, 이 둘은 서로 의존적이에요. 서로 의존하는 타입을 선언하려면 불완전 타입 선언(incomplete type declaration)을 사용할 수 있어요:
package Simple_List is
type Node;
-- This is an incomplete type declaration,
-- which is completed in the same
-- declarative region.
type Node_Acc is access Node;
type Node is record
Content : Natural;
Prev, Next : Node_Acc;
end record;
end Simple_List;
이 예시에서 Node와 Node_Acc 타입은 서로 의존적이에요.