42Seoul - get_next_line

devicii·2022년 1월 13일
0
post-thumbnail

get_next_line

char *get_next_line(int fd)

특징

해당하는 fd의 파일로부터 라인단위(개행 문자로 구분)로 글을 읽어온다. 반환은 개행 문자를 포함해서 반환한다.
함수를 한 번 호출할 때 마다, 한 줄씩 글을 읽는다.

리턴값

읽기 성공하면 1, EOF(End of File)에 다다르면 0, error가 발생한다면 -1 반환

선행지식

1.static 키워드

  • static키워드를 변수 앞에 붙이고 함수 안이나 밖에 선언할 수 있다.
  • static [데이터명][변수명];
    사용범위는 선언된 함수 안과 해당 파일에서만 사용할 수 있고 종료는 프로그램이 종료될 때 같이 된다.
함수 안에서는 **지역변수의 성향**을 가지지만 종료될 때까지 값이 남아있는 것이 특징이다.

//예시 1
#include <stdio.h>
#include <stdlib.h>

static int g_var; //0으로 초깃값이 설정된다.

 void func()
 {
	 g_var++;
 }
 
 int main(void)
 {
	printf("%d \n", g_var);	 
	func();
	printf("%d \n", g_var);

	return (0);
 }
 


//예시 2
 void func()
 {
	static int	var;
	printf("%d \n", var);
	var++;
 }
 
 int main(void)
 {
	printf("%d \n", g_var);	 
	func();
	printf("%d \n", g_var);

	return (0);
 }

2. read();

프로토타입
size_t read (int fd, void *buf, size_t nbytes)

int fd == 파일 디스크립터
void *buf == 파일을 읽어들인 버퍼
size_t nbytes == 퍼버의 크기
return =>
size_t == -1 실패
0 == 정상적으로 실행되었다면 읽어들인 바이트 수

3.FD (File Descriptor)

유닉스 / 리눅스 계열의 시스템은 모든 파일 및 디렉토리, 소켓 및 모든 장치마저도 파일로 관리된다.
파일 디스크립터는 프로세스에서 열린 파일의 목록을 관리하는 테이블의 인덱스이며, 특정 파일에 접근하려면 파일 디스크립터가 필요하다. 파일을 핸들링 하기 위해서 해당 파일의 주소를 참조하여 접근하는 방식이다.

기본적으로 할당받는 파일 디스크립터 값
0 : 표준 입력(standard input)
1 : 표준 출력(standard output)
2 : 표준 에러(standard error)

또한 파일 디스크립터는 모두 양수의 값이고, -1은 잘못된 접근을 했을 때 반환된다.

풀이

예를 들어 a.txt라는 파일에 아래와 같은 형식의 텍스트가 있다고 가정하고, BUFFER_SIZE가 3 으로 주어진다고 가정하자.

a.txt

1 2 3 \n
4 5 6 \n
7 8 9

전체적인 큰 흐름은 정적 포인터 변수 1개와 문자열을 리턴할 포인터 변수 1개를 선언한다.
1. 정적 변수 lstr에 123\n45까지 buff라는 임시변수를 활용해 담아준다.
2. 반환은 123\n을 해야하니 line에다가 lstr에서 123\n만 담아준다.
3. lstr에는 다음 행 출력을 위해 123\n45에서 이미 출력한 것들을 제외한 45를 lstr에 담아준다.

 첫 실행
 
 1 2 3 \n 출력 전 lstr = 1 2 3 \n 4 5
 4 5 6 \n line = 1 2 3 \n
 7 8 9    출력 후 lstr = 4 5
 
두 번째 실행

1 2 3 \n 출력 전 lstr = 4 5
4 5 6 \n line = 4 5 6 \n
7 8 9	  출력 후 lstr = 7
세 번째 실행

1 2 3 \n 출력 전 lstr = 7 8 9
4 5 6 \n line = 7 8 9
7 8 9	  종료

// 마지막 번째는 read를 하던 중 EOF를 만난다면 함수가 종료되고, 읽은 곳까지의 양수를 리턴한다.

문제 해결

1.댕글링 포인터 (Dangling pointer)

gnlTester로 돌리던 중 계속 아래와 같은 코멘트가 생겼다.
하단의 코드에서 한참을 헤매다가 알아낸 것이 댕글링 포인터다.

/* free()를 사용하면 할당된 메모리를 해제할 수 있다. 
그런데 포인터가 여전히 해제된 메모리 영역을 가리키고 있을 땐 문제가 될 수 있다. 
이것을 댕글링 포인터라고 한다 */

if (result == - 1)
{
	free(buffer);
  	return (0); <-- return (0);를 추가해줬다.
}

/*free()를 사용하면 할당된 메모리를 해제할 수 있다. 
그런데 포인터가 여전히 해제된 메모리 영역을 가리키고 있을 땐 문제가 될 수 있다. 
이것을 댕글링 포인터라고 한다. */

int *ptr;
ptr = malloc(sizeof(int));
*ptr = 1;
printf("%d\n", *ptr);

free(ptr);
*ptr = 2;   <-- 이렇게 ptr변수를 free()해준 이후에 다시 메모리에 접근하면 문제가 발생할 수 있다.


//해결 방법

int *ptr;

ptr = malloc(sizeof(int));
*ptr = 1;
printf("%d\n, *ptr);

free(ptr); 
ptr = 0;  <-- 위와 다르게 포인터를 null로 바꾸면 포인터가 해제된 메모리를 가르키지 않아 잘못된 영역을 참조하지 않는다.

2. OPEN_MAX 이슈

OS를 직접 제어해 OPEN_MAX보다 더 큰 수를 파일 디스크립터로 더 활용할 수 있다고 한다.
그래서 헤더 파일을 이용해 OPEN_MAX의 값을 상수로 줄까 고민하다 하지 않았다.
이유는 다음과 같다.
OPEN_MAX와 같은 메크로는 범용성을 위해서 활용한다. 어느 OS 환경이던 컴퓨터의 사양이던 프로그램이 잘 작동해야 한다. 그러한 이유 때문에 우리는 메크로를 이용해 더 많은 환경에서 프로그램이 동작할 수 있음을 보장해줄 의무가 있다.

참고자료

참고 블로그

profile
Life is a long journey. But code Should be short :)

2개의 댓글

comment-user-thumbnail
2022년 1월 17일

잘 보고 갑니다 ^^

1개의 답글