stdio

- 시스템 콜 함수인 read(), write()의 대안으로 바이트 단위를 읽던것과는 달리 독자적인 버퍼를 사용해 커널 스트림 줄/여러바이트 단위로 빠르게 입출력 가능

- FILE *fopen(const char *path, const char *mode) : open()과 유사하나 FILE 타입을 반환

- int fclose(FILE *stream), int fgetc(FILE *stream), int fputc(int c, FILE *stream);

- 줄단위 입출력 : 1줄 혹은 size -1만큼 읽고 쓰기 fgets(), fputs() 

- 고정 길이 입출력 : size_t fread(void *buf, size_t size, size_t nmeb, FILE * stream) - size x nmemb 바이트 읽어 buf쓰기

 * stdio에서 제공하는 fread, fwrite -> 시스템 콜 read, write랑 비슷하지만 stdio꺼라 다양한 환경서 사용 가능

- 파일 오프셋 처리 : fseek, fseeko(후자는 64비트 고려한 오프셋), ftell, ftello - 파일 오프셋 반환, rewind - 오프셋 처음으로

- file 타입 : int fileno(FILE *stream) 해당 파일 스트림의 디스크립터 반환, FILE *fdopen(int fd, const char *mode) 파일디스크립터로 스트림 포인터 반환

- 버퍼링 : fflush(FILE *stream) - 버퍼 데이터 write

- EOF, 에러 : feof(FILE *stream) - 스트림 EOF 플래그 가져옴, EOF 도달시 0이아닌 값나옴. ferror 스트림 에러 플래그 가져옴, 에러와 EOF 구분 불가시 사용, clearerr 에러와 EOF 플래그를 지움 -> 이런 상황 발생시 클리어하여 다시 읽기 가능 

 

 

head

- 처음 몇줄만 출력하는 명령어

- 버전 1 : 파일 지정 x

#include <stdio.h>
#include <stdlib.h>

static void do_head(FILE *f, long nlines);

int main(int argc, char * argv[])
{
    if (argc != 2)
    {   
        fprintf(stderr, "Usage: %s n\n", argv[0]);
        exit(1);
    }   
    do_head(stdin, atol(argv[1]));
    exit(0);
}

static void do_head(FILE *f, long nlines)
{
    int c;
    if (nlines <= 0) return;
    while ((c = getc(f)) != EOF) {
        if (putchar(c) <0 ) exit(1);
        if (c == '\n'){
            nlines--;
            if (nlines == 0) return;
        }
    }    
}

- 버전 2 : 파일 지정 o + 여러개

#include <stdio.h>
#include <stdlib.h>

static void do_head(FILE *f, long nlines);

int main(int argc, char * argv[])
{
    long nlines;

    if (argc < 2)
    {
        fprintf(stderr, "Usage: %s [file file ...]\n", argv[0]);
        exit(1);
    }
    nlines = atol(argv[1]);
    if (argc == 2)
        do_head(stdin, nlines);
    else{
        int i;

        for (i = 2; i < argc; i++){
            FILE *f;
            f = fopen(argv[i], "r");
            if (!f){
                perror(argv[i]);
                exit(1);
            }
            do_head(f, nlines);
            fclose(f);
        }
    }
    exit(0);
}

static void do_head(FILE *f, long nlines)
{
    int c;
    if (nlines <= 0) return;
    while ((c = getc(f)) != EOF) {
        if (putchar(c) <0 ) exit(1);
        if (c == '\n'){
            nlines--;
            if (nlines == 0) return;
        }
    }
}

옵션 파싱 : 짧은 옵션 get opt()

- 짧은 옵션 : -p, -a

- 긴 옵션 : --version, --help

- int getopt(int argc, char * const argv[], const char *optdecl) : 유닉스 OS 옵션해석 API, 짧은 옵션만 인식, 잘못된 옵션은 ?반환,

extern char *optarg;

extern int optind, opterr, optopt;

- 파라미터를 받지 않는 옵션으로 -a, -t, -x가 있는 경우 optdecl는 atx(순서 무관)

- 파라미터를 받는 옵션이 있는 경우 옵션문자 다음에 콜론 : 사용(f가 옵션받을시) : af:tx or atxf: or f:atx

- getopt 관련 전역변수 : optarg(현재옵션 파라미터), optind(현재 옵션인덱스), opterr(참인경우 에러 표시), optopt(현재 처리중인 옵션)

#include <unistd.h>

int main(int argc, char *argv[])
{
	int opt;
    while ((opt = getopt(argc, argv, "af:tx")) != -1)
    {
    	switch (opt)
        {
        	case 'a':
            	//a일떄 코드
                break;
           	case 'f':
            	//f일떄 코드
                break;
            .....
            case '?':
            	//잘못된 옵션이 전달될 때 코드
            	break;
        }
    }
    // 프로그램 본체
}

옵션 파싱 2 : 긴 옵션 getopt_long

#define _GNU_SOURCE

#icnlude <getopt.h>



int get_long(int argc, char * const argv[], const char *optdecl, const struct option *longoptdecl, int *longindex);

struct option{
	const char *name;
    int has_arg;
    int *flags;
    int val;
};

extern char *optarg;
extern int optind, opterr, optopt;

 

 

옵션 처리가 가능한 head

#include <stdio.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <getopt.h>

static void do_head(FILE *f, long nlines);
#define DEFUALT_N_LINES 10

static struct option longopts[] = {
    {"lines", required_argument, NULL, 'n'},
    {"help", no_argument, NULL, 'n'},
    {0, 0, 0, 0}
};

int main(int argc, char * argv[])
{
    int opt;
    long nlines = DEFUALT_N_LINES;


    while ((opt = getopt_long(argc, argv, "n:", longopts, NULL)) != -1){
        switch (opt){
            case 'n': // -n == --lines
                nlines = atol(optarg);
                break;
            case 'h':
                fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(0);
            case '?':
                fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(0);
        }
    }
    
    if (optind == argc){
        do_head(stdin, nlines);
    }
    else {
        int i;

        for (i = optind; i < argc; i++){
            FILE *f;

            f = fopen(argv[i], "r");
            if (!f){
                perror(argv[i]);
                exit(1);
            }
            do_head(f, nlines);
            fclose(f);
        }
    }
}

static void do_head(FILE *f, long nlines)
{
    int c;
    if (nlines <= 0) return;
    while ((c = getc(f)) != EOF) {
        if (putchar(c) <0 ) exit(1);
        if (c == '\n'){
            nlines--;
            if (nlines == 0) return;
        }
    }        
}

 

 

 

gdb로 디버깅하기

- 위 코드의     while ((opt = getopt_long(argc, argv, "n:", longopts, NULL)) != -1){ 를

    while ((opt = getopt_long(argc, argv, "n", longopts, NULL)) != -1){로 바꾸자(n:에서 :를 뺏다)

 -> :을 뺏으므로 이 옵션은 파라미터를 받지 않는다는 의미. 실제로는 이옵션이 있을때 몇 줄인지 받아야함.

int main(int argc, char * argv[])
{
    int opt;
    long nlines = DEFUALT_N_LINES;


    while ((opt = getopt_long(argc, argv, "n", longopts, NULL)) != -1){
        switch (opt){
            case 'n': // -n == --lines
                nlines = atol(optarg);
                break;
            case 'h':
                fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(0);
            case '?':
                fprintf(stdout, "Usage: %s [-n LINES] [FILE ...]\n", argv[0]);
                exit(0);
        }
    }

- gdb 실행 : gdb ./바이너리

- 옵션을 줘서 프로그램 시작 : run -n 5

 -> stdlib(libc)의 __GI_____strtol_l_internal 함수에서 세그먼테이션 폴트 발생

- bactrace 함수 호출 과정 보기 : __GL____strtol_l_internal에서 뒤로 돌아가면서 어떻게 함수들이 호출됬는지 볼 수 있음

 -> head3_debug.c 의 24번째 줄에서 strtol_c로 넘어감 -> 24번째 줄이 문제이므로 보기

- frame(backtrace 해당 줄 보기) : frame 1로 본 24번째 줄은 다음과 같음

- list : 해당 줄 위아래 5줄씩 주위를 보기

- print {변수명} : 해당 변수 값 보기

 -> 원래 optarg에는 -n 5를 하면서 5가 들어갔어야 하나 21번줄에 파라미터가 있음을 의미하는 n: 가아닌 파라미터가 없다는 의미인 n이 들어가 optarg에 파라미터 5가 입력되지않음.

- quit : gdb 종료

+ Recent posts