컴파일러 : 코드 전체를 한번에 해석해서, 실행 파일로 변환시킨다. ( C언어는 컴파일 언어이다. )
인터프리터 : 코드를 한줄한줄씩 해석하면서 실행한다. ( JavaScript는 인터프리터 언어이다. )
프로그래밍의 동작 원리는 모든 언어가 대부분 비슷하다. 다만, 스크립트 언어는 프로그램의 동작 원리를 이해하기 힘든 구조이다. 예를 들어, JS에서는 세미콜론을 생략할 수 있고, 자료형에 대한 명시가 없다. 따라서, 대표적인 컴파일러 언어인 C언어의 변수, 자료형, 배열, 포인터들을 공부하며 동작원리를 이해할 수 있게 되고, 이를 통해 TypeScript 학습 전에 배경지식에 대해 공부한다.
프로그래밍 언어란, 사람과 컴퓨터 간의 통신 수단을 의미한다.
사람의 언어 : 프로그래밍 언어 (C, JavaScript... )
컴퓨터의 언어 : 기계어 (10100110 ... )
즉, 사람의 언어(프로그래밍 언어)를 컴퓨터의 언어(기계어)로 변환시켜주는 것을 컴파일이라고 한다.
사람의 언어와 컴퓨터의 언어는 각각 저급 언어 , 고급 언어 라고 부르며, 종류는 다음과 같다.
저급 언어 ( 컴퓨터의 언어 )
고급 언어 ( 사람의 언어 )
프로그래밍의 과정은 다음과 같다.
설계 - 머리속으로 구상한 것을 기록하고, 문서화한다.원시코드 작성 - 구상한 내용들을 코드로 작성한다.컴파일 - 소스코드를 이진코드로 변환한다.링크 - 실행 파일을 생성한다.여기서, 컴파일과 링크를 통합하여 빌드라고도 부른다.
C언어는 엄격한 문법을 사용한다.
예를 들어, C언어의 문장의 끝에는 반드시 세미콜론이 필요하다. 이는 세미콜론이 생략가능한 JS와 비교된다. 또한, 변수를 지정할 때에는 반드시 자료형을 명시해줘야 한다.
그럼 이제 변수에 대해 알아보자.
변수란, 변경이 가능한 수 를 의미한다.
변수에는 어떤 값이 들어갈 지 예측할 수 없다.
메모리의 기본단위는 byte인데, 32bit 운영체제의 경우 정수형 변수는 4바이트를 차지한다.
메모리의 주소값은 주민등록번호와 같이 유일한 값이다.
한 사람이 몇십만명의 주민등록번호를 외울 수 없듯이, 수십만개의 메모리 주소값을 인간이 외워서 사용할 순 없다.
따라서, 주소 값을 기억하는게 아니라, 메모리 공간에 이름을 부여하고 이 이름을 기억하여 사용한다. 즉, 메모리 공간에 붙인 이름을 변수라고 한다.
예를 들어 다음의 코드를 살펴보자.
int a = 10 ;
a 값을 100으로 바꾸고 싶을 때,
a = 100 ; 을 하게 되면 a의 메모리주소를 찾아가서, 해당 메모리의 공간의 값이 바뀌게 되는 것이다.
메모리의 영역에는 크게 4가지가 존재한다.
1. 코드 영역 : 실행 명령어들 저장
2. 스택 영역(LIFO) : 지역변수 및 매개변수 저장
3. 힙 영역(Queue) (FIFO) : 프로그래머가 직접 할당, 동적 메모리 ( new 연산자 )
4. 데이터 영역 : 전역변수, static 변수 저장
예를들어, OS의 시스템 큐는 애플리케이션의 큐에게 값을 전달하며 사용하는데,
우리가 클릭하거나 입력한 순서대로 전달한다.
스택을 알아보기 위해 다음의 코드를 살펴보자.
void Test(int a) {
char b = "A",
int c = 1 ;
dobule d = 3.14 ;
}
스택에는 a(4Byte), b(1Byte), c(4Byte), ,d(8Byte) 순으로 차곡차곡 쌓인다.
그리고 이 코드 블럭이 끝나면 스택은 공간을 비울 것인데,
d - c - b - a 순서로 소멸한다.
힙(Queue)을 알아보기 위해 다음의 코드를 살펴보자.
int a = 10 ;
b = [1,2,3] ;
c = { name : "" , age : 20}
a는 스택메모리에 주소 와 값이 저장된다. 이를 원시 타입 데이터라고 부른다.
b는 스택메모리에 주소 와 힙메모리의 주소가 적힌 값이 저장된다. 즉, 힙메모리의 주소를 이용해서 힙 메모리를 찾아가서 값을 찾는다. 이를 참조 타입 데이터 라고 부른다.
c도 마찬가지로, 스택메모리에 주소 와 힙메모리의 주소가 적힌 값을 저장한다.
그럼 이제 자료형에 대해 알아보자.
우리가 요리할 때, 음식의 종류에 따라 대형냄비, 소형냄비, 후라이팬 등 을 사용하는 것처럼,
변수를 선언할 때에도 어떠한 용도로 사용할 것인지 컴퓨터에게 알려줘야한다.
이는 그에 맞는 적절한 메모리 공간을 확보하기 위함이다.
소형냄비에 20인분의 요리를 하면 요리가 넘치는 것처럼 말이다.
대표적으로,
정수형은 보통 int 데이터타입(4Byte),
실수형은 보통 double 데이터타입(8Byte)을 사용한다.
물론 더 큰 공간의 자료형인 double , long long 등의 자료형도 있으므로 효율적인 메모리 사용을 위해 필요에 맞게 선택하여 사용한다.
다음의 코드를 살펴보자.
int main()
{
int width = 10 ; // 4Byte
int height = 20 ; // 4Byte
printf("사각형의 넓이 : %d" , width * height ) ;
return 0;
}
JavaScript와 다르게, int 라는 데이터 타입을 명시했다.
따라서, 컴파일러가 변수가 어떤 타입인지를 인식하고 그에 맞는 메모리 공간을 할당해준다.
당연히 int 자료형에 실수값이나 문자열을 넣게되면, 오류가 발생하거나, 이상한 값이 나올 것이다.
C언어에서는 문자열에 변수를 사용할 때, %d, %f , %c 등의 % 기호를 이용한다.
이 때, d는 decimal의 약자로 10진수를 의미하고,
f는 float(실수) , c는 character(문자) 를 의미한다.
추가적으로, 문자 자료형은 char 자료형을 사용하며 특정 숫자와 문자를 대응 시킨 아스키코드를 사용한다.
아래 코드를 살펴보자.
int main()
{
char ch1 = 'A' ; //1Byte
printf("%d , %c", ch1 , ch1 ) ;
return 0;
}
A는 아스키코드로 65의 값을 가지므로, 10진수(d)로 표현하면 65의 값을 가진다.
문자(c)로 표현하면 문자 "A"의 값을 가진다.
즉, 결과로는 65, "A"가 출력된다.
그럼 이제 상수에 관하여 알아보자.
상수란, 변하지 않는 수를 의미한다. (<-> 변수 )
const 키워드를 사용하면 상수화시킬 수 있다. 또한, 선언과 동시에 초기화가 필요하다.
다음의 코드를 보자.
const int TEN = 10; // 더이상 수정 불가
const int TEN ; // 쓰레기 값이 들어가있음.
사용자로부터 데이터를 입력받을 때, C언어에서는 scanf를 사용한다.
int main()
{
int input = 0 ;
scanf("%d", &input) ;
printf("input : %d " , input );
return 0;
}
scanf("문자열" , &변수) ;
변수명 앞에 & 이 붙어있는데, 주소 연산자라고 부르며, 해당 주소의 메모리주소값을 나타낸다.
우리가 입력(하드웨어)한 값은 운영체제의 시스템 Queue에 전달되고, 애플리케이션의 input이라는 변수의 메모리로 전달이 되어야 한다.
수많은 시스템 Queue의 값들 중에서, 애플리케이션의 input을 찾아가려면 input의 메모리 주소값이 필요하다. 따라서, 주소를 명시해주는 &를 사용하는 것이다.
다른 언어들은 주소 연산자를 사용하지 않지만, 원리는 마찬가지로 다르지 않고, 메모리 주소를 이용해서 값을 찾아간다.