boostcource
모두를 위한 컴퓨터 과학 (CS50 2019) : David J. Malan
www.boostcourse.org/cs11
실습환경 : CS50 Sandbox & CS50 IDE
C로 구현할 수 있는 다양한 데이터 구조를 배우기 이전에
데이터 구조를 정의하고 관리하는데에 있어서 반드시 필요한 개념인 메모리와 포인터를 복습해보자.
malloc과 포인터 복습
이전에 작성한 포스팅
'[CS][CS50] 메모리 - 메모리 주소 / 포인터',
'[CS][CS50] 메모리 - 문자열 / 문자열 비교 / 문자열 복사 / 메모리 할당과 해제 / 메모리 교환, 스택, 힙' 참고
아래 코드의 문제점을 찾아보자.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *x;
int *y;
x = malloc(sizeof(int));
*x = 42;
*y = 13;
}
포인터 x와 y를 선언한 뒤
x에는 malloc 함수를 이용해서 int 자료형의 크기 만큼(4 byte)의 메모리를 할당한다.
그 다음 포인터 x가 가리키는 지점에 42를 저장, 포인터 y가 가리키는 지점에는 13을 저장한다.
문제점은 *y = 13
포인터 y는 선언만 되었지, 어디를 가리킬지에 대한 정의가 없기 때문에(초기화 되지 않았기 때문에)
*y는 프로그램 어딘가를 임의로 가리키는 것이 아닌 유효한 메모리 주소를 가리키거나 NULL 포인터 일 수 있다.
때문에 y가 가리키는 곳에 13을 저장할 수 없다.
index.c:11:6: error: variable 'y' is uninitialized when used here [-Werror,-Wuninitialized]
*y = 13;
^
index.c:6:11: note: initialize the variable 'y' to silence this warning
int *y;
^
= NULL
1 error generated.
<builtin>: recipe for target 'index' failed
+) "포인터를 초기화 시키지 않았다" 의 의미는 두 가지
- 포인터 변수를 선언하고 메모리를 할당하지 않았다. <- 위 경우는 여기에 해당된다.
- 이미 사용한 포인터 변수를 초기화하지 않았다.
이 문제점은 y = x; 를 추가해
y는 x가 가리키는 곳과 동일한 곳을 가리킨다고 초기화를 해주는 것이 하나의 해결 방법이 된다.
그렇게 되면 *y = 13 으로 인해 x가 가리키는 곳도 동일하게 13이 저장된다.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *x;
int *y;
x = malloc(sizeof(int)); // 포인터 x 초기화
y = x; // 포인터 y가 포인터 x와 동일한 곳을 가리키도록 초기화
printf("%p\n", *&x); // ? 0x1792290
printf("%p\n", *&y); // ? 0x1792290 // 동일한 곳을 가리키고 있음을 확인
*x = 42;
printf("%i\n", *x); // ? 42
printf("%i\n", *y); // ? 42
*y = 13;
printf("%i\n", *x); // ? 13
printf("%i\n", *y); // ? 13
}
배열의 크기 조정하기
메모리는 사물함과 같기 때문에 한 번 사용할 갯수를 정하고나서는
그 이상 마음대로 다른 남의 사물함까지 사용할 수 없다.
하지만 포인터와 malloc의 개념을 응용해서 이미 정의된 배열의 크기를 바꿀 수 있다.
새로운 공간에 큰 크기의 메모리를 다시 할당하고
기존 배열의 값을 하나씩 옮겨주는 방법으로 배열의 크기 n만큼 O(n)의 실행 시간이 소요된다.
예시로 이어서 확인해보자.
배열 list는 크기가 12byte(int 3개)로 선언되어 있지만, 16byte(int 4개)의 크기로 변경하고 싶다.
이를 위해 새로운 배열 tmp 를 선언해주고 이 곳에 list의 값을 하나씩 옮겨주는 예제이다.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3*sizeof(int)); // 포인터 list를 선언하고 int 3개로 이루어진 메모리 할당
if(list == NULL){ // 포인터 선언 재확인
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
// list -> tmp 이사
int *tmp = malloc(4*sizeof(int)); // 포인터 tmp를 선언하고 int 4개로 이루어진 메모리 할당
if(tmp == NULL){
return 1;
}
for(int i=0; i<3; i++){ // list의 값을 tmp로 복사
tmp[i] = list[i];
}
tmp[3] = 4; // tmp[3] 할당
free(list); // list의 메모리 초기화
list = tmp; // list가 tmp와 같은 곳을 가리키도록 초기화
for(int i=0; i<4; i++){ // 새로운 배열 list 확인
printf("%i\n", list[i]);
}
free(list); // list의 메모리 초기화
}
// ? 1
// ? 2
// ? 3
// ? 4
tmp[i] = list[i] 를 통해서 list의 값을 하나씩 tmp로 복사하고
list = tmp 를 통해서 포인터 list가 포인터 tmp와 같은 곳을 가리키도록 초기화해주는 것이 핵심이다.
동일한 작업을 함수 realloc 를 사용해서도 수행할 수 있다.
// malloc : 메모리 할당
int *tmp = malloc(4*sizeof(int));
for(int i=0; i<3; i++){
tmp[i] = list[i];
}
// realloc : 메모리 할당 및 임의의 배열(list)의 값 복사
int *tmp = realloc(list, 4*sizeof(int));
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3*sizeof(int)); // 포인터 list를 선언하고 int 3개로 이루어진 메모리 할당
if(list == NULL){ // 포인터 선언 재확인
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
// list -> tmp 이사
int *tmp = realloc(list, 4*sizeof(int)); // 포인터 tmp에 메모리를 할당하고 list의 값 복사
if(tmp == NULL){
return 1;
}
list = tmp; // list가 tmp와 같은 곳을 가리키도록 초기화
tmp[3] = 4; // tmp[3] 할당
for(int i=0; i<4; i++){ // 새로운 배열 list 확인
printf("%i\n", list[i]);
}
free(list); // list의 메모리 초기화
}
// ? 1
// ? 2
// ? 3
// ? 4
복습 퀴즈
Q1. int 자료형 2개로 이뤄진 배열 list를 int 자료형 5개를 담을 수 있는 배열로 확장하고 싶다. 이를 위해 임시 포인터 *tmp를 선언하는 올바른 realloc() 코드를 작성하자면?
답: int *tmp = realloc(list, 5 * sizeof(int));
'CS > CS50' 카테고리의 다른 글
[CS][CS50] 자료구조 - 트라이 / 스택 / 큐 / 딕셔너리 (0) | 2023.06.19 |
---|---|
[CS][CS50] 자료구조 - 연결 리스트 / 트리 / 해시 테이블 (0) | 2023.06.19 |
[CS][CS50] 메모리 - 파일 쓰기 / 파일 읽기 (1) | 2023.06.19 |
[CS][CS50] 메모리 - 문자열 / 문자열 비교 / 문자열 복사 / 메모리 할당과 해제 / 메모리 교환, 스택, 힙 (0) | 2023.06.19 |
[CS][CS50] 메모리 - 메모리 주소 / 포인터 (1) | 2023.06.18 |