프로그래밍언어

[프로그래밍언어] Names, Bindings, and Scopes

명령형 언어는 폰노이만 아키텍처의 추상화입니다. 

메모리에서 명령어와 데이터를 저장해서 기계메모리셀의 언어에서의 추상화(변수)가 이루어지죠.

RAM에서 CPU로 load(fetch), CPU에서 RAM으로 store되는 life cycle을 가집니다.

 

변수는 이때 특징들을 가집니다. (키워드)

scope(범위), lifetime(메모리에 적재되어 소멸까지의 시간), type checking(a=b;에서 a와 b는 상호호환), initialization, and type compatibility.

 

Names

변수를 논의하기 전, 변수의 기본속성 중 하나인 이름을 논의해야합니다. 

Identifier(식별자)라는 말과 혼용되어 쓰입니다. 

1. 설계시 고려해야하는것은, 이름들이 case sensitive한가?(대소문자구분 문제)  이 이름은 reserved words(예약어)인가 keywords(키워드)인가?

(reserved word:keyword포함해서 앞으로 사용될 것들(변수로 사용못함)//keywords:현재언어에서도 쓰고있는 reserved word)

ex) keyword: int, float, if등 _ if도 키워드이긴하지만 예약어가 아니면 변수명으로 사용할 수 있음. 

2. Length: 너무 짧으면 함축적이지 못하게 됩니다. 

3. Special Characters: PHP-($), Perl-($,@,%:scalar,array,hash), Ruby-(@ vs @@)

여기서 스칼라는 벡터와 구분되는 개념이다. 스칼라는 방향이 없는 항상 (+)의 속력이라면, 벡터는 방향이 있는 (+/-)의 속도다. 

4. Case Sensitivity: 이는 readability(가독성)를 해칩니다. C++, Java, C#에서 심한데, 예를 들어 Java메소드 ParseInt에 대해서 parseint와 Parseint는 인식이 안되는 것입니다. writability는 오르지만 readability는 해칩니다. 

PHP에서 Case-insensitive(ex. if,else,null,echo등)는 대소문자 구분하지 않고 case-sensitive(variables,array keys등)는 대소문자 구분을 한다. 

5. Special Words(keywords or reserved words)

keyword는 특정 컨텍스트에서만 적용됩니다. ex)Real: "Real VarName"에서는 데이터타입이고, "Real=3.4"에서는 변수입니다.

reserved word는 사용자정의이름(즉, 식별자)으로 사용될 수 없습니다. ex)Java_goto 의미가없고, 쓰이지않는다. 

keyword는 reserved word의 부분집합입니다.

 

Variables

변수는 메모리셀의 추상화입니다. 

6개묶음(sextuple) 특징(attributes)로 구분됩니다. Name, Address, Value, Type, Lifetime, Scope. 

<Name>

모든 변수가 가지고 있지않습니다. 즉 이름없는 변수(nameless variable)이 있습니다. 

예를 들어

int *p1;

p1=new int; 

하면 이름없는 변수가 선언되었습니다.  이는 explicit-heap-dynamic variables로 nameless(abstract)메모리 셀에 explicit directives가 값을 할당과 해제합니다. ex) new, delete in C++

<Address>

메모리가 어디에 변수가 할당된 것인지에 대한 속성. 주소값을 lvalue(location value)라고 부르기도 합니다.

두 변수가 같은 메모리주소를 접근한다면, 이를 aliases(별칭)이라고합니다.(conflict 발생가능) 

별칭은 C, C++에서 unions, 포인터 등으로 생겨납니다. 그리고 이는 가독성을 해칩니다.

 

(tip. Big-endian vs Little-endian)

리틀엔디안(Little-Endian)

낮은(시작)주소에 하위 바이트부터 기록합니다.  예를 들어 32비트형(4바이트)값: 0x01020304 

하위주소 0x04 0x03 0x02 0x01 상위주소

variable을 거꾸로 출력해주면됩니다. 

 

빅엔디안(Big-Endian)

낮은(시작)주소에 상위바이트부터 기록합니다. (Mac OS) 

하위주소 0x01 0x02 0x03 0x04 상위주소

네트워크 소켓프로그래밍이 빅엔디안을 사용합니다. 그래서 리틀엔디안으로 들어오면 빅엔디안으로 순서를 뒤집어줘야합니다. 

variable을 보이는 순서대로 출력해주면 됩니다.

 

<Type>

타입은 변수의 범위와 변수가 처리할 수 있는 연산의 종류를 결정합니다. signed byte 8 bits, signed char 16bits 처럼 크기 따라 op가 결정됩니다. 

 

<Value>

lvalue(left-value)는 변수의 주소를, rvalue(right-value)는 변수의 값을 말합니다. 즉 lvalue는 집이고 rvalue는 그 집에 들어가는 사람이라고 말할 수 있습니다. lvalue는 examined(조사) 되거나 altered(변형)될 수 있는 object(객체)를 참고합니다. 

무엇이 먼저 결정되어야할까요? 집이 있어야 사람이 들어오니, lvalue(집)가 있어야 rvalue(사람)이 들어옵니다.

abstract memory cell은 int a에서 a 변수 자체를 말합니다.  

 

Binding

바인딩은 지금까지 본 변수의 이러한 속성들이 결정되는 것을 말합니다. 

Binding time은 binding이 일어나는 시점입니다. 즉, 변수의 속성이 결정되는 시기

 

<Possible Binding Times>

-Language design time(프로그래밍 언어를 정의할 때) : (+, *)와 같은 연산자에 연산을 정의하는 것.

-Language implementation time(컴파일러 구현할때) : 32bit or 64bit에 부동소수점 바인딩, int타입에 값의 범위를 바인딩

-Compile time: int a=1;에서 변수a에 int의 타입을 바인딩

-Link time(프로그램을 라이브러리와 링크할때): 코드에 library subprogram을 바인딩. *c파일과 *o파일 연결.

-Load time(프로그램 실행위해 메모리에 적재할때): 메모리셀에 static variable(global)을 바인딩. 즉 프로그램이 시작되기 직전에 전역변수 할당 및 초기화가 load time에 이루어집니다.

--------여기까지가 static binding이다.

-Runtime(프로그램을 실행할때):메모리셀에 non-static local value를 바인딩. 

이는 dynamic binding으로 그때그때 쓰고 버리는 것을 의미합니다. 

 

예시

int a=b+3;

int가 정수형이라는 의미, int에 들어갈 수 있는 값의 종류는 language design time에 바인딩.

int가 4byte라는 것은 language implementation time에 바인딩 (int타입에 가능한 값의 범위)

a가 int형이라는 것은 compile time에 바인딩 (타입결정-데이터타입 뭔지 따라 메모리를 잡을 수 있음) 

=이 배정문이라는 것은 language implementation time에 바인딩

+연산의 의미는 양쪽 피연산자를 파악한 뒤에 결정되므로 compile time에 바인딩

3 리터럴을 표현하는 방법은 language implementation time에 바인딩

a의 값은 run time에 바인딩

 

Static Binding: 실행시간 전에 이루어지고 프로그램이 실행되더라도 변하지 않는 속성들. ex) 변수의 타입

Dynamic Binding: 프로그램이 실행될때마다 바뀌는 속성들. ex)변수의 주솟값

 

Type Binding

변수의 이름에 타입을 어떻게 명시하고 언제 할당되는지에 대해 

예를 들어, int타입에 대해 타입이 바인딩되있어야 사이즈가 결정됨(컴파일러가 아 얘가 int구나 알음)

4가지로 구분할 수 있습니다.

Dynamic type binding

런타임 때 바인딩이 되어 값할당때 타입이 결정됩니다. 그래서 변수가 이전에 어떤 type을 가졌는지에 상관없습니다. 이는 높은 비용과 타입에러를 컴파일러가 확인하기 어렵습니다. 

 

(스칼라변수: 문자형이든 정수형이든 실수형이든 단일값을 갖는 변수) 

lifetime에 따른 스칼라 변수의 4가지 카테고리

Static variables

Stack-dynamic variables

Explicit heap-dynamic variables

Implicit heap-dynamic variables

 

Static variables

전역변수나 static변수가 정적방식으로 생성됩니다. (런타임전에 할당) 실행시간 내내 같은 셀을 유지해서 같은 메모리로 바인딩이 이루어집니다. 그래서 메모리가 쭉 유지됩니다.

-장점: direct addressing이라서 할당과 해제로 인한 런타임 오버헤드가 없습니다.

History-sensitive: static키워드와 함께 subprogram에서 사용하면 변수의 실행동안 종료까지 동일 기억장소 바인딩으로 유지됩니다. (프로그램 종료까지 계속 유지!!)

-단점: 유연성 부족으로 재귀적인 호출하는 subprogram을 못 만듭니다. 

Stack-Dynamic Variables

지역변수가 스택동적방식으로 바인딩됩니다. 함수가 호출되어 콜스택(runtime stack)에 쌓였을 때 메모리에 할당되고, 함수동작을 마치고 종료되어 콜스택에서 빠져나갈때 메모리에서 할당해제되는 것입니다.

스칼라 경우, address를 제외한 모든 속성은 정적바인딩됩니다. subprogram들이 메모리공간을 공유합니다. 메모리 전체 크기(즉 스택크기)는 주지만 실제 주솟값(address)는 런타임때 결정됩니다.(중요!!) 

 

장점: 재귀가 가능합니다.

단점: 할당과 해제로 오버헤드 가능성, history-sensitive하지 않은 것, indirect addressing 같은 비효율적 참조

Explicit heap-dynamic variables

포인터나 참조를 이용해 할당과 해제 작업.  대표적으로 c의 malloc, free함수들이 쓰는 방식.

실행시간에 메모리 주소와 크기가 모두 결정됩니다.

C언어:

int *ptr;
ptr=(int*)malloc(sizeof(int));
*ptr=20;
free(ptr);

C++:

int *ptr;
ptr=new int;
*ptr=20;
delete ptr;

Implicit heap-dynamic variables

묵시적 힙 동적 바인딩 방식은 explicit heap-dynamic variables과 동일한데 데이터타입까지 해서

실행시간에 데이터타입, 메모리 주소와 크기가 모두 결정됩니다.

유연하지만 런타입오버헤드가능성이 있고 에러체킹이 힘들다는 단점이 있습니다. (에러체킹은 보통 컴파일 타임에 하는데 다 동적결정이므로 힘듦)

 

네 가지 카테고리를 다시 정리해봤을 때

Static variables

Stack-dynamic variables

Explicit heap-dynamic variables

Implicit heap-dynamic variables

효율성, 편리성은 내림차순,

runtime overhead는 오름차순입니다.

 

 

Scope

변수가 쓰일 수 있는 범위

nonlocal variable: 프로그램 유닛이나 블럭 내에서 사용은 되나 그곳에서 선언되진 않은 변수

 

Static Scoping

컴파일 타임에(프로그램 실행전) 컴파일러에 의해 영역이 정해집니다. 

1.자신의 속한 영역에서 변수 찾고 2.못 찾으면 바로 상위 sub program에서 3.못 찾으면 그의 상위..

그런데 전역변수 dependency가 올라가므로 필요이상으로 변수와 subprogram에 접근을 해서 가급적 많이 사용하지는 않는게 좋습니다. 

 

Dynamic Scoping

런타임execution time(실행시간)에 호출관계에 의해 영역이 정해집니다. 

1.자신이 속한 영역에서 변수 찾음 2. 못 찾으면 자신을 호출한 상위 sub program에서 찾음

함수의 "call chain"을 따라 호출됩니다. (호출 흐름이라고 보시면 됩니다.) 

function big(){
function sub1(){
var x=7;  //1. sub1의 x를 참조한다면 dynamic scoping
}
function sub2(){
var y=x; // sub2의 x가 
var z=3;
}
var x=3;//2. big의 x를 참조한다면 static scoping
}

 

Blocks

Declaration Order

블록 밖에서 블록 안의 변수에 접근은 불가능합니다.(define)

int main(void){
{
int x;
}
x=30;
count<<x<<endl;

return 0;
}

 여기서 x=30은 int x에 접근을 할 수 없습니다.

 

Global Scope

extern 함수, 변수는 다른 파일을 참조할 수 있지만

static 함수, 변수는 참조 불가합니다.

ex) C++의 영역 연산자(scope operator)

scope operator(::)를 통해 global variable에 접근

int x;
void func(void){
double x;
x=3.14;
::x=30;
cout<<x<<endl;
}

ex)Python global scope

Reference Environments

구문에서 visible한 이름들의 컬렉션. 

Named Constants 이름상수

변하지 않는 변수로, 값에 바운딩을 한번만 할 수 있는 변수입니다.

장점: readability, modifiability

ex) const int length, readonly(dynamically bound), const(static binding), final 

const는 선언과 동시에 값할당, 즉 초기화해야합니다.

그러나 readonly상수는 선언할때 값할당해도되고 생성자에서도 할당 할 수 있습니다. 생성자에서도 그 값을 변경할 수 있습니다. 

그래서 const를 컴파일 타임 상수라고 하며 readonly를 런타임 상수라고 부르기도 합니다.