본문 바로가기
  • Fearless
프로그래밍언어/C언어

[C언어문법] 11. preprocessor & File I/O

by Albatross 2021. 12. 15.
반응형

1. preprocessor

<그림1>

1) macro function

#define SQUARE(x) ((x)*(x))

#define MAX(x,y) ((x)>(y)?(x):(y))

#define PRINT(x) if (debug==1 && mode ==1) \ printf("%d",x); - 한줄이상의 macro func. 정의는 \를 활용

macro function은 매개변수를 통하여 결과물을 출력하는 함수를 형성한다. 

다만 define이 치환문이기 때문에 예를 들어 SQUARE함수의 경우 a+b를 인자로 전달할 시 a+b*a+b가 도출된다. 이를 방지하기 위해 꼭 괄호를 붙인다. macro func.는 함수 호출할 필요가 없어 실행속도가 빠르다.

 

2) #ifdef

#define DEBUG_1

double average(int x, int y)
{
#ifdef DEBUG_1
      printf("x=%d, y=%d\n", x, y);
#endif
      return (x+y)/2.0;
}

#ifdef 기호상수의 기호상수가 정의되어 있다면 #endif가 나타날때 까지의 구간을 실행한다. 

위 예시에서는,

1)#define DEBUG_1를 통해 기호상수 DEBUG_1을 정의한다. 

2)기호상수가 정의되었기 때문에 #ifdef DEBUG_1부터 #endif 사이의 printf명령문이 실행된다. 

3)만약 #define DEBUG_1이 없다면 average함수는 return값만 반환한다. 

 

3) #ifndef

#ifndef 기호상수의 기호상수가 정의되어 있지않다면 #endif가 나타날때 까지의 구간을 실행한다. 

 

4) #undef

이전에 정의했던 macro define을 해제한다. 

 

2. multi-source file

<그림2>

다중소스파일은 서로 관련된 코드끼리 소스 파일에 분류하여 저장할 수 있다. 따라서 소스 파일 재사용이 쉽다. 

 

3. File I/O

data를 송수신하기 위한 프로그램, OS간 통로가 존재한다. 키보드에서 입력되는 stdin이 하나 있고, 프로그램을 거쳐 모니터로 display되는 stdou, 그리고 error를 통보하는 stderr 세가지 스트림이 존재한다. 그러나 이 통로를 키보드나 디스플레이가 아닌 파일로 변경해줄 수 있다. 

 

void main(void)
{
    int c = 0;
    int count = 0;
    while (c! = EOF)
    {
    	c = getchar();
        putchar(c);
        count += 1;
        printf("%d", count);
    }
    printf("**END**\n");
}

이 경우 stdin을 통해 getchar을 입력받아 c에 저장하고, putchar(c)를 통해 이를 display에 출력한다. 

입출력에는 각각의 버퍼가 존재한다. 입출력 데이터는 버퍼에 저장되고, OS가 이들을 한번에 처리하여 CPU 작업량을 줄인다. 모든 입력문자는 입력 버퍼에 임시적으로 저장되었다가 \n이 입력되면 버퍼에 있던 문자를 프로그램에 전달한다. 

 

void main()
{
    char ID[5];
    char name[10];
    fputs("Enter ID num : ", stdout);
    fgets(ID, sizeof(ID), stdin);
    fputs("Enter name : ", stdout);
    fgets(name, sizeof(name), stdin);
    printf("ID: %s\n, ID);
    printf("name : %s\n", name);
}

fgets()의 경우 ID로 123이 입력된 경우에, 123과 \n(엔터)에다가 \0까지 추가해서 버퍼에 저장한다. 따라서 ID를 printf()하면 줄바꿈이 발생한다. 또 ID에 5를 초과하는 123456이 들어왔을 경우, 1234(\0)만큼만 불러온다. 배열에도 1234만큼만 저장되고, 버퍼에 56\n이 남아있는 것이다. 이는 다음 fgets() 실행에서 버퍼에 남은 데이터가 읽혀온다. 

 

파일처리는 1)Open File, 2)Read or Write File, 3)Close File의 단계를 거친다. 

파일 입출력을 위해선 FILE* p 형식의 파일 포인터를 사용한다. 

 

1) Open File

FILE* fopen(const char* filename, const char* mode)

 

파일오픈모드=파일접근모드+데이터입출력모드

데이터 입출력 모드는 t(text mode) 혹은 b(binary mode)이며 default값은 t이다. 

<그림1> <그림2>

2) Close File

int fclose (FILE *stream)

오류없이 종료하면 0을 반환하고, 오류가 발생하면 -1을 반환한다. fopen은 사용하면 반드시 fclose로 끝나야한다.

 

3) error check

#include <stdio.h>
int main(void)
{
      int state;
      FILE *file = fopen("test.txt", "w");
      if (file == NULL)
      {
            printf("error\n");
            return 1;
      }
      fputs("abcd\n", file);
      state = fclose(file);
      if (state != 0)
      {
            printf("error\n");
            return 1;
      }
}

File Open&Close 때 error가 발생하는 경우는 다음과 같다. 

(1)File Open

a. open하려는 파일명이 존재하지 않을 때

b. 새 파일을 생성하기 위한 메모리공간이 부족할 때

: 이 경우 스트림 포인터 변수는 NULL값을 가진다. 

(2)File Close

a. fclose 함수는 파일이 성공적으로 닫히면 0을 반환한다

b. 에러가 있는 경우 EOF(-1)를 반환한다. 

: 따라서 if문을 활용하여 파일이 성공적으로 닫혔는지 꼭 확인해줘야한다. 

 

4) EOF(end of file)

EOF는 파일의 끝이라는 함수들의 신호를 뜻한다. 이름이 그렇다고해서 파일의 끝에 존재하진 않는다. 

파일을 읽는 함수들의 파일의 끝에 다 다르면, EOF(-1)을 반환하게 된다. int fgetsc(FILE * stream)을 사용하여 파일 끝에서 EOF(-1)값을 반환받는다.  이 때 char형을 사용하면 255번 code(-1)과 EOF간에 차이가 없기 때문에 꼭 int를 사용해야만 한다. 

 

 

5) File I/O functions

int fprintf(FILE *stream, const char*format[,argument]...): 파일에 데이터를 출력한다. 에러시 음수반환

int fscanf(FILE *stream, const char*format[,argument]...): 파일로부터 데이터를 입력받는다. 모두 읽으면 EOF(-1)반환.

 

이 예시를 순차적으로 읽어나가보자. 

a. FILE *fp_r, *fp_w로 파일의 구조체형식을 갖는 포인터를 선언했다. 

b. 각각에 reading, writing 목적으로 파일을 열어주었고, NULL 여부를 확인해주었다.

c. fp_r(file stream)을 통해 %d 형식의 data를 반복하여 읽어온다. feof(fp_r)일 경우 break해준다. 이는 파일의 끝부분에 도달했는지를 확인해주는 함수다. fprintf()를 통해 읽어온 데이터를 fp_w stream을 통해 출력한다. 이 과정을 break가 걸릴 때까지 반복한다. 

d. fp_r, fp_w를 닫아준다. 닫아 줄때도 fclose()가 반환하는 값이 0이 아닌지 꼭 확인해줘야한다. 

 

char* gets_s(char* s, int n) : 표준입력으로 문자열을 읽고 반환한다.

char* fgets_s(char* s, int n, FILE* spln) : 파일로부터 문자열을 읽고 반환한다.

int puts(const char* s) : 모니터에 문자열을 출력한다.

int fputs(const char* s, FILE* spout) : 파일에 문자열을 출력한다.

fputs, fgets를 쓰더라도 stream경로를 stdout, stdin처럼 모니터와 키보드로 설정해주면 직접입력받을 수 있다. 

 

int fseek(FILE *stream, long offset, int origin)은 파일 위치를 제어한다. 

origin : SEEK_SET(맨앞), SEEK_CUR(현위치), SEEK_END(맨끝-마지막데이터 한칸 뒤)

offset : origin에서 이동하고 싶은 바이트수

 

 

4. 연습문제

 

반응형