프로그래밍 언어(PL)

프언 ch5) Concepts of Programming Languages

chris3471 2025. 4. 10. 21:30
728x90
반응형

Introduction

1. Imperative languages는 폰 노이만 아키텍쳐의 추상 개념임

   - Memory

   - Processer

 

2. 변수들은 속성들로 특징화 되어있음

  - type을 디자인 하기 위해서 scope, lifetime, type checking, initialization, and type compatibility를 고려

 

Names

1. 고려사항 : 대소문자 구분, 예약어 or keywords 인지 아닌지 (예약어  ⊃  keywords)

 

예외) 

goto(Java)

- 키워드 x

- 예약어(reserved word) o

 

goto(python)

- 키워드 x

- 예약어 x

 

2. 길이가 너무 짧으면, 함축적인 의미를 가질 수 없음

 

3. Special characters

 - PHP : 모든 변수 이름에 $가 붙음 (ex) $abc

 - Perl : 모든 변수가 변수의 타입을 명시해주는 special chracters로 시작함

ex) @abc - 배열 표시

      %abc - 키와 value를 갖는 것

 

- Ruby : @로시작하는 변수는 instance variables이고, @@로 시작하는 변수는 class variables임

 

4. Case sensitivity (대소문자 구분)

 - 단점 : readability가 안 좋음 (이름이 대소문자에 의해서 다름)

    - C 기반 언어에서의 이름은 대소문자에 예민함, 기타 언어들은 구분하지 않음

    - C++, Java, C#에서 더 심각한 이유는?   미리 정의된 이름들에 대소문자가 섞여 있음 

          -> ex) IndexOutOfBoundsException 

 

5. Special words

 - readability를 높이는 것이 목적

 - keyword는 특정 문맥에서만 특별한 의미를 가지는 단어

 ex) this, super, public같은 단어는 특정 위치에서만 의미 있음

 

 - 예약어는 언어에서 미리 의미를 정해둔 단어로, 변수나 함수 이름으로 사용 불가

 - 예약어가 너무 많으면 충돌이 자주 발생함

 

Variables

1. 변수는 memory cell의 추상 개념임

2. 변수는 다음과 같은 특성에 의해 특징화 되어있음

  - Name

  - Address (메모리 주소)

  - Value (변수의 주소 or 값)

ex) x = 10 (x는 addr, 10은 value)

  - Type

  - Lifetime (변수의 주소가 시작~끝 까지 시간)

  - Scope (변수가 유효한 공간)

 

Variables Attributes

1. Address - 메모리 상의 주소와 연결

 - 실행 중 똑같은 변수의 주소가 계속 바뀔 수 있음

 - Aliases = 두 개 이상의 변수가 같은 메모리 주소를 가리키는 것

   -> 포인터(pointer)와 참조 변수(reference variable)로 만들어짐

   -> 단점 : readability가 떨어짐

 

ex) int *p = &a;       int& ref = a;

 

2. Value - 변수에 실제로 저장된 데이터

 (1) l-value : 변수의 주소

 (2) r-value : 변수의 값

 

3. Type - 변수의 값의 범위와 가능한 연산을 결정하는 속성

 - 정밀도(precision)도 타입에 따라 다름 ex) float vs double

 - Static type, Dynamic type

 

4. Abstract memory cell - 변수와 연결된 물리적인 메모리 셀 또는 셀들의 집합

 - 실제로 값이 저장되는 공간

 - 변수가 점유하고 있는 메모리 블록

 

 

Concept of Binding

1. 개체(entity)와 속성(attribute)을 연결 시키는 것

 - 변수에 타입이나 값을 연결시키는 것

 

ex)

 - 변수와 값의 바인딩 -> x = 5

 - 변수와 자료형(type)의 바인딩 -> int x;

 - 함수 이름과 함수 정의의 연결  

 

2. Binding time : 바인딩이 언제 일어나는지를 의미

  (1) Language Design Time (언어 설계 시점)

      - 연산자 기호(+, *)와 해당 연산 의미를 연결

  (2) Language implementation time (언어 구현 시점)

      - 타입의 세부 속성 결정 (c언어에서 int는 16비트 or 32비트 ?)

  (3) Compile time

      - 변수 이름을 타입에 바인딩

  (4) Load time (global/static 변수)

      - 정적(static) 변수와 메모리 위치를 연결 

  (5) Runtime (Heap/stack 영역)

     - 프로그램 실행 중

     - 지역(local) 변수나 동적 변수의 메모리 할당

 

 

Static and Dynamic Binding

1. Static Binding (정적 바인딩)

 - 프로그램 실행 전에(컴파일 타임에) 바인딩이 이루어지고, 실행 중에는 변하지 않는 바인딩 방식

 - C, C++

 

2. Dynamic Binding (동적 바인딩)

- 프로그램 실행 중(runtime)에 바인딩이 이루어지며, 실행 도중 바뀔 수 있음

- Java, Python

 

 

Explicit/Implicit Declaration (타입을 (비)명시적으로 선언))

1. Explicit Declaration (명시적 선언)

 - 변수의 타입을 직접 코드에 명시하는 방식

 - C, C++, Java

 

2. Implicit Declaration (비명시적 선언)

 - 선언문 없이 변수의 사용이나 기본 규칙에 의해 자동으로 타입이 정해지는 방식

 - Python, 자바 스크립트

 - 장점 : writability가 좋음

   단점 : reliability(신뢰성) 가 안 좋음

 

3. Type Inference (타입 추론)

 - 변수에 초기값을 기반으로 컴파일러가 타입을 추론

 - 명시적 선언과 암시적 선언의 중간 성격)

 

Dynamic Type Binding (Python, JavaScript, PHP, and C#)

- 타입을 그때 그때마다 선언

  -> 타입을 잘몬 준 경우 잡을 확률이 낮음 (타입을 바꿔버림)

 

장점 : Flexibility

단점 : 비용이 많이 듦(dynamic type 체크와 해석 때문에)

          컴파일러에 의해 type error detection이 어려움

 

대부분 explicit -> static , implicit -> dynamic

 

예외) $abc -> implicit -> static (PHP 문법?)

 

Variables Attributes

1. Storage Bindings & Lifetime

 - Allocation & Deallocation

 - Lifetime은 heap, stack, data 중 Data가 제일 긺

 - lifetime은 특정 메모리 셀에 변수가 존재하는 시간임

------------------------------------------------------------------------------------------------------------------------------

Categories of Variables by Lifetimes

1. Static

 - 프로그램 실행 전에 메모리 공간이 할당되고, 실행이 끝날 때까지 계속 그 메모리를 유지하는 변수

 - 항상 같은 메모리 위치를 사용

 - 프로그램이 종료될 때까지 살아 있음

 

장점 : 효율성(메모리 주소가 고정되어 있어서 빠른 접근(direct addressing)이 가능

          상태유지(함수가 이전 호출에서 어떤 값을 저장했는지 기억(history-sensitive) 가능

단점 : 유연성 부족(재귀에서 동일 변수를 공유하므로 병렬처리나 독립 실행 불가능)

          여러 실행 흐름 간에 같은 상태를 공유해서 의도치 않은 결과를 초래할 수 있음

 

예시 언어 : C, C++, Java, Python

 

2. Stack-dynamic

 - 변수가 선언될 때(run-time시점) 메모리에 할당되고, 해당 블록이 끝나면 자동으로 해제되는 변수

 - 선언과 동시에 실행되며, 런타임 스택(stack)에 저장됨

 - C에서 static이 아닌 지역 변수, Java 메서드의 지역 변수 등

 

장점 : 재귀(recursion) 가능 -> 호출마다 새로운 스택 프레임이 생성되기 때문

          메모리 절약 -> 필요한 만큼만 생성되고 끝나면 제거됨

단점 : 할당/해제 오버헤드 -> 호출할 때마다 스택에 변수 할당 -> 성능 약간 손해

          히스토리 유지 불가 -> 함수 호출 간에 이전 값을 기억할 수 없음

          간접 접근 -> 스택 프레임 기반으로 접근해야 해서 static 변수보다 느릴 수 있음

 

예시 언어 : C, C++, Java, Python

 

3. Explicit heap-dynamic

 - 변수의 메모리 할당과 해제를 프로그래머가 명시적으로 제어하며, 실행 중(runtime)에 효과가 발생하는 변수

 - 힙(heap) 영역에서 메모리 확보

 - 포인터/참조 를 통해서만 접근 가능

 - 할당 : new, 해제 : delete (C++)

    -> Java에서는 new만 있고, Garbage Collector가 해제 담당

 

장점 : 동적 저장 공간 관리 기능

            -> 실행 중 필요한 만큼 메모리를 할당할 수 있어 유연성이 높음

            -> 객체 수나 크기가 가변적인 상황에 매우 유리 (ex)동적 리스트, 트리 등

단점 : 비효율적 -> 힙 접근은 느리고, 메모리 파편화(fragmentation) 발생 가능

          신뢰성 문제 

              (1) 메모리 해제 누락 -> 메모리 누수

              (2) 이중 해체 -> 프로그램 충돌

 

예시 언어 : C, C++, Java

 

4. Implicit heap-dynamic

 - 대입문(assignment statement)에 의해 변수의 할당과 해제가 자동으로 이루어지는 변수

 - 프로그래머가 직접 타입도, 메모리도 선언하지 않음

 - 힙에 메모리 자동 할당

 - 모든 속성(type, size 등)이 런타임에 결정됨

 

장점 : 최고 수전의 유연성(flexibility) -> 타입, 크기, 구조 등 모든 것이 실행 중 결정됨

단점 : 비효율성과 오류 탐지 어려움

 

예시 언어 : Python

------------------------------------------------------------------------------------------------------------------------------

 

2. Scope (유효범위)

(1) Local variable (지역변수)

 - 해당 블록이나 함수 안에 선언된 변수

 - 선언된 범위 내에서만 사용 가능

 

(2) NonLocal variable (비지역 변수)

 - 현재 함수나 블록에는 선언되어 있지 않지만, 외부에서 정의되어 접근 가능한 변수

 - 보통 상위 scope에 존재

 

(3) Global variable (전역 변수)

 - 프로그램 전체에서 접근 가능한 변수

 - 사실상 nonlocal 변수의 특별한 형태

 

(4) Scope Rule

 - 언어마다 어떤 스코프의 변수를 우선 참조하는지 규칙이 다름

 - 대부분은 가까운 범위(local) 우선

    -> 이를 가까운 변수로 가려진다(hidden) 라고 표현

 

3. Static Scope (정적 유효범위) -> 우리가 사용하는 언어의 유효 범위

- 소스 코드의 구조(문자 그대로 "프로그램 텍스트")를 기준으로 변수가 어느 스코프에 속하는지를 컴파일 시점에 결정하는 방식

- 컴파일러 또는 인터프리터는 변수를 사용한 위치에서 가장 가까운 선언을 찾음

   -> 먼저 로컬(local)에서 찾고, 없으면 바깥쪽 스코프로 점점 확대

 

- 중첩 스코프(Nested Scope)

 -> 한 스코프 안에 다른 스코프를 중첩해서 정의할 수 있음

 

ex) def ____

           def ______

                 def ______

 

Evaluation of static scoping

=> 많은 상황에서 잘 쓰임

Problem)

  - 대부분의 경우, 더 많은 접근이 가능

  - 초기 구조는 파괴되고, local variables이 종종 global이 되어버림 (활용도가 낮음)

 

----------------------------------------------------------------------------------------------------------------------------------

Blocks

- 프로그램 유닛 내부에 static scopes를 만드는 방식

 

 

The LET Construct

let 구문 : 이름을 값에 바인딩(bind) 하고, 그 이름을 사용하여 계산을 수행하는 2단 구성의 구문

 

let의 구성 (두 부분)

1. 이름-값 바인딩

  - 변수 이름을 값(또는 표현식)에 연결

  - ex) val x = 5

2. 이 바인딩을 사용하는 본문

  - 바인딩된 이름을 사용하여 새로운 계산 수행

  - ex) x+1

 

----------------------------------------------------------------------------------------------------------------------------------

4. Global Scope -> 자바는 불가능 (public static을 사용하면 의미적으로 전역변수)

(1) C, C++, Python 공통 사항

  - 프로그램은 보통 여러 함수 정의들이 나열된 구조로 구성됨

  - 이들 언어에서는 변수 밖에서 변수 선언 가능 -> 전역 변수

 

(2) C, C++의 특성

  - 선언(declaration) vs 정의(definition)

    -> 선언은 변수의 존재만 알리는 것이고, 정의는 실제 메모리 공간을 할당함

 

(3) Python의 특징

  - 전역 변수는 함수 안에서 읽기는 가능, 하지만 쓰기나 수정은 제한됨

    why ? 전역 변수에 대입하면 자동으로 지역 변수가 생성되기 때문에

  해결법 : global x 이런 식으로 사용하면 가능

 

 

 

 

728x90
반응형