GetNextLine(2)

mtak0235·2021년 1월 18일
0

42Seoul

목록 보기
2/11

0.The point is

Mandatory Part
Prototype
int get_next_line(int fd, char **line);

Return Value :

1 : A line has been read
0 : EOF has been reached
-1 : An error happened

  • file descriptor로부터 파일을 읽어 줄 단위로 가져온다. 단 개행문자 \n은 제외하고 가져온다.

  • 반복문 내에서 get_next_line 함수를 호출하면 fd의 텍스트를 한 번에 한 줄씩 EOF가 나올 때 까지 읽는다.

  • FILE로부터, Standard input으로부터, redirection으로부터 텍스트를 잘 읽어와야한다.

BUFFER_SIZE 가 9999일 때, 1일 때, 10,000,000일때 작동 되는가? 이유도 아는가?

한 번 실행할 때 마다 한 줄 씩만 읽어야한다. newline을 만나면, 현재 line을 반환해야한다. 한 번에 모든 파일을 읽어온다음 처리하지 말자.

Standard Output 에 newline을 보내거나, CTRL-D를 보내면 어떻게 되는가?

CTRL-D는 EOF이다.

  • lseek 함수는 허용되지 않는다. File reading은 한 번만 발생해야한다.
  • binary file에 대해서는 undefined behavior이므로 신경쓰지 않아도 된다.
  • 전역변수 사용 금지

Bonus Part

  • n mandatory가 100점이어야만 bonus를 평가한다.
    get_next_line() 을 단 하나의 static 변수로 구현
    여러개의 file descriptor를 활용하려면, 예를 들어 fd 3, 4, 5를 이용한다면, get_next_line 함수를 3→4→5 →3→4→5 순서로 호출해야 읽기 스레드를 잃지 않을 수 있다.

1.consideration

  • fd는 음수면 안된다.
  • line도 NULL안된다.
  • buffer size도 1미만이면 안된다.
  • NULL과 개행문자를 고려하자.
  • macro 상수는 쓰지 말 것.

2.공부 해야할 것

2.1.파일 디스크립터(fd)

  • 운영체제가 만든 파일 또는 소켓의 지칭을 편히 하기 위해서 부여된 숫자이다.
  • 기본적으로 파일 디스크립터는 정수형으로 차례로 넘버링 되고 0,1,2는 이미 할당되어 있어서 3부터 파일 디스크립터를 부여한다.
    0 : 표준입력 (Standard Input)
    1 : 표준출력 (Standard Output)
    2 : 표준에러 (Standard Error)

2.2.read()

size_t read(int fd, void *buf, size_t bytes);
  • byte만큼 fd를 읽어 buf에 저장한다.
  • 성공시 읽어온 byte값을 반환하고, 실패시 -1을 반환한다.
    파일을 끝까지 읽었으면(EOF)0을 반환.

2.3. open()

#include <fcntl.h>
int open (const char *FILENAME, int FLAGS[, mode_t MODE])
  • open 성공시 fd 반환하고 실패 시 -1을 반환.

참고

시스템 콜

파일 입출력

2.3.gcc -d

  • 외부에서 #define을 정의한다.

  • 이 문제에서 컴파일은 다음과 같이 진행된다.

      $ gcc -Wall -Wextra -Werror -D BUFFER_SIZE=32 get_next_line.c get_next_line_utils.c

    즉, BUFFER_SIZE를 컴파일할 때 정하게 된다.

2.4.static variable(정적 변수)

사용 목적

  • 지역 / 전역 변수의 값을 기억해 두고 싶을 때 사용한다.

    ⚠ get_next_line()을 호출할 때 이전에 읽은 값 중 일부는 기억하고 있어야 하기에, 정적 변수에 기억해야 할 값들을 넣어둬서 다음 호출에 대비한다.

  • 데이터 영역에 저장되어 프로그램 종료 시 까지 남아있기 때문에, 다음 line을 읽을 시작 주소을 계속 저장할 수 있도록 backup 버퍼를 static 변수로 선언해야 한다.

사용 방법

  • static 자료형 변수이름
  • 정적 변수의 범위
    • A함수 내부에 선언 ➡ A함수 내부에서만 사용
    • A.c 내부에 선언 ➡ A.c 내부에서만 사용
  • 정적 변수는 프로그램이 시작될 때 생성 및 초기화되고 프로그램이 끝날 때 사라진다.
    (고로 정적 변수가 있는 함수를 재호출하면 초기화를 무시한다. )
  • 정적 변수는 초깃값을 지정하지 않으면 0으로 초기화된다.

사용 예시

  • accu_bufs는 함수 내부 정적변수인 backup을 누적해서 더해나가는 함수
#include <stdio.h>
#include "get_next_line.h"

char *accu_bufs(char *str) {
	static char *backup = "";
	char *new_backup;
	// new_backup의 길이를 구하기
	size_t new_backup_len = ft_strlen(backup) + ft_strlen(str);

	if (!(new_backup = (char *)malloc(sizeof(char) * (new_backup_len + 1))))
		return (NULL);

	// new_backup에 backup 값과 새로운 값 복사
	size_t idx = ft_strlcpy(new_backup, backup, new_backup_len + 1);
	ft_strlcpy(new_backup + idx, str, new_backup_len + 1);	

	// backup이 new_backup을 가리키도록 설정
	if (ft_strlen(backup) != 0)
		free(backup);
	backup = new_backup;
	return (backup);
}

int main(void) {
	printf("%s\n", accu_bufs("Hello 42"));
	printf("%s\n", accu_bufs(" world, "));
	printf("%s\n", accu_bufs("hao!"));
	return (0);
}
// 실행 결과
// Hello 42
// Hello 42 world, 
// Hello 42 world, hao!

새로 알게된 점

  • 변수

    • 지역 변수 : 스택에 들어가는 변수
      = 일반 변수

    • 전역 변수 : 스택에 들어가지 않는 변수

      = static 전역 변수 + heap 전역 변수

2.5.heap 과 stack


메모리 공간 = 코드 영역 + 데이터 영역 ( heap + stack )
처음부터 모든 메모리 공간을 사용하는 것은 아니고, 보통 힙과 스택 사이가 비어있다. 이들은
(1) 스택이라면 함수가 호출될 때
(2) 힙이라면 동적으로 할당했을 때
할당된다.
그렇다면 힙과 스택은 최대 얼마까지 할당할 수 있을까?
힙과 스택은 기본적으로 사용자 지정에 의해 최대 크기를 정할 수 있지만 하드웨어는 유한하기 때문에 할당 가능한 최대 크기도 유한하다. 스택과 힙은 남은 램의 크기만큼 할당할 수 있다.
코드에서 설정한 스택 사이즈를 오버하는 큰 메모리를 스택에 할당하면(called Stack Over Flow) "Segmentation fault"와 함께 프로그램이 죽은 것을 확인할 수 있으니, 그 때는 힙 메모리를 활용해라.

stack size를 확인하는 리눅스 명령어
> ulimit -a

3. 목표

1234\n123\n이고 버퍼사이즈가 20일때
첫번째 gnl에서 read가 끝까지 다 읽어서
두번째 gnl을 호출했을때는 read가 0을 뱉는 것으로 알고있습니다
첫번째 gnl에서 1234가 출력되고 save에 123\n이 저장 되어 있어서
두번째 gnl에서 123을 출력하려면 save안에서 개행문자를 찾아줘야 되는것으로 알고있습니다

profile
Born to explore.

0개의 댓글