Get Next Line은 읽어온 파일의 한 줄을 반환하는 프로그램이다. 자세한 코드는 아래 저장소에.
size_t read(int fd, void *buf, size_t bytes)
외부에서 #define
을 정의한다.
이 문제에서 컴파일은 다음과 같이 진행된다.
$ gcc -Wall -Wextra -Werror -D BUFFER_SIZE=32 get_next_line.c get_next_line_utils.c
즉, BUFFER_SIZE를 컴파일할 때 정하게 된다.
파일을 read 할 임시 버퍼를 만든다.
char buf[BUFFER_SIZE];
read한 버퍼를 백업할 static 버퍼를 만든다.
static char *backup
read(fd, buf, BUFFER_SIZE);
를 해서 라인을 읽은 다음,
buf
를 static 변수 backup
에 백업한다.
backup
안에 개행문자가 있는지 없는지 검사한다.
개행문자가 있으면 다음 단계로 넘어가고, 없다면 개행 문자가 있을 때 까지 3번으로 돌아가 파일을 계속 읽으면서
개행문자가 있는 backup
을 개행문자 전과 후로 잘라서, \n
전까지는 line
에다가 주고 \n
후는 다시 static 변수 backup
에 백업한다. -> split_line 함수
** line은 왜 이중포인터인가?
-> 읽어들인 라인(char *)의 주소를 저장하기 위해서다
GNL을 호출할 때 마다 GNL 내부에서 line을 재할당 해줘야 되는가?
-> get_next_line() 에서는 한 줄 라인의 길이가 얼마나 길어질지 모르기때문에 항상 line에 동적으로 할당해야한다.
아무것도 적혀있지 않은 빈 파일을 읽었을 때 line에 할당을 해야하나?
-> line에 빈 문자열을 할당하고 0을 반환한다.
버퍼 사이즈가 10이라고 가정하고
abcd
1234
를 읽는다고 생각하면 한번 부르면 abcd
를, 한번 더 부르면 1234
를 *line에 넣어줘야 한다.
그런데 버퍼 사이즈가 10이라 한번에 파일을 끝까지 다 읽어버리기 때문에 처음 함수가 호출 됐을 때 나머지 1234
를 따로 저장해둬야 한다. 그래서 따로 저장할 때 함수가 끝나도 날라가지 않게 하려고 static(정적) 변수를 쓰게 된다.
파일을 끝까지 다 읽어서 0 반환
line = 0으로 메모리 해제가 된 상황. 남은 backup을 line에 넣어준다. main에서 다른 파일을 read할 수 있으니 backup = 0; 해준다.
free(buf)를 해버리면 *line이 담고있는 주소값에 대한 메모리가 해제돼버려서 올바른 값이 들어가지 않고 쓰레기값이 남게된다.
빈 파일을 읽어서 0 반환
*backup이 아무 값도 가리키고 있지 않기 때문에 위와 같은 경우로 묶었을 때 segmentation fault가 뜬다.
read_size == 0 && *backup == 0 인 경우를 새로 추가해서
*line에 1만큼의 공간을 동적할당하고 널문자를 넣어줘야 한다.
char buf = malloc(10) 와 같이 동적할당 된 메모리 주소를 buf가 가리킬 때, free(buf) 와 buf = 0; 의 차이는?
후자는 할당받은 메모리 주소를 잃어버린 게 돼서 해제가 불가능 하게 된다. 이런 상황을 memory leak, 메모리 누수라고 한다.
backup의 세로 축은 fd가 들어간다. *backup이 가리키는 값을 fd에 따라서 따로 관리하고 싶었다. 한 파일이 끝나기 전에 gnl로 다른 파일을 호출할 수도 있으니까.
"고민한 부분은 사용자가 한번에 파일을 몇 개나 열 수 있도록 할 것인가? " 였다. 처음에는 그냥 50개 정도로 설정했었는데, 나중에 알고보니 파일 디스크럽터는 0 ~ OPEN_MAX 까지의 값을 가질 수 있으며, OPEN_MAX 값은 플랫폼에 따라 다르다고 한다.
프로세스 하나가 동시에 open할 수 있는 최대 파일 갯수는 다음의 명령을 사용하여 알아볼 수 있다.
$ getconf OPEN_MAX
65536
혹은 sysconf() 함수를 사용할 수도 있다.
sysconf(_SC_OPEN_MAX);
>>> return 65536
100만 까지는 되는데 1000만 부터는 Segmentation fault (core dumped)가 뜬다.
-> 자동변수는 stack 영역에 저장되는데, 보통 스택 사이즈가 윈도우는 1메가, 리눅스는 8메가로 설정되어있다.만약에 char buf[BUFFER_SIZE + 1];
라고 선언하고 여기에 스택 사이즈보다 큰 수를 받으면 스택 오버플로우가 생길 수 있다
정적
변수로 선언하여 데이터 영역에 잡는다.전역
변수로 선언하여 데이터 영역에 잡는다.malloc
등을 사용, 동적 할당하여 힙 영역에 잡는다.