C, 키워드
포인터
주소값 | 모든 데이터는 해당 데이터를 담고 있는 주소값을 가지고 있다. 여기서 주소값이란 데이터가 저장되어 있는 메모리의 위치. 만약 int형 데이터를 선언한다면, 해당 시작 주소값으로부터 4byte만큼의 메모리 영역을 사용하게 된다. |
포인터의 개념 | 여기서 해당 int 자료형의 첫번째 주소값을 가르키도록 선언할 수 있는데, 이것을 포인터라고 한다. int n = 100; // 변수의 선언 int *ptr = &n; // 포인터의 선언 |
포인터의 연산자 | 해당 데이터가 저장되어있는 첫번째 주소값은 주소연산자(&) 로 표현하고 이를 가르키는 포인터는 참조연산자(*)로 선언하게 된다. 여기서 주소 연산자 앰퍼샌드(ampersand)라고 부르며, 사용법은 위에 보이는 예시와 같이 변수 이름 앞에 사용하며, 해당 변수의 주소값을 반환한다. 참조 연산자는 포인터의 이름이나 주소 앞에 사용하여, 포인터에 가리키는 주소에 저장된 값을 반환한다. |
포인터의 선언 | 포인터를 선언한 후 참조 연산자를 사용하기 전에 포인터는 반드시 먼저 초기화 되어야 한다. 타입* 포인터이름 = &변수이름; 타입* 포인터이름 = 주소값; 타입* 포인터이름 = NULL; (후에 값 할당) |
포인터의 연산 | 포인터는 가르키고 있는 자료형에 따라 증감치가 다르다. 위 예제를 살펴보면 각각 최초로 저장된 주소에서 해당 자료형의 크기 만큼의 증감치를 가진다는 사실을 알 수 있다. 이러한 특징을 이용해서 다음과 같이 배열에 접근도 가능함 |
가상화
가상화란? | 하나의 물리적 머신에서 가상머신 (Virtual Machine) 만드는 프로세스이다. 대표적인 예로는 지금 실습을 하며 사용하고 있는 docker같은 가상 컨테이너 기술등이 속한다. |
가상화의 장점 | 1개의 하드웨어 상에서 여러 개의 가상머신을 구동할 수 있다. 하드웨어와 문관하게 원하는 운영체제나 그에 맞는 애플리케이션을 실행할 수 있다. |
GCC
GCC란? | GCC는 GNU 컴파일러의 모음이다. C++, JAVA등 여러 언어를 컴파일 할 수 있음 단순히 컴파일 과정 뿐만 아니라 전처리 과정, 어셈블 과정, 링킹 과정과 같이 실행파일을 만드는데 필요한 작업을 모두 수행한다. |
동작과정 | 1. 전처리 과정 define으로 설정된 메크로와 헤더 파일을 전처리 해서 .i 파일로 생성한다. 2. 컴파일 과정 C 컴파일러를 사요해서 어셈블리어로 이루어진 .s 파일을 생성한다. 3. 어셈블 과정 어셈블러를 통해서 바이너리 코드로 이루어진 .o파일을 생성한다. 4. 링킹과정 헤더에 선언된 바이너리 파일을 필요한 위치에 위치시킨다. (역어셈블링으로 확인함) |
메모리 동적할당
정적할당 | 프로그램이 실행되기 전에 컴파일 시점에 소스 코드를 읽고 메모리 공간을 확보하는 것은 정적할당. |
동적할당 | 컴파일 시점이 아닌 프로그램이 실행되는 중인 런타임에 필요한 만큼의 메모리 공간을 확보하는것. 예를들어 입력 파라메터에 따라 길이가 변경되는 Array를 생각해보면 될것같다. |
동적할당이 필요한 이유 | 그때그때 필요한 만큼만 메모리 공간을 확보하고, 다 사용했다면 free 시켜서 메모리 공간을 해제해줘, 메모리를 효율적으로 사용할 수 있다. |
힙 영역 | 지역 변수같은 경우 stack 영역에 선언되어 프로시저가 종료되거나 변수의 영역을 벗어나면 자동으로 메모리 해제가 이루어지지만, 동적으로 할당된 메모리는 힙 영역에 선언된다. 스택이 아닌 힙영역에 할당되기 때문에 메모리를 해제해주는 free가 중요한것. |
동적할당 장단점 | 장점 상황에 따라 원하는 크기 만큼의 메모리가 할당되므로 경제적 이미 할당된 메모리라도 언제든 크기를 조정할 수 있다. (realloc) 단점 C언어같은 경우 Garbage Collector가 없기 때문에, 개발자가 명시적으로 메모리를 해제해주어야 한다. 안해주면 메모리 누수 발생. |
malloc, calloc | malloc은 최초 메모리 공간이 할당될때 기존에 들어있던 쓰레기값을 초기화 하지 않지만, calloc은 초기화한다. 성능 자체는 malloc이 더 낫다. 하지만 초기화가 필요한 상황에서는 calloc도 써야함 |
메모리 재할당 | 이미 할당되어 있는 메모리 공간의 크기를 변경해 재할당. 사용방법은 다음과 같다. |
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
// 5개의 int 형 변수에 대한 메모리 할당
ptr = (int *)malloc(5 * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 메모리 블록의 크기를 10개의 int 형 변수로 변경
ptr = (int *)realloc(ptr, 10 * sizeof(int));
if (ptr == NULL) {
printf("Memory reallocation failed\n");
return 1;
}
// 메모리 해제
free(ptr);
return 0;
}
realloc 사용법 | 지금같은 경우 ptr을 직접 선언해줬지만, 만약 ptr이 NULL이면 realloc은 malloc(size)와 동일한 동작을 수행. ptr이 NULL이 아닐 경우 realloc은 기존에 할당된 ptr포인터가 가리키는 메모리를 size만큼으로 조절한다. 새로운 메모리 블록의 크기가 기존 메모리블록의 크기보다 작은 경우, 데이터 일부가 손실될 수 있다. |
B-Tree
B-트리의 특징 | B-트리는 자기 균형 트리로, 모든 리프노드가 동일한 레벨에 위치하도록 설계되었다. B-트리는 특이하게 노드가 추가되면 노드를 타고 아래로 내려가는 것이 아닌, 특정 조건을 충족하면 노드가 위로 확장되는 구조를 가지고 있음. B-트리의 노드는 여러개의 키와 포인터 쌍을 저장할 수 있음. B-트리는 균형이 잘 잡혀있어, 키를 검색하는데 필요한 평균적인 시간 복잡도가 O(log n)이다. 가장 큰 특징은 한번의 디스크 액세스로 여러개의 레코드를 한번에 읽어들일 수 있어, 디스크 I/O의 수를 최소화하면서 데이터를 효율적으로 접근할 수있음 |
B-트리의 삽입 | B-트리에 새로인 키를 삽입할 때는 다음과 같은 과정을 따른다. 적절한 위치를 찾아 키를 삽입한다. (모든 노드의 삽입은 leaf 노드에서 이뤄진다.) 만약 노드의 키 수가 차수보다 크다면 노드를 분할한다. 분할되면 중간 키를 상위 노드로 올리고, 두개의 새로운 노드를 생성한다. (차수가 짝수일땐 중간 기준 왼쪽 노드를 상위노드로 올린다.) |
B-트리의 삭제 | 먼저 삭제할 노드를 탐색한다. 탐색할 노드를 자신의 가장 근삿값 (이진 탐색트리의 근삿값을 구하는 방법과 동일함)와 swap하고 leaf노드에서 삭제한다. 그 후 B-트리의 조건에 맞게 병합과 재분배 과정을 수행해 트리를 재구성 |
완벽하게 이해하기 위해 3 ~ 5차 B-Tree 삽입, 삭제 차력쇼 진행함
'Krafton_Jungle > Study' 카테고리의 다른 글
Krafton_jungle 5기 5주차 WIL - RB_Tree (0) | 2024.04.22 |
---|---|
Krafton_jungle 5기 31 ~ 32일차 TIL - RB-Tree, 알고리즘 (2) | 2024.04.19 |
Krafton_jungle 5기 28 ~ 29일차 TIL - 자료구조 (2) | 2024.04.15 |
Krafton_jungle 5기 27일차 TIL - 자료구조 (2) | 2024.04.13 |
Krafton_jungle 5기 25 ~ 26일차 TIL - 알고리즘, 자료구조 (0) | 2024.04.12 |