위 지시문들은 보통 조건부 컴파일 지시자라고 한다. #이 앞에 오는 전처리 지시자이다. 먼저 #if, #elif, #else, #endif부터 알아보면, 일반적으로 우리가 사용하는 if, else if, else와 방식이 유사하다는 것을 알 수 있다. 규칙은 #if를 사용하면 꼭 끝나는 부분에 #endif를 삽입해야 한다는 것을 숙지하자. 왜냐하면, 전처리기는 scope를 사용하지 않아서 꼭 끝을 내줘야 하기 때문이다. 이는 보통 조건부로 코드를 추가 및 제거해서 컴파일할 때 많이 사용한다. #include #define VERSION 3 int main(void) { #if (VERSION == 1) printf("VERSION 1. \n"); #elif (VERSION ==2) printf("VERS..
#define은 전처리기 지시자로 컴파일하기 전에 실행되는 컴파일러의 한 부분이다. 또한 기호 상수를 만들기 위한 지시자로 보통 매크로라고 한다. 매크로를 정의하는 방법은 다음과 같이 세미콜론을 사용하면 안된다. 이때 보통 매크로 이름은 대문자를 많이 사용한다. 즉, 정의한 매크로를 사용하면 컴파일하기 전에 해당 매크로에 정의한 값으로 변경이 되는 방식이다. #include #define ARRAY_SIZE 10 int main(void) { int num[ARRAY_SIZE]; for (int i = 0; i < ARRAY_SIZE; i++) { num[i] = i; } return 0; } 위의 코드처럼 배열의 크기를 변경할 때도 해당 매크로의 정의에서 값만 변경해주면 모두 적용되기 때문에 훨씬 사용..
가상 메모리 시스템에서는 각 프로그램이 가상의 주소를 사용하도록 하며, CPU가 메모리 참조를 시도할 때는 MMU(Memory Management Unit)이라는 하드웨어 장치를 이용하여 해당 가상 주소(Virtual Address)를 실제 메인 메모리의 물리 주소로 변환하여 메모리 참조를 진행한다. 그렇다면 가상 메모리 기술은 왜 사용하는 것일까? 그 이유는 크게 3가지로 나눠서 생각해볼 수 있다. (Caching, Memory Management, Memory Protection). 메인 메모리를 효율적으로 사용하기 위해서. 가상 메모리 시스템에서는 각 프로그램이 사용하는 가상 주소 공간을 우선 디스크에 저장해두고, 그 중에서 자주 사용되는 부분만 메인 메모리로 가져와서 사용한다. 즉, 메인 메모리를..
이진 탐색 트리의 한 종류로 스스로 균형을 잡는 트리이다. 이때 balance factor를 통해 균형을 유지하는게 핵심이다. 그럼 balance factor에 대해서 조금 더 자세히 알아보자. 노드의 balance factor란 임의의 노드 x에 대해서 그 노드의 왼쪽 서브트리의 높이와 그 노드의 오른쪽 서브트리의 높이의 차이를 구했을때, 그 높이 차이를 그 노드의 balance factor라고 한다. 여기서 AVL 트리의 특징은 트리의 모든 노드들의 balance factor가 -1, 0 ,1 값을 가진다는 특징이 있다. 또한 AVL 트리는 트리에 삽입 혹은 삭제후에 balance factor가 -1, 0, 1이 아닌 노드가 생기면 균형을 맞추는 작업을 수행한다. 즉, AVL 트리에서 삽입/삭제를 위..
프로세서에서 전력이 공급된 시점부터 전력 공급이 끊기는 시점까지 PC는 다음과 같은 값들을 갖는다. a는 명령어 I의 시작 주소를 의미한다. 이와 같이 실행하는 명령어에 따라 PC의 값이 변해가는 흐름을 제어 흐름(Control Transfer)라고 부른다. 제어 흐름의 양상은 크게 3종류이다. 메모리에 연속적으로 할당되어 있는 명령어들을 순차적으로 실행하는 경우. 이 경우는 가장 기본적이면서 대부분의 시간을 차지하는 제어 흐름에 해당한다. 프로그램 변수로 표현되는 프로그램 상태의 변화에 반응하여 제어 흐름이 갑자기 바뀌는 경우. 대표적으로 jump, call, return 등의 명령어를 수행하는 경우가 이에 해당한다. Exceptional Control Flow(ECF). 이는 시스템 상태의 변화에 반..
링킹은 여러 개의 코드와 데이터를 모아서 연결하여 메모리에 로드될 수 있고, 실행될 수 있는 한 개의 파일로 만드는 작업이다. 링킹은 컴파일 시에 수행할 수 있으며, 이때 소스코드는 머신코드로 번역된다. 프로그램이 메모리에 로드되고, 로더에 의해서 실행될 때에는 로드 타임에, 응용프로그램에 의해서 심지어 실행시에도 수행될 수 있다. 현대 시스템에서 링킹은 링커라고 부르는 프로그램에 의해서 자동으로 수행된다. 링커는 독립적인 컴파일을 가능하게 만든다. 즉, 큰 규모의 응용 프로그램을 한개의 소스 파일로 구성하는 대신 별도로 수정 및 컴파일할 수 있는 보다 유지보수에 용이한 더 작은 모듈로 나눌 수 있다. 이러한 링커를 배워야 하는 이유는 대략적으로 다음과 같다. 링커가 참조를 해결해 나가는 방법, 라이브러..
프로그래밍에서 선언과 정의에 대해서 알아보자. 선언과 정의의 가장 큰 차이는 "메모리를 할당하는가"이다. 즉, 메모리를 할당하지 않고, 대상의 이름만 알려준다면 선언이고, 대상의 메모리가 할당된다면 그것은 정의이다. 다만 혼동하기 쉬운 예로 typedef는 사용자 정의 타입이라고 불리지만, 실제로 메모리 영역상에 올리지는 않기 때문에 정의라고 볼 수는 없다. void main() { int a; int b = 10; } 위의 예에서 int a; 는 선언과 동시에 정의한 것이다. 즉, int 4byte 만의 영역이 메모리 할당되었다는 말이다. 너무 헷갈리는데 선언과 정의가 무엇이며, 각자의 특징에 대해 좀 더 자세히 알아보려고 한다. 선언 컴파일러가 참조할 식별자와 이름을 알린다는 의미이다. 여기서 식별자..
구조체는 서로 다른 타입의 변수들을 한 그룹으로 묶는 방법을 제공한다. 이렇게 함으로써 관련된 데이터를 하나의 단위로 취급할 수 있게 된다. 구조체 메모리 할당 방식 우선 구조체를 시작하면서 구조체의 메모리 할당 방식, 구조체의 크기 계산 방법, 그리고 구조체와 포인터의 상호 작용에 대해서 알아보자. 이 개념을 배우고나면 구조체가 메모리 상에서 어떻게 위치하는지 이해할 수 있고, 메모리를 효츌적으로 활용하는 방법을 배울 수 있다. 또한 이를 통해 프로그램의 성능 향상과 메모리 관리를 위한 기본적인 이해를 갖출 수 있다. 구조체는 메모리 상에서 연속적인 공간에 할당된다. 즉, 구조체를 선언하면 구조체의 모든 멤버가 메모리상에서 순차적으로 위치하게 된다. 이러한 방식은 구조체의 각 멤버에 접근하거나 값을 변..
우리가 c언어로 scanf를 사용하려고 할 때, 비주얼 스튜디오 코드를 사용하면 에러코드 C4996과 함께 오류가 발생하며 빌드가 되지 않는 것을 볼 수 있다. 이때 발생하는 오류가 뭐냐하면 scanf가 안전하지 않으므로 scanf_s를 쓰거나, _CRT_SECURE_NO_WARNINGS를 이용하라는 것이다. 그럼 scanf가 안전하지 않은 이유는 무엇일까? 이는 scanf가 받을 수 있는 입력 데이터의 크기가 있는데 사용자가 고의로 또는 실수로 해당 데이터 크기를 초과하는 값을 입력할 경우, 초과된 데이터가 다른 메모리를 침범해서 프로그램에 오류가 발생할 수 있기 때문에 이를 막기위해 scanf_s로 일정 크기까지만 입력받는 함수를 사용하라고 권장하는 것이다. 그럼 해결 방법을 알아보자. 해결 방법에는..
typedef type define의 줄임말로 새로운 type을 define 정의한다. 사용자 정의 타입이라고 불리며, 즉, 기본 자료형을 확장시키는 역할을 하며, 별명을 붙여주는 것으로 이해해도 좋다. 이런 typedef를 사용하면 복잡한 데이터 형식도 새로운 자료형으로 만들 수 있다. 사용법은 아래와 같다. typedef 진짜자료형 별명 예) typedef int INT typedef는 특히 struct 구조체에서 유용하게 사용되는데, 다음의 예시를 이해하면 도움이 될 것이다. #define _CRT_SECURE_NO_WARNINGS #include #include #include struct point { char a; int b; double c; } int main(void) { struct p..