오늘은 권오흠 교수님께서 전화번호부를 만드는 예제를 가르쳐주셨다.
여태까지 배웠던 C언어의 기초를 응용해서 전화번호부 예제를 만들 수 있었다.
void add() {
char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE];
scanf("%s", buf1);
scanf("%s", buf2);
names[n] = strdup(buf1); // strdup 와 strcpy 차이는?
numbers[n] = strdup(buf2);
n++;
printf("%s was added successfully.\n", buf1);
}
buf1 과 buf2 는 지역변수이기 때문에 lifetime 은 짧다. 그러므로 이 함수를 벗어나게 되면 두 변수 모두 소멸하게 된다. 그래서 strdup 를 써주어야 하는 이유는 strdup 함수는 buf1 에 있는 문자열을 복사한 새로운 복제본의 주소를 리턴하게 되기 때문이다. strdup 의 실행원리는 지난 시간에 알아보았었다.
그리고 strcpy 가 사용될 수 없는 까닭은 마찬가지로 지난 시간에 배웠지만, n 이라는 변수는 현재 문자열이 아닌 포인터 변수이기 때문에 strcpy 함수가 사용될 수 없었다.
< C 언어에서 메모리 레이아웃 >
전역변수는 함수의 외부에 선언된 변수들이며, 지역 변수는 함수의 내부에 선언된 변수들을 말한다.
특징을 알아두는게 좋을 것 같아서 밑에 정리해보았다.
전역변수
- 프로그램이 시작될 때 메모리가 할당되며 프로그램이 종료될 때까지 유지된다
- Data section 이라고 부르는 메모리 영역에 위치한다
지역변수
- 자신이 속한 함수가 호출될 때 메모리가 할당되며 함수가 retrun될 때 소멸된다
- 스택(stack)이라고 부르는 영역에 위치한다.
그리고 동적 메모리 할당의 특징이다.
정리해보면 malloc 함수를 함수 내부에 할당받았더라도 그렇게 할당된 메모리는 그 함수에 국한되지 않는다. 함수가 return 되고 종료되더라도 사라지지 않고 내가 malloc 함수로 할당한 메모리의 주소만 기억할 수 있다면 계속 사용이 가능하다.
이 메모리가 더이상 필요 없다고 생각될 때는 메모리를 해제할 수 있으며, 메모리를 해제시키기 위해서는 명시적으로 free() 함수를 선언해야 한다.
따라서 위 그림의 C언어 메모리 레이아웃은 프로그램이 실행될 때 각 변수들이 저장되는 공간을 그림으로 나타낸 것이며, code 라는 부분은 우리가 프로그램한 코드가 컴파일된 기계어로 차지하고 있는 부분이다. 그리고 data section 과 code 부분은 메모리에서 크기가 변하지 않는다. (= 일정하다 ) 그러나 stack 부분은 보통 메모리의 윗부분에 자리하고 있으며, 생기거나 없어질때마다 윗부분이 늘어나거나 줄어들거나 한다. ( = 일정하지 않은 편이다) 그리고 heap 이라는 부분은 메모리의 아랫부분에 자리하고 있으며 이 역시도 늘어나거나(malloc) 줄어들었다(free())가 하기 때문에 일정하지 않다.
< Remove 함수 >
void remove() {
char buf[BUFFER_SIZE];
scanf("%s", buf); // 배열이니까 & 쓰지 않는 것을 주의한다.
int i;
for (i = 0; i < n; i++) {
if (strcmp(buf, names[i]) == 0) {
names[i] = names[n - 1];
n--; // 사람 수 감소된다.
printf("'%s' was deleted successfully. \n", buf);
return;
}
}
printf("No person named '%s' exists.\n", buf); // return 되지 않으면 실행된다.
}
위 코드문은 전화번호부에서 사람을 삭제하는 코드문이다. 참고로 strcmp 함수는 2개의 문자열이 같은지 검사하는 함수로, 2개의 문자열이 같으면 1이 아닌 0을 반환하는 함수이다. (생각보다 실수하므로 주의하자) names[ i ] 는 i번째 사람의 이름을 말하며 만약 buf 에 입력한 문자열이 i번째 사람의 이름 문자열과 같다면 삭제를 해야한다.
이때 i번째 사람의 이름을 그냥 삭제할 수 이유는 배열은 삭제하고 그 부분이 빈공간으로 남아있으면 다른 코드문에서 for문 반복을 하면서 에러가 발생한다. 그러면 해결 방법을 생각해보자.
이 배열은 사실 순서가 정렬되어 있지 않는 배열이다. 따라서 i번째의 사람이름을 삭제하고 나서 마지막 n-1번째의 사람이름을 i번째에 할당되어도 크게 상관없다. 즉, 맨 마지막 사람을 삭제된 공간으로 옮긴다.
코드를 다 리뷰하면 좋겠지만 내가 다시 짚어봤으면 하는 부분들만 간단히 정리해서 보는게 나중에 다시 볼때 더 편할 것 같아서 간추렸다.
실습 코드
#include <stdio.h>
#include <string.h>
#define CAPACITY 100
#define BUFFER_SIZE 20
char * names[CAPACITY]; /* names */
char * numbers[CAPACITY]; /* phone numbers */
int n = 0; /* number of people in phone directory */
void add();
void find();
void status();
void remover();
int main() {
char command[BUFFER_SIZE];
while (1) {
printf("$ ");
scanf("%s", command);
if (strcmp(command, "add") == 0)
add();
else if (strcmp(command, "find") == 0)
find();
else if (strcmp(command, "status") == 0)
status();
else if (strcmp(command, "delete") == 0)
remover();
else if (strcmp(command, "exit") == 0)
break;
}
return 0;
}
void add() {
char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE];
scanf("%s", buf1);
scanf("%s", buf2);
names[n] = strdup(buf1);
numbers[n] = strdup(buf2);
n++;
printf("%s was added successfully.\n", buf1);
}
char *strdup(char *s)
{
char *p;
p = (char *)malloc(strlen(s) + 1);
if (p != NULL)
strcpy(p, s);
return p;
}
void find() {
char buf[BUFFER_SIZE];
scanf("%s", buf);
int i;
for (i = 0; i < n; i++) {
if (strcmp(buf, names[i]) == 0) {
printf("%s\n", numbers[i]);
return;
}
}
printf("No person named '%s' exists.\n", buf);
}
void status() {
int i;
for (i = 0; i < n; i++)
printf("%s %s\n", names[i], numbers[i]);
printf("Total %d persons.\n", n);
}
void remover() {
char buf[BUFFER_SIZE];
scanf("%s", buf);
int i;
for (i = 0; i < n; i++) {
if (strcmp(buf, names[i]) == 0) {
names[i] = names[n - 1];
numbers[i] = numbers[n - 1];
n--;
printf("'%s' was deleted successfully. \n", buf);
return;
}
}
printf("No person named '%s' exists.\n", buf);
}
'💡 자료구조' 카테고리의 다른 글
전화번호부 v3.0 - 배열 재할당, 라인단위 입력과 문자열 tokenizing (0) | 2021.06.22 |
---|---|
전화번호부 v2.0 - 파일을 저장하고 로드하기, 알파벳 순으로 정렬 (0) | 2021.06.19 |
문자열 예제 (0) | 2021.06.18 |
시간 복잡도와 빅-오 표기법 (0) | 2021.06.17 |
문자열 (0) | 2021.06.16 |