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 종료
'컴퓨터과학' 카테고리의 다른 글
리눅스 프로그래밍 4 - 메모리, 프로세스, 시그널, 환경, 로그인 (0) | 2022.06.01 |
---|---|
리눅스 프로그래밍 3 - grep 구현, 리눅스 디렉터리구조, 파일시스템관련 명령어 구현 (0) | 2022.05.31 |
리눅스 프로그래밍 1 - 리눅스 개요, 스트림 시스템 콜, cat 구현 (0) | 2022.05.29 |
파이썬 - 02. 원격 저장소 이용하기 + 온도변환기, 계산기, 스톱워치 (0) | 2022.05.11 |
파이썬 - 01. tkinter 기초(위젯, 레이아웃, 이벤트) + 원격저장소에 올리기 (0) | 2022.05.09 |