GetNextLine(2)

mtak·2021년 1월 18일
0

42Seoul

목록 보기
2/13

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
노는게 젤 조아. 친구들 모여라!!

0개의 댓글