Sign In
Programming Language

[프로그래밍 언어] 3. Data Type

Y
Yerim
카테고리
  1. Programming Language

서론 Introduction

데이터 타입

데이터 값들의 모임과 그들 값들에 대한 미리 정의된 연산들의 집합
사용자-정의 타입 user-defined type: C언어에서 typedef를 사용하여 없는 타입 정의
의미 있는 이름을 사용하여 향사된 판독성 제공
프로그램 수정에 도움 제공
추상 데이터 타입 abstract: 사용자-정의 타입에서 발전, 최근 대부분의 언어에서 지원
객체 object: 사용자-정의 추상 데이터 타입의 인스턴스 instance
기본 데이터 타입: 다른 데이터 타입을 이용하여 정의하지 않은 데이터 타입
거의 모든 프로그래밍 언어가 기본 데이터 타입 집합을 제공
어떤 기본 데이터 타입은 하드웨어의 반영

기본 데이터 타입 Primitive Data Types

수치 타입 numeric type

정수 integer
가장 공통된 기본 수치 데이터 타입
Java: 4가지 크기의 부호 정수 제공 → byte, short, int, long
C, C++: 비부호 정수도 제공
Python: 하드웨어에 의해 직접 지원되지 않는 정수 타입 제공
명시적 힙 동적 변수로 배열을 계속 키워나간다
긴 정수 타입 long interger type → 제한되지 않는 길이를 가질 수 있음
Overflow가 발생하지 않는다
부동-소수점 수 floating-point
실수를 모델링
대부분의 실수 값에 대한 근사값을 제공 → 표현 가능한 정수/실수의 개수는 유한함
대부분의 언어는 float, double을 제공
복소수 complex
일부 프로그래밍 어어들만 복소수 데이터 타입을 지원
주로 과학 분야에서 사용된다
Python: 복숫 리터럴의 허수 부분은 j나 J로 끝남 → scipy
십진수 decimal
사무 시스템 응용 분야 지원하기 위한 프로그래밍 언어들에서 십진수 지원
대표적 언어들: COBOL, C# 등

불리안 타입 boolean type

모든 타입들 중에서 가장 단순: true, false
1950년 이후 설계뙨 대부분의 범용 언어에 포함됨
C언어
C89는 불리안 타입을 지원하지 않고 0을 거짓, 0이 아닌 모든 값을 참으로 처리
C99와 C++은 불리안 타입을 지원, 수치식 처럼 제공
Java, C#: 부리안 식으로 사용하는 것을 허용하지 않는다

문자 타입 character type

문자 데이터는 수치 코딩으로 컴퓨터에 저장
ASCII: 가장 공통적으로 사용하는 8비트 코딩 기법 (실제로는 7비트 + start bit)
Unicode: 16비트 문자 코드로 세상에 존재하는 대부분의 자연어 문자 표현 가능
Java는 Unicode를 쓰고 모든 문자 처리 가능 → 백엔드에서 사용
USC-4, UTF-32: 4바이트 문자 코드
대부분의 프로그래밍 언어가 문자를 기본 데이터 타입으로 지원
Python은 단일 문자를 단지 길이가 1인 문자 스트링으로 지원 (’a’ == “a”)

문자 스트링 타입 Character String Types

스트링과 연산

가장 공통된 스트링 연산: 배정, 접합, 부분 스트링 참조, 비교, 패턴 매칭
배정 assignment
일련의 문자열을 다른 곳에 배정(다른 객체), 별칭을 제공 (같은 객체)
다른 곳에 배정할 경우 내용을 수정해도 원본은 변경되지 않음
접합 catenation
b를 a에 접합시키는가, 새로운 영역에 접합시키는 것인가?
새로운 영역에 접합시키는 경우 별칭이 있을 때 별칭은 늘어나지 않는다
부분 스트링 참조 substring reference
주어진 스트링의 부분 스트링에 대한 참조 → 배열의 슬라이스(slice)
C언어
스트링이 기본 타입으로 정의되지 않음
스트링은 단일 문자들의 배열로 저장 → 스트링의 끝을 널 NULL 문자로 처리
스트링 연산은 표준 라이브러리 함수로 제공: strcpy(), strcat(), strlen()
strcpy(dest, src): 스트링이 오버플로하는 것에 대해 보호하지 않음
src의 길이가 dest보다 크다면 dest 다음 30바이트를 덮어쓰게 됨
Java
String, StringBuffer 클래스에 의해서 지원
String: 값의 변경이 불가능한 상수 스트링
String Buffer: 값이 변경 가능한, 단일 문자들의 배열과 유사
Python
스트링이 기본 타입
스트링은 변경 불가, Java의 String 클래스와 유사
문자와 부분 스트링 참조에 대해서는 문자들의 배열과 매우 유사
스트링 길이 선택 사항
스트링 길이에 따라 선택 사항들이 존재한다
정적 길이 스트링, 제한된 동적 길이 스트링, 동적 길이 스트링

정적 길이 스트링 static length string

길이는 정적, 스트링이 생성될 때 설정 됨
Python의 스트링, Java의 String 클래스
구현
서술자 descriptor는 단지 컴파일 과정에서만 요구
서술자는 세 가지 필드를 가짐: 타입의 이름, 길이, 주소
실행 시점에서 메모리가 할당된 후에는 필요 없다 (서술자 = symbol table)

제한된 동적 길이 스트링 limited dynamic length string

스트링이 그 변수 정의에서 선언, 고정된 최대 길이까지의 가변적인 길이를 갖는 것을 허용
C의 스트링 (문자들의 배열, 마지막 글자는 null)
구현
고정된 최대 길이와 현재 길이를 저장하는 실행-시간 서술자를 요구
대부분의 경우 서술자들은 심볼 테이블에 저장
컴파일러는 current length를 알 수 없다 → symbol table을 남겨 interpreter처럼 동작
C와 C++: 스트링의 끝이 널 문자로 표시 → 실행-시간 서술자를 요구하지 않음
C에서는 current length를 사용하지 않기 위해 NULL을 사용
배열의 색인 값에 대해 범위 검사 X → 최대 길이도 사용하지 않음
maximum length를 사용하지 않기 위해 오버플로우 검사 X → 보안상 문제 발생
특별히 동적 기억공간 할당을 요구하지 않음 → 한 번의 할당 과정만 필요

동적 길이 스트링 dynamic length string

최대 길이 제한 없이 가변 길이를 갖는 것을 허용
Java의 StringBuffer 클래스, JavaScript, Perl, 표준 C++ 라이브러리
구현
더 복잡한 기억 공간 관리를 요구
스트링의 길이와 바인딩되는 기억공간은 동적으로 늘어나거나 줄어들어야 함
동적 할당과 반환을 지원하는 방법
스트림을 연결 리스트에 저장
스트링이 늘어날 떄 새롭게 요구된 메모리 셀은 의 어느 곳으로부터 올 수 있음
단점: 연결 리스트의 링크를 표현하기 위해 여분의 메모리 필요, 스트링 연산의 복잡성
스트링을 힙에 할당된 개개의 문자들을 가리키는 포인터의 배열로서 저장
장점: 여분의 메모리를 사용하지만 연결리스트 접근보다 더 빠를 수 있음
스트링 전체를 인접한 기억공간 셀들에 저장
단점: 스트링이 늘어날 때 문제 발생
기존의 셀들에 인접한 기억공간에 여유가 없을 때
새로운 메모리 영역을 찾고 스트링 이전 부분을 새로운 영역으로 이동
이전 스트링에 대해서 사용된 메모리 셀 회수

열거 타입 Enumeration Types

이름 상수들인 모든 가능한 값들이 그 정의에서 제공되는, 즉 열거 타입
열거 상수라 불리는 이름 상수들의 모임을 정의 ,그루핑 하는 방법을 제공
→ 사용할 문자열이 한정되어 있다면 열거 타입을 사용
Python, JavaScript, Perl, PHP, Ruby, Lua 등 최근 개발된 스크립트 언어들은 지원하지 않음
C언어
enum colors (red, blue, green, yellow, black); // 열거형 타입 정의
colors myColor = blue, yourColored;            // 열거형 변수 정의
colors 타입은 열거 상수에 대해서 디폴트 내부 값 0, 1, …을 사용
이 열거 값들은 정수 문맥에서 사용될 때 int로 강제 변환
→ 실제 값의 의미를 알 수 없고 범위를 벗어나는 문제가 발생할 수 있음
Java
열거 타입은 클래스라는 것을 제외하고는 C와 유사
신뢰성 측면에서 차이가 존재
어떤 산술 연산도 여러 타입에 적용되지 않음
범위 밖의 값을 할당 받을 수 없음
열거형은 이름을 갖는 값들은 쉽게 인식되기 때문에 판독성이 매우 향상된다

배열 타입 Array Types

배열

배열
구조체
동질적인 데이터 원소들의 집합체
(데이터 원소들은 동일한 타입)
서로 다른 데이터 원소들의 집합체
(데이터 원소들은 동일 / 다른 타입)
배열의 이름은 포인터
→ 배열을 넘기면 배열/pointer로 받음
구조체 이름은 구조체
→ 구조체를 넘기면 구조체로 받음
int a[10];
struct A{
    int a;
    char c;
}
[10, "abc", 7.5]
a = 10
a = 'abc'
a = 7.5
C, Java, C++, Ada, C#
배열의 모든 원소들은 동일한 타입에 속해야한다
포인터참조들단일 타입을 가리키거나 참조하도록 제한
가리켜지거나 참조되고 있는 객체나 데이터값들도 단일 타입에 속함
Python, JavaScript, Ruby
변수들은 객체나 데이터 값들에 대한 타입과 상관 없는 typeless 참조
배열은 여전히 단일 타입의 원소들로 구성되지만, 그 원소들은 다른 타입 객체 또는 데이터 값들을 참조 가능
배열의 원소들이 동일한 타입에 속하기 때문에, 원소들은 여전히 동질적

색인

배열은 두 레벨의 구문 메커니즘으로 참조됨
집단체 이름 aggregate name
첨자 subscript 또는 색인 index
개개의 원소는 집단체 첫 번째 원소와의 상대적인 위치로 식별됨 → 시작주소로부터 얼마나 떨어져 있는가?
배열 원소들의 참조는 첨자나 색인으로 나타냄
유한 사상 finite mapping
선택 연산은 배열 이름과 첨자 값들의 집합으로부터 집단체의 한 원소를 사상 mapping으로 생각됨
배열은 떄때로 유한 사상 finite mapping이라고 불림
기호로 표시하면 다음과 같음: 배열_이름 (첨자_값_리스트) → 원소
첨자의 범위 오류
여러 프로그램에서 발생하는 공통된 현상
범위 검사를 요구하는 것은 언어의 신뢰성에 중요한 요소
Java, ML, C#은 범위 검사를 명세
C언어를 포함한 많은 프로그래밍 언어들은 첨자에 대한 검사를 명세하지 않음

첨자 바인딩 subscript binding과 배열 유형

배열 변수에 대한 첨자 타입의 바인딩은 보통 정적, 그 첨자 값 범위는 때때로 동적으로 바인딩
C언어를 포함하여, 어떤 언어들에서는 첨자 범위의 하한이 묵시적으로 존재
C언어나 Java에서 모든 색인 범위의 하한은 0으로 고정
Fortran 98+는 색인 범위의 하한의 디폴트 값이 1
다른 언어에서 첨자 범위가 프로그래머에 의해 명세 되어야 함
배열은 3가지 기준으로 4가지 유형으로 구분 가능
첨자 값 범위에 대한 바인딩 → 첨자 값 범위
기억 공간에 대한 바인딩 → 정적, 동적
기억공간의 할당 위치에 기반하여 분류 → DS, heap, stack
정적 배열
첨자 범위가 정적으로 바인딩 + 기억공간 할당이 정적인 배열
장점: 실행 시간의 효율성
동적 할당이나 회수가 요구되지 않음
Data Segment에 있으면 메모리 접근에 직접 접근이 가능
단점: 배열에 대한 기억공간이 프로그램 실행 시간 전체에 걸쳐서 고정
Data segment 내부에 큰 공간을 잡고 프로그램이 끝날 때까지 사용한다면 비효율적
int a[10];
f(){
    static int a[10]; // C언어에서 static으로 선언되는 배열은 정적 배열
}
고정 스택-동적 배열
첨자 범위가 정적으로 바인딩 + 기억공간의 할당이 실행 시간 중
선언된 기억 공간을 바인딩하는 시간에 이루어지는 배열
장점: 정적 배열에 비하여, 기억 공간의 효율성이 존재
단점: 할당과 회수에 소요되는 실행 시간
할당과 회수가 실행시간이 더 많이 든다 → 메모리를 할당하는 회수할 때 비용이 든다
f(){
    int a[10]; // C언어에서 함수에 선언된 배열
}
고정 힙-동적 배열
첨자 범위와 기억공간의 바인딩이 기억공간이 할당된 후에 고정
고정 스택-동적 배열과 유사
첨자 범위와 기억공간의 바인딩이 실행 중에 사용자 프로그램의 요청으로 이루어짐
기억공간이 스택이 아닌 힙으로부터 할당
장점: 유연성 → 배열의 크기는 항상 문제에 맞춤화됨
단점: 할당시간 → 스택보다 힙으로부터 할당 시간이 더 김
f(){
    mt[] array={1, 2, 3, 4} // heap에 저장
}
C언어에서 malloc()free()로 힙의 할당, 회수 실행 → C의 배열에 대해 사용 가능
Java에서 비포괄적 배열 (non-generic arrays = 타입이 지정된 배열)은 고정 힙-동적 배열
이 배열은 한 번 생성되면, 동일한 첨자 범위와 기억공간을 유지함
힙-동적 배열
첨자 범위의 바인딩과 기억공간의 할당이 동적, 배열의 존속기간 동안 여러 번 변경 가능
장점: 다른 유형에 비해 유연성을 가짐 → 기억공간에 필요에 따라 늘거나 줄어들 수 있음
단점: 할당과 회수의 시간이 길어지고, 프로그램 실행 중에 여러 번 발생 가능
List<String> list = new ArrayList<String>();
list.add("홍길동");
STring name = list.get(0);
Java에서 포괄적 클래스인 ArrayList로 만들어진 배열
첨자를 사용하지 않음
add(), get() 메소드로 원소를 삽입하거나 가져오기도 함
Python 배열은 원소를 추가하거나 다른 배열과의 접합하는 메소드를 통해 크기가 들어남, 한 원소나 슬라이스들을 삭제 가능

배열 연산

배열 단위로 연산을 수행하는 연산
공통된 배열 연산: 배정, 집합, 동등과 비동등을 위한 비교, 슬라이스
배정: 배열 하나를 통째로 다른 배열에 배정
집합: A, B를 접합하거나 새로 만들어서 접합 가능 ****
동등과 비동등을 위한 비교: 내용으로 비교, 참조로 비교
슬라이스: 배열의 부분 구조, 배열의 부분을 한 단위로 참조하기 위한 매커니즘
파이썬에서 리스트를 슬라이스를 이용하여 표현 가능
C언어는 배열 연산을 제공하지 않음 (라이브러리 함수로 제공)
Java도 메소드를 통하는 경우를 제외하고는 배열 연산을 제공하지 않음
Python의 배열 연산
동적 배열의 모든 특성을 가지지만, 리스트 (List)라고 불림
배열 원소는 객체에 대한 참조 (동질 원소) + 객체들은 임의의 타입 간으 (이질적인 값 참조 가능)
참조 변경이기는 하지만, 배열 배정을 제공 (배정 연산 제공)
배열 접합 연산 (+)과 원소 멤버십 연산 (in) 제공
두 개의 비교 연산 (is)와 (==)를 제공
is는 참조가 같은데, ==는 내용이 같은지 검사
슬라이스 연산([:])도 제공

직사각형 배열과 톱니형 배열

직사각형 배열 rectangular array
모든 행들이 동일 개수의 원소들을 포함
모든 열들이 동일 개수의 원소들을 포함
Fortran, Ada, C#
톱니형 배열 jagged array
행들의 크기가 동일할 필요가 없는 다차원 배열
톱니형 배열으 다차원 배열이 실제로는 배열을 포함하는 배열일 떄 가능
C, Java는 톱니형 배열 지원, but 직사각형 배열은 지원하지 않음

배열 타입의 구현

배열의 발전 단계
초기 배열에서의 주요 진보: 동적 배열, 슬라이스
최근의 주요 진보: 연상 배열 (연관 배열)
배열 타입의 구현
배열을 구현하는 것은 기본 타입 구현보다 더 많은 컴파일 노력이 요구됨
배열 원소의 접근을 허용하는 코드는 컴파일 시간에 생성되어야 함
생성된 코드는 실행 시간에 배열에서 특정 원소들의 주소를 계산할 수 있어야 함
1차 배열
주소(list[k]) = 주소(list[하한]) + ((k - 하한) * 원소_크기)
list[k]로 표현되는 일차원 배열의 접근 함수
다차원 배열
주소(a[i, j]) = 주소(a[row_lb, col_lb]) + (((i - row_lb) * n) + (j - col_lb)) * 원소크기
두개 이상의 차원을 갖는 데이터 타입의 값들은 1차원 메모리로 사상
행-우선 순서: 첫 번째 행의 원소 먼저 저장 후 다음 행의 원소 저장 (대부분의 언어)
열-우선 순서: 첫 번째 열의 원소 먼저 저장 후 다음 열의 원소 저장 (Fortran)

연상 배열 Associative Arrays

원소 개수와 동일한 개수의 값(키 key)들로 인덱싱되는 순서를 갖지 않는 데이터 원소 모임
배열에서는 색인을 저장할 필요가 없음 (색인의 규칙성)
→ 연상 배열에서 사용자-정의 키들이 그 구조에 저장
각 원소는 항목들의 쌍 (키, 값)이 됨
원소들에 대한 탐색 → 배열보다 훨씬 좋음 (묵시적인 해시 연산이 매우 효율적)
리스트의 모든 원소들에 대해 오퍼레이션을 수행해야하면 배열이 더 효율적
지원 언어
Python에서 지원 (딕셔너리 → 값들이 모두 객체에 대한 참조)
Java에서 표준 클래스 라이브러리로 지원
기타 직접 지원 언어: Ruby, Perl, Lua
기타 표주 클래스 라이브러리로 지원되는 언어: C++, C#

레코드 타입 Record Types

개개의 원소들이 이름으로 식별 → 오프셋을 통해 접근되는 데이터 원소들의 집단체
대부분의 언어에서 필드 참조를 위해 도트 표기법을 사용
C 언어, C++, C#: struct 데이터타입으로 지원
Python, Ruby: 딕셔너리 또는 해시로 구현, 레코드 자체가 배열의 원소일 수 있음
Java: 데이터 클래스로서 정의 → 레코드 필드로서 역할을 수행

튜플 타입 Tuple Types

레코드와 유사한 데이터 타입, 원소들이 명명되지 않는다는 차이점
Python 튜플
myTyple = (3, 5.8, 'apple') # 레코드와 유사, 각 원소를 가리키는 이름이 없음
myTyple[1] # 색인이 0부터 시작, 두 번째 원소를 참조 -> 5.8
변경 불가 튜플 타입 제공
list 함수를 사용하여 배열로 변환할 수 있음
배열을 함수의 매개변수로 전달해야 하지만, 그 함수가 배열의내용을 변경하지 못하도록 할 때 사용

리스트 타입 List Types

함수형 언어인 LISP에서 처음 사용
최근에 일부 명령형 언어에 도입
Python 리스트
myList = [3, 5.8, "grape"] # 이름에 리스트 값을 할당하여 생성
x = myList[1]              # x에 5.8이 배정
del myList[1]              # 원소 삭제 가능

# 리스트 포괄 구문
[x * x for x n range(12) if x % 3 == 0] # [0, 9, 36, 81]
배열로서도 역할을 수행
파이썬 리스트는 변경 가능 (Python 문자열은 변경 불가, 튜플도 변경 불가)
리스트는 임의의 데이터 값이나 객체를 포함할 수 있음

공용체 타입 Union Types

변수가 프로그램 실행 중 다르 시기에 다른 타입의 값을 저장할 수 있는 타입
C, C++ 공용체
union flexType{
    int intEl;
    float floatEl;
};

union flexType el1;
float x;
...
el1.intEl = 27;
x = el1.floatEl;
el1은 4바이트 크기의 메모리
마지막 배정문은 타입 검사되지 않음
시스템이 el1의 현재 값의 현재 타입을 결정할 수 없기 때문
시스템은 27의 비트 표현을 float 변수 x에 배정
→ 값이 올바르게 배정되었는지 여부는 관심 없음

포인터 타입과 참조 타입 Pointer and Reference Types

포인터 타입

메모리 주소 + 특수값(nil)으로 구성되는 값들의 범위를 갖는 타입
NULL: 접근 가능한 주소가 아니다
nil: C언어 → 최근에 값이 없다
포인터의 두 가지 사용 용도
간접 주소 지정 형태
동적으로 할당되는 이라는 기억공간 영역의 한 위치를 접근하는 데 사용
힙 동적 변수 (무명 변수): 포인터나 참조 타입 변수로만 참조 가능, malloc() new 사용
힙-동적 변수
힘으로부터 동적으로 할당하는 변수 → 변수들은 이들과 연관된 식별자를 갖지 않음
포인터참조-타입 변수들에 의해서만 참조 가능
이름 없는 변수 = 무명 변수
포인터 연산
배정 연산: 포인터 변수의 값을 어떤 주소로 설정
역참조 연산: 포인터 변수에 바이딩된 메모리 셀이 가리키는 메모리 셀에 포함된 값 참조
포인터 문제
int *arrayPtr1;
int *arrayPtr2 = (int*) malloc(sizeof(int)*100);
arrayPtr1 = arrayPtr2;
free(arrayPtr2);
// 이제 arrayPtr1은 허상 포인터 -> 포인터가 가리키고 있는 힘 기억공간이 반환
허상 포인터: 회수된 힙-동적 변수의 주소를 포함하는 포인터
int *p1;
int p2;
p1 = (int *)malloc(sizeof(int)*100);
p1 = &p2;
// 이제 malloc()으로 할당받은 힘-동적 변수는 쓰레기가 됨
분실된 힙-동적 변수: 더 이상 접근할 수 없는 할당된 힙-동적 변수
쓰레기 garbage, 메모리 누수 memory leakage
C언어
j = *ptr // 역참조 연산
(*p).age // '*'로 역참조 후, '.'으로 필드 참조
p->age   // '->'로 역참조와 필드참조를 한꺼번에 처리
malloc() // 힙 메모리 관리르 위한 명시적인 할당
void *   // 포괄 포인터 -> 타입의 포인터, 역참조 불허

참조 타입

포인터와 유사, 참조 변수는 메모리의 객체나 값을 참조
참조에 대한 산술 연산은 무의미
Java 클래스 인스턴스들은 묵시적으로 회수허상 참조가 존재할 수 없음
Python의 모든 변수는 참조 변수임 → 묵시적으로 역참조
Java
String str1; // null(nil)로 설정
...
str1 = "This is a Java literal string";

포인터와 참조 타입 구현

포인터와 참조의 표현
인텔 마이크로프로세서 기반: 세그먼트 + 오프셋 (2개의 16비트 셀, 총 32비트)
허상 포인터
비석 접근 방법 → 널리 사용되는 어떤 언어도 사용하지 않음
잠금-키 접근 방법 제안
현재 제안된 것 중 가장 좋은 해결책으로 “묵시적 회수” 방법이 존재
프로그래머를 거치지 않고 힙-동적 변수를 회수
실행-시간 시스템이 묵시적으로 회수 → 허상 포인터 X
Java, LISP 등에서 사용
힙 메모리 관리
참조 계수기 방법의 회수 시점: 회수가 점차적으로 이루어지며, 접근될 수 없는 셀들시 생성될 때 수행 → 조기 접근 방법
참조 계수기 감소 연산 → 0이면 쓰레기, 가용 리스트에 반환
문제: 공간 부담, 실행 시간, 순환 연결 시 복잡
장점: 응용 프로그램 지연 감소
표시-수집 방법의 회수 시점: 가용 기억공간이 부족할 때만 회수가 실행 → 지연 접근 방법
쓰레기 누적 허용, 모든 셀 할당 → 쓰레기 모음 (여분의 지시자 가짐)
문제: 너무 드물게 수행, 수행되면 많은 시간 소요 → 점차적 표시 수집
Subscribe to 'Yerim-DevNote'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to 'Yerim-DevNote'!
Subscribe
👍