이중배열과 배열 포인터에 대한 이해
gnl의 bonus를 진행하기 위해서는 이중배열 char buffer[fd][BUFFER_SIZE + 1]을 memset할 수 있어야했다. -> 가능하다.!!
(이중배열을 써야하는 건가? (BUFFER_SIZE+1) * fd 이면 값이 꽤나 많이 클 것으로 예상됨)
char buffer_double[5][20];
memset(buffer_double[1], 0, 20);
printf("&buffer_double[2][0] p : %p\n", &buffer_double[2][0]);
printf("&buffer_double[2] p : %p\n", &buffer_double[2]);
printf("buffer_double[2][0] p : %p\n", buffer_double[2][0]);
printf("buffer_double[2] p : %p\n", buffer_double[2]);
printf("buffer_double[2][0] d : %d\n", buffer_double[2][0]);
printf("buffer_double[2] d : %d\n", buffer_double[2]);
printf("*buffer_double[2] d : %d\n", *buffer_double[2]);
&buffer_double[2][0] p : 0x7ffee3298738
&buffer_double[2] p : 0x7ffee3298738
buffer_double[2][0] p : 0x0
buffer_double[2] p : 0x7ffee3298738
buffer_double[2][0] d : 0
buffer_double[2] d : -483817672
*buffer_double[2] d : 0
[2]은 자동적으로 [2][0]의주소를 가리킨다.
배열 포인터는 배열의 주소가 아니라 배열의 첫번째 요소의 주소를 가리킨다. 왜냐하면 배열에는 주소가 없기 때문이다.
https://blockdmask.tistory.com/56
개행이후에 저장하는 문자열에 대한 생각.
BUFFER_SIZE가 엄청 큰 경우 저장하는 문자열에 '\n'이 많이 담길 수 있다. 이때는 read를 읽기전에 처리해주어야한다.
1. static char *remain_string[2055]로 개행 이후를 저장함.
2. linked_list로 구현
1번으로 잘 구현 한 것 같은데 자꾸 "할당되지않은 주소에 free를 하였습니다" 라는 에러가 발생한다.
/dev/null : 아무것도 씌여있지않은 unix의 특수 파일
open("/dev/null", O_RDONLY); // return 3;
line[0] == '\0';
1. fd가 음수, open으로 할당되지않은 fd값이 get_next_line(int fd, **line)에 들어갔을 때는 line에 빈문자열 할당해주고 -1 반환한다.
2. bufferr size가 음수이거나 0이면, line에 할당해주지않고 -1을 반환한다.
3. open에 의해서 이전에 생성되었던 fd값과 동일한 fd값이 생긴다면, static variable을 다시 사용하기때문에, 한번 끝까지 사용한 fd에 대해서는 static variable을 (char *)의 포인터를 NULL로 초기화 해줘야한다.
4. 존재하는 fd로 파일을 다 읽어서 read값이 0을 return한 이후에 다시 한번 get_next_line을 실행시켰을 때, 빈 문자열을 반환시켜야하는 테스터기가 있다. (https://github.com/Alexandre94H/gnl-war-machine-v2019)
//ft_strdup에 추가해준 사항
if (str == NULL)
{
str_dup = malloc(sizeof(char));
str_dup[0] = '\0';
return (str_dup);
}
//
#include "get_next_line.h"
int get_next_line_sub(int fd, char **line)
{
char buffer[BUFFER_SIZE + 1];
char *oneline_string;
static char *remain_string[1000] = {0, };
int validation;
oneline_string = NULL;
if (exist_newline_in_remain_string(remain_string, fd, line) == 1)
return (1);
ft_memset(buffer, 0, (BUFFER_SIZE + 1));
while ((validation = read(fd, buffer, BUFFER_SIZE)) > 0)
{
if (read_until_newline(buffer, fd, &oneline_string, remain_string) == 1)
{
*line = oneline_string;
return (1);
}
}
if (validation == -1)
{
*line = ft_strdup("");
return (-1);
}
*line = ft_strdup(remain_string[fd]);
free(remain_string[fd]);
remain_string[fd] = NULL;
return (0);
}
배운 점
1. 함수 밖을 빠져나가는 함수내에서 정의된 heap영역이 없게해야한다. 외부에서 실행한 free 함수가 함수 내부에 영향을 끼치면 안된다.
따라서 항상 함수 밖으로 내보내는 값은 ft_strdup을 이용해서 복사본을 내보내야 메모리누수를 미연에 방지할 수 있다.
(이런 이유때문에, malloc(0)이 1byte 크기의 동적할당해주는 이유 같음. malloc을 했다는 건 메모리공간이 발생했다는 것으로 규칙을 정하면, 쓰는 사람이 편할 수 있기때문에)
2. github에 있는 많은 테스트기를 이용하는 편이 좋다. 특히 내부구조가 간단할수록 에러가 났을 때 내가 고치기쉽고, 내용이 많은 경우에는 bash코드가 복잡하여 고치기는 오히려 어렵다.
3. 25줄 압박에서 벗어나고, segfault 및 memory leak을 피하기위해서는 항상 예외처리를 위한 함수를 만들어야한다.
ex) get_next_line(int fd, char **line)에서는 예외처리를 다루고, 실제로 다루는 함수는 get_next_line_sub(int fd, char **line)에서 다루도록한다.
4. 원하는 동작을 하는 프로그램을 만들 때, 디버깅을 하면서 접근해야한다. 또한 디버깅 역시 체계적으로 순서를 갖추어서 접근해야한다.(printf에서 적용) 그렇지 않으면 나중에 정말 힘들어진다 꼴도 보기싫어질 정도다.
'42cursus > get-next-line' 카테고리의 다른 글
개행으로 끝나느냐, 개행으로 끝나지 않느냐 (0) | 2020.07.22 |
---|---|
get_next_line(mandatory) (0) | 2020.07.02 |
프로세스의 메모리, 파일 입출력함수 (0) | 2020.07.01 |