구조체(record)

구조체는 이질적인 데이터의 모임이다. 구성 요소들은 서로 다른 이름을 가지기 때문에 색인값(element)이 아니라 이름(component)으로 참조 한다. 각 구성요소의 이름은 필드라고 한다. 구조체는 Cobol에서 도입된 이후로, pascal, modula 2, ada, c, c++, fortran95등 대부분의 언어에서 제공한다.

  • 표기법

    • 함수 표기법

      knuth가 제안 했다. 각 필드를 name(ibm), price(ibm)등으로 표기하는 것이다.

    • ALGOL

      함수 표기법과 유사한 방법으로 name of ibm, price of ibm등을 채택

    • ML

      #name ibm, #price ibm과 같이 앞에 #을 붙인다.

    • pascal, ada, c

      점 표기법을 사용하여 ibm.name, ibm.price등올 필드에 접근

    • fortran90

      ibm%name, ibm%price 등으로 %표기법을 사용하여 필드를 접근한다.

  • 구조체 연산

    • pascal, modula 2

      전체 구조체의 배정만을 허용한다.

    • ada

      배정 동등, 비동등 연산을 허용한다.

    • c, c++

      구조체 x의 주소인 &x를 취할 수 있고, 필드 선택이 가능하다. 구조체의 배정을 허용하지만 동등 연산자 적용이 불가능하다.


공용체

서로 다른 파입의 값을 상이한 시간에 공유 기억장소에 저장할 수 있는 타입이다. fortran의 equivalence, PASCAL의 가변 레코드 그리고 c, c++ 공용체가 여기에 해당한다.


포인터

  • 포인터가 필요한 때
    • 실행 시까지 몇 개의 항목이 생성될 것인지 정해지지 않은경우
    • 데이터 항목 간의 여러 관계를 선언할 필요가 있을 경우
    • 포인터 변수들이 동일한 리스트를 참조하는 경우
  • 포인터의 문제점
    • 다중 참조
    • 허상 참조
    • 메모리 누수
    • 표기법 문제
  • 기억 장소 조각들이 할당되고 미리 정의되지 않은 방법을 홰제되는 기억장소의 블록이다.

  • 참조 타입

    포인터는 기억장소의 주소를 참조하는데 반해, 참조 변수는 객체나 기억장소의 값을 참조한다. C++에서 참조 타입 변수는 서브 프로그램의 형식 매개변수로 사용될 때 매우 유용하다.

  • 허상 포인터

    포인터 변수가 더 이상 의미 없는 것을 가리키고 있을 때, 이 포인터를 허상 포인터 혹은 허상 참조 라고 한다.

    • 해결책

      • 묘비(tombstone)

        묘비를 통해 스택과 힙에대한 모든 허상 참조를 추적하는 기법이다. 포인터는 묘비의 주소를 가지고 묘비는 객체의 주소를 가진다. 객체를 해제할 때 묘비의 값은 유효한 주소가 될 수 없는 nil로 설정한다. nil로 설정된 묘비를 가리키는 포인터에 대한 참조는 오류로 감지된다. 시간과 공간 측면에서 비용이 많이 든다.

      • 자물쇠-열쇠(locks-and-keys)

        묘비 기법의 대안이다. 모든 포인터는 주소와 키로 구성된 튜플이다. 힙 내의 모든 객체는 자물쇠로 시작하여, 객체로의 포인터는 포인터의 키가 자물쇠와 일치할 때에만 유효하다.


가비지 수집

허상 참조를 방지하는 가장 용이한 방법은 어떠한 경우에도 헤제를 허용하지 않는 것이다. 그러나 이경우 가비지 문제가 발생한다. 가비지는 할당되었으나, 프로그램에서 접근할수 없는 기억장소이다. 때로는 이러한 문제를 메모리 누수 라고 하며, 가비지를 자동적으로 재활용하는 시스템을 가비지 수집 이라고 하고, 일상생활에서 쓰레기 재활용과 유사하므로 쓰레기 수집 혹은 이삭 줍기라고 번역하기도 한다.

  • 가비지 수집시스템을 사용하는 언어

    LISP, Smalltalk, simula 67, Eiffel, java, c#

  • 가비지 수집시스템을 사용하지 않는 언어

    c, c++

  • 가비지 수집 방법

    • 참조계수(reference count)

      각 객체는 자신을 가리키는 객제의 개수를 가진다. 참조 계수가 0이되면 이 객체의 기억장소는 헤제될 수 있다.

      • 장점

        가비지 수집 오버헤드가 분산될 수 있다.

      • 단점

        • 계수를 증가시키고 감소시키는데 따른 갱신 작업의 오버헤드
        • 가장 심각한 단점은 원형 참조로, 객체들은 직접적이거나 간접적으로 여러 참조 체인을 통해서 자기 참조가 될 수 있다. 만일 이러한 체인상의 객체들이 루트나 진입점으로부터 접근이 불가능한 가비지 이면 전체 체인에 대해 해제가 불가능하다.
    • 표시 후 청소(mark-and-sweep)

      기억장소를 해제하기 위해 전역적인 정보를 사용한다.

      • 표시단계

        실행 환경의 루트 객체로부터 접근 가능한 모든 객체에 표시를 하는단계이다. 표시된 모든 객체들은 살아 있는 객체가 된다.

      • 청소단계

        표시되지 않은 객체에 대해 기억 장소를 해제하고, 현재 표시되는 살아있는 객체들이 표시를 없애고 기억장소 공간을 압축한다.

        • 장점

          • 루트 객체에서 접근 불가능한 객체는 자동으로 가비지 수집이 된다.
          • 청소단계에서 기억장소가 압축될 수 있다는 점이다.
        • 단점

          • 기억장소가 모두 소진되었을 때 오버헤드가 상당하다.

집합

동일한 타입의 상이한 값을 중복 없이 저장하고자 할 때 사용하는 구조화 타입이다.

  • Python

    공집합 set(), 공집합이 아니면 set((1,2,3)), 원소인 각 값을 입력한 횟수가 중요하지 않다. set([1,2,3])set([1,2,3,3,2,1])의 결과는 같다.

  • swift

    var cities : Set = ["Wonju", "Gangreung", "Seoul"] 초기화시 저장할 원소의 타입을 생략해도 된다. var cities : Set<String> = ["Wonju", "Gangreung", "Seoul"] 타입을 생략하지 않은 경우 var cities = Set<String>()

    for g in cities{
      print("\(g)")
    } //배열과 마찬가지로 순회 탐색 할 수 있다.
    

튜플

튜플에 여러 타입을 저장할 수 있지만, 일단 선언되고 나면 상수 성격을 가지므로 더 이상 값을 추가하거나 삭제하는 등의 변경이 불가능하다. 타입과 관계없이 다양하게 저장할 수 있지만 오직 최초에 선언된 상태의 항목만 사용할 수 있고 수정 삭제 추가 등의 변경이 불가능한 것이 특징이다.

  • Python

    >>>vowel = ('A', 'E', 'I', 'O', 'U')
    >>> for b in vowel;
    ... print b
    A
    E
    I
    U
    

    요소가 하나뿐인 튜플은 (x)가 아니라 (x,)라고 표기해야 한다.

  • Swift

    두 개 이상의 값으로 구성된 복합값을 나타낸다.

    //데이터 타입 생략
    let member = (10, "Youngtae", "Wonju", "123456")
    let result = (true, "10 records found")
    //데이터 타입 지정
    let member(int, String, String, String, String) = (10, "Youngtae", "Wonju", "123456")
    let result(Bool, String) = (true, "10 records found")
    

    배정된 값을 읽기 위해서는 점 연산자와 색인 값을 사용한다. 상수이기 때문에 새로운 값을 배정할 수는 없다.


사전

사전은 맵이라고 부르기도 하는 것으로, 키와 값으로 구성된 비순차적, 변경 가능한 집합적 자료구조이다. 집합의 원소와 마찬가지로 키는 변경이 불가능하다.

  • Python

    >>>love_makers = {'Youngtae' : 6, 'Hyeja' : 9}
    >>>love_makers
    {'Youngtae' : 6, 'Hyeja' : 9}
    >>>love_makers['Youngtae']
    6
    

    특정 키가 존재하는지의 여부를 검사하기 위해서는 다음과 같이 해야한다.'키' in 사전

    >>> trouble_makers = {}
    >>> trouble_makers['Bbangtae'] = 999
    >>> trouble_makers['Hyeja'] = 666
    >>> trouble_makers
    {'Bbangtae':999, 'Hyeja':666}
    >>>trouble_makers['Hyeja'] = 6  //새로운 값 갱신
    >>>trouble_makers
    {'Bbangtae':999,'Hyeja':6}
    

    사전에서 항목을 삭제하려면del d[k] 사전에 존재하지 않은 항목을 삭제하려고 시도하면 오류가 발생한다.

  • Swift

    var coupleCodes: Dictionary<String, String> = ["YT":"Youngtae", "HJ":"Hyeja", "BT":"Bbangtae","HZ":"Hyeza"]
    
  • Ruby

    키와 값을 조합하여 묶은 배열과 유사한 해시를 제공한다. 배열과는 달리 요소의 순서에 신경쓸 필요가 없다. 해시를 정의하기 위해 {} 안에 키와 값을 저장한다.

    love = {"name" => "영태", "num"=>3, 10=>100}
    

타입 시스템

타입과 식을 결합하는 규칙의 집합이다. 타입 시스템의 주요 주제는 다음과 같다.

  • 타입에 대한 연산
  • 타입의 일치성

    때때로 편리하게 상이한 타입의 변수를 섞는 것이 가능할 수도 있고, 불가능할 수도 있다.

  • 강제 변환

    프로그래밍 언어에 자동적으로 삽입되어 하나의 타입을 다른 타입으로 변환하는 것이다.

  • 중복정의

    +, * 와 같은 내장 연산자나 사용자 정의 프로시져 이름이 상이한 문맥에서 서로 다른 의미를 가질 수 있다.

  • 다형성

타입 일치성

  • 이름 동등

    두 변수가 동일한 선언문에서 함께 선언되거나 타입 이름이 동일하다면, 두 변수의 타입은 동일하다. 역으로 두 변수의 타입이 동일하다면, 두 변수가 함께 선언되거나 동일한 타입 이름을 가진다.

  • 구조 동등

    두 변수가 동일한 타입을 가진다면, 그들의 타입의 구성 요소가 동일하다.

  • 선언 동등

    이름 동등 개념에 추가하여 재 선언을 하여 원래의 구조를 그대로 유지하는 경우에도 원래의 데이터 타입과 동일하다고 간주하는 방식이다.

    type T1 = array[1..100] of integer;
    type T2 = array[1..100] of integer;
    type T3 = T2;
    

    구조 동등 관점에서는 T1, T2, T3가 모두 타입이 동일하고, 선언 동등 관점에서는 T2, T3가 동일하고, 이름 동등 관점에서는 모두 서로 다른 타입이다.


C의 캐스트와 타입 변환

  • 명시적인 캐스트 식이 사용될 수 있다.

    i = (int) f;

  • 식의 피연산자가 묵시적으로 다른 타입으로 변환되고 나서 다음 연산에 이용될 수 있다.

    정수와 실수의 곱은 먼저 정수를 실수로 변환한다.

  • 함수의 실 매개변수는 해당하는 형식 매개변수의 타입으로 묵시적으로 변환 될 수 있다.
  • 함수로부터의 리턴 값은 리턴 타입으로 묵시적으로 변환될 수 있다.

강제 변환이 발생하는 상황

  • 명시적인 캐스트에 의해서
  • 변환은 배정문의 좌측 식의 타입이 우측의 타입과 일치해야 한다는 규칙에 의해 일어날 수 있다.
  • 변환은 연산자가 적용될 수 있도록 피연산자에 대해 일어날 수 있다.
  • 변환은 함수 호출의 매개 변수에 대해서도 일어날 수 있다.

언어별 특징

  • pascal과 Ada에서는 변환이 거의 제공되지 않는다.

    타입 불일치의 경우 몇 가지 예외를 제하고는 오류로 취급한다.

  • C

    타입 불일치는 컴파일러가 적절한 변환 연산을 찾아 타입을 변경하는 코드를 삽입한다. 변환이 불가능한 경우는 오류로 처리된다.

  • java

    int 와 float이 더해질때 int가 float으로 강제 변환 된다.

  • c#

    불리안 타입은 같은 데이터 타입 이외에 다른 타입으로의 변환이 금지된 데이터 타입이다. C#의 데이터 타입은 값을 저장하는 방식에 따라 크게 값타입(수치, 문자, 불리안, 열거, 구조체), 참조 타입(클래스, 인터페이스, 델리게이트, 배열) 로 구분할 수 있으며 이들 사이의 타입 변환을 박싱과 언박싱 이라고 부른다.

    • 박싱

      값 타입의 데이터를 참조 타입으로 변환하는 것을 의미하며 컴파일러에 의해 묵시적으로 실행된다.

    • 언박싱

      참조 타입을 값 타입으로 변환하는 것을 의미하며 반드시 캐스티을 통하여 명시적으로 실행 된다.

  • 강타입 언어

    타입 오류가 항상 탐지되면 강타입 언어라고 한다. Java, C#, Ada, ML

  • 강타입이 아닌 언어

    c, c++, fortran95