일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- swift
- 인공지능
- 데이터베이스
- 스프링부트
- 오라클
- javascript
- DBMS
- MySQL
- 스프링부트 웹 소켓
- jenkins
- CD
- Xcode
- 42seoul
- Spring
- 다이어리
- JPA
- 아이패드다이어리
- sql
- 프로그래밍언어론
- CI
- 리눅스
- 오블완
- springboot
- AI
- IOS
- 소켓
- libasm
- 스프링
- 네트워크
- 티스토리챌린지
- Today
- Total
Hi yoahn 개발블로그
#3 프로그래밍 언어 설계의 원칙, 구문 정의, 기초 의미론 본문
1. 프로그래밍 언어 설계의 원칙
- 바람직한 언어를 판단
- 좋은 프로그래밍 언어를 선택할 수 있다.
1) 효율 Efficiency
- 돈, 시간, 공간의 효율
▶ 최적화 용이성
- 실행 효율
- 초창기 언어일 수록 중요(컴퓨터가 귀한 자원)
- 프로그램이 짧은 시간&적은 메모리로 실행을 완료하려면 실행 파일이 효율적이어야 함
-> 컴파일러가 실행파일을 효율적으로 만들도록 하는 것: 최적화
- 최적화가 얼마나 정교하게 높은 수준으로 이루어질 수 있는가
최적화가 쉽게 이루어지려면 언어가 어떻게 설계되어야 하는가
-> FORTRAN = 변수 길이 6글자 이하 (word=36bit, 글자인코딩=6bit=1글자)
-> 판독성은 떨어지고 인건비는 올라감
▶ 번역 효율
- 컴파일 시간이 빠른 것
1. go to 레이블이 go to 문장보다 먼저 나온 경우만 허용
(레이블이 뒤에 있으면 go to 목적지를 바로 파악할 수 없어 나중에 레이블을 읽은뒤 다시 번역 -> 번역 2번)
2. 연산이 이루어질 때 변수 타입을 체크해서 이상하면 오류를 발생시키는 코드를 컴파일러가 추가 -> 컴파일 느림
-> 신뢰성은 올라감
- 타입체크하는 코드를 컴파일러가 만들지 않으면 -> 번역 효율은 상승, 신뢰성은 떨어짐
▶ 구현 용이성
- 컴파일러 작성이 쉬운 것
- block { }: 효율적으로 컴파일하는 방법 -> Algol60 의 장애 요인
▶ 작성 용이성
- 프로그래밍(작성)의 효율
- 표현력 : 사람 머릿속의 추상화 매커니즘을 프로그래밍 언어가 가지고 있는 것
▶ 판독성
- 추상화로 가늠할 수 있다
- 판독성이 떨어지면 유지보수가 어렵다 -> 생산성 ↓
- 유지보수 단계에서 중요
▶ 신뢰성
- 버그 발생 확률이 적은 것
▶ 유지보수 용이성
- 판독성과 밀접한 관계
2) 규칙성 Regularity
언어 기능들의 통합 정도를 가늠하는 척도
- 특정 구문 사용에 별난 제약이 적은 것
- 구문 사이에 이상한 상호 작용이 적은 것
- 언어 동작에 예상치 않은 일이 적은 것
1. 일반성 generality
- 구문 사용에 특수 케이스를 피하고 밀접하게 관련된 구문을 일반적인 것으로 통합
- 파스칼: 기본적인 것들을 제약없이 다양하게 조립할 수 있는 조립 방법을 제공 =일반성
2. 직교성 orthogonality
- 구문이 어떠한 의미 있는 형태로도 결합 가능
- 프로그래밍 언어의 요소를 결합할 수 있도록 허용하는 것
직교성이 너무 높으면 -> 복잡, 비정상적인 상황이 발생할 가능성 높아짐
3. 일률성 uniformity
- 비슷한 형태는 비슷한 의미를 가진다. (다른 형태는 다른 의미)
-> 함수{ }, 클래스{ }; -> 의미가 비슷한데 다른 모양 -> 일률성이 깨짐
3) 단순성
- 도구는 단순해야 배우기 쉬움
- 너무 단순하면 작성용이성이 떨어짐, 표현력 부족
-> 최적의 선을 넘으면 안됨
4) 표현력
- 표현력이 좋으면 같은 내용을 더 간결하게 나타낼 수 있다
-> 판독성과 상충 가능
5) 확장성
- 사용자가 언어에 새로운 기능을 추가할 수 있는 매커니즘 존재
(연산자 오버로딩 -> 확장성, 표현력↑, 복잡성도 올라감)
2. 프로그래밍 언어의 구문 정의
언어 -> 구문, 의미
한국어 문장
-> 1. 적법한 단어인지, 2. 문법 기준에 맞는지
구문 level
2.1 어휘 구조 Lexical Structure
- 프로그램을 구성하는 단어, 토큰의 구조
(토큰이 어떤 구조를 가지고 있느냐)
> 토큰 부류
- 예약어 : if, while, 정해진 용도로만 사용 가능, 다른 용도 사용 불가
- 리터럴 / 상수 : 글자 그대로가 자신의 값 / 상수
- 특수 기호 : +, - , * , /
- 식별자 : 변수명, 함수명, 클래스명, 상수 이름,,
(keyword : 사용자가 따로 선언해서 사용하는 것을 허용하는 경우)
< 토큰 기술 방법 >
1) 자연 언어
- 자연언어로 설명
2) 정규 표현 regular expression
- 일종의 언어
접합 , 선택 | , 반복 * +, ? .
(a|b)*c
2.2 구문 구조 Syntactic Structure
프로그램에서 토큰들이 조합되는 규칙 (=문법)
< 구문 구조 기술 방법 >
- 문맥-무관 문법 CFG
- 배커스-나우어 BNF
- 확장 배커스 나우어 형식 EBNF : BNF + 정규표현
- 구문 다이어그램
-> 모두 표현력이 동일하다
- 언어 구문의 정의는 의미와는 무관함
- 형식이 문법에 맞는가, 문법 형태만 체크
3. 기초 의미론
언어=문장들의 집합
-> 언어에 포함된 모든 문장들의 의미를 파악하면 그 언어를 파악한 것이다.
but, 불가능 -> 문장의 수 무한개
-> 문장들의 공통적인 요소들을 완전히 이해하고 나면 공통 요소들을 조립하여 큰 단위가 되는 의미 합성,,
-> 문장의 의미를 익혀나가면 모든 문장을 보지 않고도 어떤 문장이라도 이해할 수 있다.
프로그래밍 언어의 문장을 구성하는 요소
문장 sentence == 문법적으로 올바른 하나의 프로그램
C++ 언어 == c++로 작성 가능한 모든 프로그램의 총 집합
3.1 구문과 의미
프로그래밍 언어의 의미 기술은 구문 기술보다 훨씬 어렵다
- 구문 기술 표준 존재: CFG, BNF, EBNF, Syntax Diagram
- 의미 기술은 확고한 표준이 없다
● 프로그래밍 언어의 의미를 기술하는 일반적인 방법들
1. 언어 참조 매뉴얼
-> 자연 언어로 기술
- 현재까지도 가장 많이 사용됨
2. 정의적 번역기 (컴파일러/인터프리터)
- 컴파일러를 돌려보면서 의미를 유추
- 언어에 대한 명확한 정의를 바탕으로 컴파일러를 작성하는 것이 제대로 된 순서
but, 거꾸로, 질문에 대해 컴파일러를 돌려보면서 의미를 유추하는 것 (본말이 전도)
- 컴파일러를 잘못 작성한 경우, 잘못된 의미가 퍼질 수 있다.
3. 정형적 정의
- 이론적으로는 가장 이상적이지만 이해하기 힘들고 익혀 사용하는 것도 힘들다
- 다수가 동의하는 표준도 없음
3.2 속성과 바인딩
1) 언어
- 그 언어에 속하는 가능한 모든) 문장들의 집합
한국어=한국어 문장들의 집합
C++ = C++ 문장sentence(프로그램) 들의 집합
c++ sentence=c++ statement * N
- 언어의 의미 = 그 언어에 속한 '모든' 문장들의 의미
- 내가 모르는 것을 이해하려면 내가 이해하고 있는 것을 가지고 대응시키는 방법밖에 없음
2) 환원주의 Reductionism (근대화~현대 주된 사상, 근대 서양 사상 ->환경오염)
: 전체를 쪼개서 부분을 이해하면 전체를 이해할 수 있다
<-> 전일주의 Holism (환경, 생태 쪽의 사상, 동양): 부분의 합이 전체는 아니다. 쪼개지 않고 그대로 이해해야 함
- 어떤 높은 단계의 개념을 더 낮은 단계의 요소들로 분할하여 정의하는 것
-> 복잡한 것을 단순한 것들의 조합으로 생각하는 것
$ 언어 의미에 대한 환원주의적 접근
- 임의의 한 문장의 의미
= 문장을 구성하는 절, 구, 개별 단어 들의 의미, 결합될 때 추가되는 의미, 뉘앙스 -> 분석하여 파악 가능
- 임의의 한 C++ 프로그램의 의미
= 프로그램을 구성하는 함수, 블록, 개별 식별자 등의 의미, 결합될 때 추가되는 의미, ,-> 분석하여 파악 가능
3) 프로그램을 구성하는 가장 기본적인 단위는 이름 | 식별자
- 변수, 상수, 연산자, 타입 ----> 공통요소 : 이름
int num;
const double PI = 3.14;
m%n;
typedef int score;
- 이름 += '종류'
이름에 종류라는 성질이 추가
-> 변수, 상수, 연산자, 타입 등으로 나눠짐
★ 가장 기본적인 단위의 의미 파악
- 기본 개체/구성체의 의미 -> 개체에 연계된 속성 값이 결정되면서 파악됨
개체의 연관된 속성을 파악하면서 그 개체에 대해 파악
속성: 연계된 개체의 의미를 결정하는 성질
- 세상 모든 객체들에 대한 모든 속성을 파악 -> 실제와 같은 가상 세계 : 매트릭스
4) 바인딩
- 개체/이름과 속성 값을 묶어주는 것이 바인딩
- 선언문 = 바인딩 이루어지는 매커니즘
★ 바인딩 타임
- 속성 값이 구해지면 그것이 개체/이름에 엮이는 시점
1. 정적 바인딩
- 실행시간 이전에 일어나는 바인딩
- 정적 바인딩으로 연결되는 속성 -> 정적 속성
> 언어 정의 시간: 언어 설계자가 의미를 정함 int, double, ,, 내장 타입
> 언어 구현 시간: int를 메모리 내에서 몇 바이트로 표현,,
> 번역 시간/컴파일 시간: 상수, 타입, 초기값
> 링크 시간: 라이브러리 함수를 연결, 함수 호출시 어떤 코드가 실행될건지
> 적재 시간: 많은 변수들의 실제 메모리 위치 (재배치 가능한 코드인지 결정)
-> 메모리 위치를 미리 정할 수 없다. 총 필요한 공간은 미리 알 수 있음
-> 적재/실행시간 이후에 실제 메모리 주소가 확정됨
2. 동적 바인딩
- 실행하는 동안 일어나는 바인딩, 머신 사이클 이후에 일어남
- 동적 바인딩으로 이름에 엮이는 속성 -> 동적 속성
> 실행 시간 / 수행 시간
※ 속성들의 바인딩 타임에 따라 언어의 의미에 영향
3-2. 기초 의미론
3.3 선언, 블록, 영역
- 바인딩은 묵시적, 명시적으로 이루어지는 것이 가능
묵시적 바인딩
- 오류를 잘 인식하지 못함 -> 잘못된 결과, 오류 찾기 어려움
- 주어진 정보의 부족으로 오류를 판단하는 근거가 부족함
명시적 바인딩
- 오류를 잘 잡음
- 선언으로 컴파일러에 정보를 더 많이 주는 것
*현대로 올수록 명시적으로 설계되는 경향
> 다수의 바인딩이 묵시적으로 설계된 언어도 존재
-> 주로 변수 이름 붙이는 방식으로 결정 (변수 이름에 규칙 부여)
- FORTRAN: 변수 식별자가 I~N 으로 시작하면 정수타입, 그 외에는 실수 타입
- BASIC: 변수 식별자가 %로 끝나면 그 변수 타입은 정수, $로 끝나면 스트링, 그 외는 실수
정의 : 어떤 개체에 관련된 (잠재적인 모든) 속성을 바인딩하는 선언
선언 : 몇개의 부분적인 속성을 바인딩하는 선언
- 모든 속성 중 일부만 바인딩
- 나머지는 링크/적재/실행 이후에 바인딩
함수 원형 -> body가 생략, 데이터 타입만 기술 -> 선언
블록
- 프로그래밍 언어에서 선언으로 바인딩되는 범위를 제한
- 선언과 연계된 특정 구조를 지칭하는 이름
- Algol60에서 최초로 제안
연속된 선언,문장들의 연속을 { }, begin-end, do-end로 둘러싼 형태
지역적 선언: 특정 블록과 연관된 선언
비지역적 선언: 블록 바깥에 있는 선언
- 모든 Algol60 후손들은 블록 구조를 다양하게 제안 (거의 모든 현대 프로그래밍 언어)
- 블록 내에 다른 블록 내포 가능
-> 내부 블록에 동일한 이름 재선언 가능
선언이 연계된 블럭 외의 구조들
구조체, 클래스, namespace, package, module
Scope 영역
- 프로그램에서 바인딩이 해체되지 않고 유지되고 있는 범위
- 선언을 통해 부여된 의미가 유지되고 있는 범위
-> 이름/프로그램의 의미를 알기 위해서는 영역을 분간해야 함
(영역 마다 의미를 구분)
# 정적 영역 바인딩
static scoping
- 프로그램 구조와 관련있다. (실행과는 관련 없음)
- 바인딩을 만드는 선언이 나타나는 블록과 내포된 블록에 영역이 한정됨
(프로그램 겉모습에 따라 영역 한정)
- 눈으로 이름의 의미를 파악 가능
# 동적 영역 바인딩
- 블록 구조 언어에서 바인딩을 만드는 선언이 나타난 블록과 그 블록을 호출한 블록에 영역이 한정됨
- 프로그램 실행과 관련됨
내부에 있는 선언이 우선 적용
- 동일한 이름의 선언이 내포된 블럭에 나타나면 바깥에 선언된 이름을 가림
-> 영역 구멍 = 기존 바인딩이 해체된 것은 아니므로 영역은 지속됨, 구멍에서 안보이는 것
가시성: 선언의 바인딩이 실제 적용되는 범위, 그 이름이 보일수 있는지없는지
3.4 이름 식별과 다중 적재
1) 이름 식별
- 이름=프로그래밍 언어를 구별하는 가장 기본적인 요소
- 개체에 연계된 속성 값이 그 개체의 의미를 결정
- 이름이 어느 영역에 포함된 것인지 판단해야 그 이름의 의미를 결정할 수 있음
(-> 프로그램의 의미->프로그래밍 언어의 의미 아는 것)
- 이름 의미를 구분하지 못하면 프로그램 의미 알 수 없음
2) 다중적재 Overloading
중복정의, 과적
- 하나의 이름에 여러 의미가 실려있는 것
- 다중적재를 지원하지 않는 언어는 없다.
(지원하지 않으면 이름 갯수가 많아짐)
다중적재 식별
- 이름의 여러 의미 중 하나를 고르는 것
다중적재-> 연산자, 함수
-> 다중적재된 이름의 의미를 파악하기 위해서 피연산자/매개변수의 타입, 개수 정보를 이용하여 의미 판단
(함수 호출 - 매개변수의 개수&타입 = 호출 문맥 (calling context))
연산자의 의미는 피연산자에 의해 구별되고,
함수의 의미는 매개변수의 개수와 타입에 의해 구별된다.
- Ada
다중적재 식별에 호출 문맥 + 반환 타입도 이용
3.5 심볼 테이블
바인딩 정보가 저장되는 곳
- 선언에서 이루어진 바인딩이 저장되는 자료구조
이름과 이름에 연관된 속성들을 가짐
- 컴파일과정에서 선언문을 보고 바인딩 정보를 추출해서 심볼 테이블에 저장
-> 컴파일과정에서 선언문을 보고 심볼 테이블에 바인딩 정보를 추출해서 저장
-> 나중에 그 이름이 사용될 때 심볼테이블을 보고 바인딩 정보를 참조해서 코드를 번역
이름 | 속성 / 바인딩 |
i | 종류=변수, 타입=int, 메모리=4byte, 영역,, |
★ 컴파일러가 관리하는 핵심 자료
-> 엔트리의 검색/삽입/삭제가 빈번하게 일어남
** 효율적으로 작업할 수 있도록 그에 맞는 다양한 자료 구조를 이용해서 심볼 테이블을 구현
(이진검색트리, 해시 테이블)
- 블록-구조 언어를 위한 심볼 테이블 -> 논리적으로 스택처럼 작동함
(괄호와 같은 의미, 블록의 영역들이 괄호처럼 사용)
- 심볼 테이블에 저장될 바인딩을 만드는 선언의
바인딩 타임에 따라 같은 프로그램이라도 다른 의미를 가짐(다른 결과)
1) 컴파일 타임에 선언 처리-> 정적 영역 바인딩
-> 컴파일 하면서 선언 처리
현대 프로그래밍 언어들에서 많이 사용
2) 실행 시간 때 선언 처리 -> 동적 영역 바인딩
-> 실행 때 심볼 테이블이 비어있지 않음, 전역 이름들이 처음에 들어있다.
함수, 전역변수, main,
- 이름에 대한 지역 선언이 없으면 바깥 블록의 선언을 찾는 것이 아니라
현재 위치한 블록을 호출한 블록의 지역 선언을 찾는다.
- 동적 영역 바인딩의 경우,
-> 같은 문장이 다른 결과를 가져올 수 있음
(함수가 호출되는 지점에 따라 다른 결과)
문제가 많아 사용하는 언어가 거의 없음
Lisp이 초기에 동적 영역 바인딩을 사용했으나, 나중에 정적 영역 바인딩으로 바꿈
# 동적 영역 바인딩의 문제점
- 프로그램 구조만 보고 변수가 어느 선언이 사용될 지 알 수 없음
- 프로그램을 실행해봐야 알 수 있다.
실행 전에 비지역변수가 어느 것일지 예측 불가
-> 데이터 타입도 동적으로 선언 = 동적 타입 바인딩
(동적 영역 바인딩은 동적 타입 바인딩을 수반함)
# 동적 영역 바인딩의 장점
- 매우 동적이고 인터프리터되는 언어는 실행 시간에 많은 속성들이 바인딩됨
-> 심볼 테이블 관리를 실행타임으로 미뤄도 부담이 없다
(어차피 실행시간에 많은 속성 바인딩됨 -> 영역&타입이 동적으로 되어도 OK)
- 인터프리터에서는 동적 영역으로 구성된 환경을 구성하는 것이 쉽다. (정적 영역보다 간단, 사용자가 읽기 쉬운 것은 아님)
- 프로그램 환경에 따라 실행 결과가 결정됨
- 연산과 함수는 의미에 있어 본질적인 차이가 없음, 구문의 차이만 존재
- 연산자는 특수문자 사용, 함수는 식별자 사용
- 연산 = 중위표기, 피연산자의 개수가 3개 이하
- 함수 = 전위표기, 매개변수 개수가 무제한
- 극단적인 다중적재
- 일부 언어에서 (변수, 타입, 함수)종류별로 다른 심볼 테이블을 사용 -> 동일한 이름을 여러 종류로 사용하는 것이 허용됨
- Java => Class A, int A, void A(A A)
- 키워드를 예약하지 않는 경우 사용자가 이름으로 사용할 수 있음
- 프로그램이 알아보기 어려워진다.
3.6 할당, 수명, 환경
1) 환경
- 이름에 바인딩된 메모리 위치들의 집합, (특정 주소 X, 추상화된 메모리 칸)
- 환경 구성 방식
적재 시간에 정적으로 구성
실행 시간에 동적으로 구성
적재/실행 시간에 정적/동적으로 구성(혼합) -> 바인딩 시간의 차이
-> 각 방식에 따라 프로그래밍 언어의 능력 차이가 발생 = 언어의 의미 차이
- 이름들이 메모리 위치에 바인딩 됨
* 상수/데이터 타입의 이름은 컴파일 시간에만 존재 (실행 시 메모리 위치에 바인딩 X)
(컴파일러가 컴파일 할 때만 심볼테이블에 저장해두고 상수를 값으로 교체)
2) 할당
- 이름에 메모리 위치가 바인딩되어 프로그램의 환경이 구성되는 작업을 지칭
- 전역 변수들은 정적으로 할당됨
지역 변수들은 해당 블록이 실행될 때 동적으로 할당되었다가 끝나면 할당 해제됨
-> 스택과 유사하게 동작
- 함수의 호출 = 활성화
- 함수가 활성화 될 때 할당된 메모리 = 활성 레코드
(함수의 지역변수들을 위한 메모리 할당)
3) 수명
- 환경에서 선언 처리의 결과로 생기는 할당된 메모리의 구역 = 객체
- 객체의 수명 -> 환경에서 할당되어 있는 기간 (시간적 개념)
(scope-> 공간적 개념)
: 메모리에 할당된 시점부터 해제될 때까지의 기간
- 프로그램에서 변수가 유효한 범위의 영역이 실행되는 동안은 그 변수를 위한 메모리 영역이 유지되다가 영역을 벗어났을 때 메모리가 해제되는 것이 좋다
- 객체의 수명이 참조 가능한 영역을 넘어서거나 객체 수명을 넘어서 참조 가능하면 문제
어떤 할당이 더 효율적인가?
- 정적 할당
- 모든 종류의 바인딩에서 정적 바인딩이 동적 바인딩보다 효율적 (실행시간 전에 한번만 하면 됨)
- 동적 바인딩이 정적보다 유연함(바인딩이 실행시간으로 미뤄지므로)
-> FORTRAN: 효율성 중시
컴파일 과정에서 필요한 메모리 총량을 분석하여 프로그램이 적재될 때 메모리에 환경 구성을 완료한다.
(메모리 다루는 것에 CPU 시간 낭비가 없음)
전역 변수는 실행 전에 정확히 필요한 만큼만 할당이 가능하다.
-> 모든 변수를 정적으로 할당하면 안됨
(전역변수, 지역변수, 포인터)
★ 모든 변수를 정적으로 할당하면 재귀, 동적 자료구조 사용이 불가능하다.
factorial() 재귀 함수
변수가 정적으로 할당된 경우
-> 재귀호출되는 함수의 매개변수는 호출될 때마다 별도의 메모리 공간을 가져야 함
정적으로 할당되면 한 메모리 공간을 재사용하게 되어 이전 값을 참조할 수 없다.
- 컴파일러가 함수가 몇번 호출되는지 예측할 수 없다.
☆ 정적 바인딩이 항상 효율적이다.
-> 프로그램이 수행되기 위해 필요한 메모리 총량을 실행전에 미리 파악할 수 있다면 미리 할당하는 것이 좋다.
=> 메모리 할당은 CPU가 관리, 미리 다 해두면 CPU의 오버헤드도 없다.
★ but, 필요한 메모리 총량을 미리 알 수 없음
-> 어차피 미리 알 수 없어 실행시간에 메모리 관리 필요
- 그러므로 아직 활성화되지 않은 block의 지역변수를 미리 할당할 필요가 없다
-> 필요한 시기에 메모리 할당 & 해제
선택
재귀 호출 허용 VS 모든 변수 정적 할당
- 재귀 호출이 중요한 제어 추상화 매커니즘, 편의성
★ 스택에 포인터 변수의 동적 할당은 안됨
스택에 포인터 변수의 공간을 할당하면, 스택은 block이 끝나면 활성레코드도 pop되어야 하는데 해당 변수가 스택 TOP에 없게 됨
# 기억장소 부류
- 메모리 위치가 바인딩되는 할당 시점에 따라 구분
1) 정적
- 전역변수
- 메모리의 전역 구역
2) 동적
1> 자동 / 스택 기반
- 지역변수
- 메모리의 스택 구역
(해당 블록이 활성화 될 때)
컴파일 시간에 함수가 활성화되면 필요한 활성레코드의 사이즈 정보를 컴파일러가 파악
2> 수동 / heap 기반
- 포인터 변수
- 메모리의 heap 구역
(메모리를 동적으로 할당하는 코드 실행 시 바인딩)
3.7 변수와 상수
1) 변수
- 저장하고 있는 값이 수행되는 동안에 바뀔 수 있는 객체
주요 속성: 할당, 영역(메모리 위치, 저장된 값)
- 이름, 메모리 위치, 값, 데이터 타입, 저장공간 크기
★변수는 메모리에 바인딩 되어야 한다.
< 배정문 >
- 변수의 값을 바꾸는 기본적인 방법
- 모든 언어에 존재
x = y; (일률성 x)
① 복사에 의한 배정
- 우변의 식의 값을 좌변의 위치에 복사
(좌변이 가리키는 위치에 우변의 값을 복사)
- 기억장소 의미론, 값 의미론
- 우변 = 값 r-value, 메모리에 저장된 값을 나타냄
- 좌변 = 위치 l-values, 변수에 연결된 주소를 나타냄
② 공유에 의한 배정
- 우변의 위치가 좌변으로 복사됨
- x, y는 별칭
구조체, 배열, 클래스는 복사에 의한 배정을 하는 경우 복사할 값이 많아짐 (공유가 효율)
- 얕은 복사
포인터의미론, 참조 의미론
- 복사 효율성 & 원치 않은 부수적 효과
** X를 바꾸면 Y 도 값이 바뀜
-> 프로그램에 숨겨진 의미가 존재 -> 가독성 떨어짐
표준적 구현
- *포인터와 묵시적인 *참조인출을 이용
- 주소를 복사
③ 복제에 의한 배정
- 깊은 복사
- 새로운 위치에 우변의 값을 복사
2) 상수
- 프로그램 전반에 걸쳐 고정된 값을 가지는 개체
- 변수와 유사, 위치 속성은 부재 (변하지 않으므로 컴파일 시 값으로 바뀜 -> 메모리 필요 없음)
[종류]
- 리터럴(실제 값), 현시 상수 PI
상수가 바인딩 되는 시점
- 컴파일 시간 상수
- 정적 상수
- 동적 상수
(프로그래밍언어마다 상수 허용 범위가 다름)
3.8 별명, 허상 참조, 쓰레기
1) 별명
- 함수 호출, 포인터 변수, 공유에 의한 배정 등에 의해 발생
<위험성>
- Alias는 side effect를 야기
- 문장 수행 이후에도 유지되는 변수 값의 변화
- 모든 부작용이 나쁜 것이 아님, 배정문은 그것을 일으키도록 의도된 것
- 잠재적 해로움
2) 허상 참조
- 환경에서 할당 해제되었지만 프로그램에서 여전히 접근 가능한 경우 (허상을 가리킴)
- 한 객체에 대하여 환경에서 수명을 넘어 참조
- 포인터가 할당 해제된 객체를 가리킬 때 발생
Java는 허상 참조 발생하지 않음
- 명시적인 포인터 부재
- 주소 연산자& 부재
- 메모리 할당 해제 연산자 free, delete 부재
<위험성>
- 틀린 결과
- 메모리에 있는 다른 프로그램을 오염
- 원인 찾기 힘든 실행 오류
*허상 참조가 매우 위험 -> 명시적 메모리 할당 해제 연산자를 없앰
대신 쓰레기를 자동으로 수거 ->CPU 오버헤드
(자바 Garbage collection)
3) 쓰레기
- 환경 내에 할당, 더 이상 접근 불가
- 메모리 해제하기 전, 다른 주소를 넣으면 이전의 주소는 참조 불가, 반납 안됨
* 쓰레기가 많아지면 메모리 부족으로 실행 중단될 수 있음
'sswu > 프로그래밍언어론' 카테고리의 다른 글
#4 데이터 타입과 제어 구조 (0) | 2020.06.29 |
---|---|
#2 프로그래밍 언어 개발의 역사 (0) | 2020.06.08 |
#1 프로그래밍언어론 - 추상화 (0) | 2020.05.24 |