SourceCode
int main(void) { int *x; int *y; x = malloc(sizeof(int)); *x = 42; *y = 13; }
→ 초기화 되지 않은 *y는 오류를 발생시킬 수도 있다.
y = x; *y = 13;
→ 추가하게 되면 y는 x와 같은 곳을 가리키게 되므로 13이 저장된다.
포인터를 초기화시키지 않고 값을 저장하면 어떤 오류를 발생할 수 있을까요?
→ 기존에 있는 데이터를 의도치 않게 변경하는 오류가 발생할 수 있다.
SourceCode
새로운 변수를 선언, 메모리할당한 후 값 복사#include <stdio.h> #include <stdlib.h> int main(void) { //int 자료형 3개로 이루어진 list 라는 포인터를 선언하고 메모리 할당 int *list = malloc(3 * sizeof(int)); // 포인터가 잘 선언되었는지 확인 if (list == NULL) { return 1; } // list 배열의 각 인덱스에 값 저장 list[0] = 1; list[1] = 2; list[2] = 3; //int 자료형 4개 크기의 tmp 라는 포인터를 선언하고 메모리 할당 int *tmp = malloc(4 * sizeof(int)); if (tmp == NULL) { return 1; } // list의 값을 tmp로 복사 for (int i = 0; i < 3; i++) { tmp[i] = list[i]; } // tmp배열의 네 번째 값도 저장 tmp[3] = 4; // list의 메모리를 초기화 free(list); // list가 tmp와 같은 곳을 가리키도록 지정 list = tmp; // 새로운 배열 list의 값 확인 for (int i = 0; i < 4; i++) { printf("%i\n", list[i]); } // list의 메모리 초기화 free(list); }
realloc 함수로 값 복사
#include <stdio.h> #include <stdlib.h> int main(void) { int *list = malloc(3 * sizeof(int)); if (list == NULL) { return 1; } list[0] = 1; list[1] = 2; list[2] = 3; // tmp 포인터에 메모리를 할당하고 list의 값 복사 int *tmp = realloc(list, 4 * sizeof(int)); if (tmp == NULL) { return 1; } // list가 tmp와 같은 곳을 가리키도록 지정 list = tmp; // 새로운 list의 네 번째 값 저장 list[3] = 4; // list의 값 확인 for (int i = 0; i < 4; i++) { printf("%i\n", list[i]); } //list 의 메모리 초기화 free(list); }
이미 할당된 메모리 크기를 조절할 때 임시 메모리를 새로 할당해줘야 하는 이유는 무엇일까요?
→ 데이터는 연속적으로 저장되기 때문에 할당된 메모리 다음 공간에 저장된 데이터에 영향을 주지 않기 위해서
SourceCode
연결리스트를 간단한 구조체로 정의typedef struct node { int number; struct node *next; } node;
→ number는 각 node가 가지는 값
→ *next는 다음 node를 가리키는 포인터
→ typedef struct가 아닌 typedef struct node 라고 명시하는 것은 구조체 안에서 node를 사용하기 위함 !
연결 리스트를 배열과 비교하였을 때 장단점은 무엇이 있을까요?
→ 배열에 비해 삽입과 삭제가 용이하다는 장점과 다음 배열의 주소도 함께 저장해야해서 추가적으로 메모리가 더 필요하다는 단점이 있다.
SourceCode
구조체로 연결 리스트 구현#include <stdio.h> #include <stdlib.h> //연결 리스트의 기본 단위가 되는 node 구조체를 정의합니다. typedef struct node { //node 안에서 정수형 값이 저장되는 변수를 name으로 지정합니다. int number; //다음 node의 주소를 가리키는 포인터를 *next로 지정합니다. struct node *next; } node; int main(void) { // list라는 이름의 node 포인터를 정의합니다. 연결 리스트의 가장 첫 번째 node를 가리킬 것입니다. // 이 포인터는 현재 아무 것도 가리키고 있지 않기 때문에 NULL 로 초기화합니다. node *list = NULL; // 새로운 node를 위해 메모리를 할당하고 포인터 *n으로 가리킵니다. node *n = malloc(sizeof(node)); if (n == NULL) { return 1; } // n의 number 필드에 1의 값을 저장합니다. “n->number”는 “(*n).numer”와 동일한 의미입니다. // 즉, n이 가리키는 node의 number 필드를 의미하는 것입니다. // 간단하게 화살표 표시 ‘->’로 쓸 수 있습니다. n의 number의 값을 1로 저장합니다. n->number = 1; // n 다음에 정의된 node가 없으므로 NULL로 초기화합니다. n->next = NULL; // 이제 첫번째 node를 정의했기 떄문에 list 포인터를 n 포인터로 바꿔 줍니다. list = n; // 이제 list에 다른 node를 더 연결하기 위해 n에 새로운 메모리를 다시 할당합니다. n = malloc(sizeof(node)); if (n == NULL) { return 1; } // n의 number와 next의 값을 각각 저장합니다. n->number = 2; n->next = NULL; // list가 가리키는 것은 첫 번째 node입니다. //이 node의 다음 node를 n 포인터로 지정합니다. list->next = n; // 다시 한 번 n 포인터에 새로운 메모리를 할당하고 number과 next의 값을 저장합니다. n = malloc(sizeof(node)); if (n == NULL) { return 1; } n->number = 3; n->next = NULL; // 현재 list는 첫번째 node를 가리키고, 이는 두번째 node와 연결되어 있습니다. // 따라서 세 번째 node를 더 연결하기 위해 첫 번째 node (list)의 // 다음 node(list->next)의 다음 node(list->next->next)를 n 포인터로 지정합니다. list->next->next = n; // 이제 list에 연결된 node를 처음부터 방문하면서 각 number 값을 출력합니다. // 마지막 node의 next에는 NULL이 저장되어 있을 것이기 때문에 이 것이 for 루프의 종료 조건이 됩니다. for (node *tmp = list; tmp != NULL; tmp = tmp->next) { printf("%i\n", tmp->number); } // 메모리를 해제해주기 위해 list에 연결된 node들을 처음부터 방문하면서 free 해줍니다. while (list != NULL) { node *tmp = list->next; free(list); list = tmp; } }
연결 리스트의 중간에 node를 추가하거나 삭제하는 코드는 어떻게 작성할 수 있을까요?
→ 다음 값을 가리키는 주소를 변경해주면 된다.
ex) 이전 노드 before, 추가하거나 변경할 노드 now, 다음 노드 next라고 정의하자.
배열이 정렬되어 있지 않은 경우의 검색 소요 시간을 연결 리스트의 검색 시간과 비교해보세요.
→ 둘 다 하나씩 탐색해봐야하므로 O(n)이라고 생각한다.
SourceCode
이진 검색 함수
(이진 검색 트리의 노드 구조체와 50을 검색)//이진 검색 트리의 노드 구조체 typedef struct node { // 노드의 값 int number; // 왼쪽 자식 노드 struct node *left; // 오른쪽 자식 노드 struct node *right; } node; // 이진 검색 함수 (*tree는 이진 검색 트리를 가리키는 포인터) bool search(node *tree) { // 트리가 비어있는 경우 ‘false’를 반환하고 함수 종료 if (tree == NULL) { return false; } // 현재 노드의 값이 50보다 크면 왼쪽 노드 검색 else if (50 < tree->number) { return search(tree->left); } // 현재 노드의 값이 50보다 작으면 오른쪽 노드 검색 else if (50 > tree->number) { return search(tree->right); } // 위 모든 조건이 만족하지 않으면 노드의 값이 50이므로 ‘true’ 반환 else { return true; } }
값을 검색할 때 이진 검색 트리가 기본 연결 리스트에 비해 가지는 장점과 단점은 무엇이 있을까요?
→ 검색 범위를 반으로 줄여나갈 수 있는 장점이 있고 삽입과 삭제가 복잡하다는 단점이 있다.
해시 함수는 어떻게 만들 수 있을까요?
→ 분류 기준을 만들고 함수를 만들면 될 것 같다.
: 기본적으로 트리 형태의 자료 구조이다.
문자열의 길이
에 한정된다.트라이가 해시 테이블에 비해 가지는 장점과 단점은 무엇일까요?
→ 검색이 용이하다는 장점이 있지만 메모리가 많이 필요하다는 단점이 있다.
선입 선출
or FIFO
(First In First Out) 방식을 따름후입 선출
or ‘LIFO
(Last In First Out) 방식을 따름키
와 값
이라는 요소로 구성됨키
를 어떻게 정의할 것인지가 중요함