42 Starter 스터디 모임에서 진행하는 C 언어 A to Z 스터디에 내용을 정리해서 포스팅을 진행합니다.

해당 스터디에서는 42 Piscine 과정에서 채우지못한 부족한 C언어 역량을 키우는 것을 목적으로 합니다.


출력

int fprintf(FILE *stream, const char *format, ...)

int printf(const char *format, ...)

int sprintf(char *str, const char *format, ...)

fprintf() : 정해진 파일 스트림에 문자열을 포맷하여 출력 printf() : 표준 출력(stdin)에 문자열을 포맷하여 출력 sprintf() : 특정 문자열에 문자열을 포맷하여 출력

  • printf()의 포맷팅

    %[플래그][너비][.숫자 정밀도 | .문자열 최소/최대 출력 개수][길이]서식 지정자 ex.

      float f = 10.5123f;
      printf("%03.2f", f);
    
  • sprintf()의 위험성 sprintf()는 특정 문자열에 출력을 하는 방식이기 때문에 특정 문자열의 길이가 모자르게 된다면, 버퍼 오버플로우가 일어날 수 있다.

기타 출력함수로 마지막에 개행문자를 추가로 출력해주는 puts, fputs가 있고 putchar와 같이 단어를 출력하는 함수가 있다.

입력

  • 한글자씩 읽기 int getchar(void)

    getchar()가 대표적으로 있고 그외로 getc()fgetc()가 있다.

    입력이 문자/문자열일 때 매우 유용하고 메모리에 입력 값을 저장하지 않는다.

    하지만 다른 데이터형의 연산이 복잡하다는 단점이 있다.

    • getchar()를 이용하여 한글 입력받기

    한글은 UTF-8 기준으로 3바이트를 차지한다. 따라서 아스키 코드가 아닌경우 한글로 인식하여 3바이트로 읽어야한다.

    아래는 단순히 한글만 입력했을 때를 가정하고 만든 코드이다.

      #include <stdio.h>
      int		main(void)
      {
          char	str_arr[4] = { 0, };
    
          char	test_hangul[] = "가";
          // UTF-8 "가" --> 3바이트, 문자열이니깐 '\0' + 1바이트 : 총 4바이트
          printf("size : %lu\n", sizeof(test_hangul)); // size : 4
          printf("values : %d %d %d %d\n", test_hangul[0], test_hangul[1], test_hangul[2], test_hangul[3]);
          printf("test : %s\n", test_hangul); // "test : 가" 가 출력됩니다.
    
          while (1)
          {
              printf("\nEnter hangul : ");
              while ((str_arr[0] = getchar()) == '\n'){}
              if (str_arr[0] == EOF)
              {
                  printf("Good Bye!!\n");
                  break ;
              }
              str_arr[1] = getchar();
              str_arr[2] = getchar();
              printf("\n--------------------------\n");
              printf("values : %X %X %X %X\n", str_arr[0], str_arr[1], str_arr[2], str_arr[3]);
              printf("answer : %s\n", str_arr);
              printf("--------------------------\n");
          }
      }
    
  • 한 줄씩 읽기 char* fgets(char* str, int num, FILE* stream)

    대표적으로 fgets()가 있다. fgets()는 num - 1 까지만 읽거나 '\n' 또는 EOF까지 읽는다.

    사용시에 항상 str 의 길이 이상만큼이 담겨지지 않도록 주의해야한다.

    *추가정보 : FILE 자료형 - 스트림을 제어하기 위해 필요한 정보를 담고 있는 자료형 다음과 같은 역할을 갖고있다.

    1. 파일의 위치를 표시
    2. 스트림이 사용하는 버퍼의 포인터
    3. 읽기/쓰기 중에 발생한 오류를 기록하는 표시자
    4. 파일의 끝에 도달했음을 기록하는 EOF 지시자

    *추가정보 : fgetsputs와 궁합이 맞지 않는다. 그 이유는 fget 자체적으로 '\n'까지 읽도록 설계가 되었는데 puts 함수는 '\n'을 추가로 출력하므로 개행문자가 하나가 더 출력이 될 수가 있다.

  • 한 데이터씩 읽기

    int scanf(const char *format, ...)

    int fscanf(FILE *stream, const char *format, ...)

    int sscanf(const char *str, const char *format, ...)

    scanf() : 표준 입력에서 문자열을 받아와 포맷팅한다.

    fscanf() : 지정한 파일 스트림에서 문자열을 받아온다.

    sscanf() : 문자열에서 포맷팅 방식으로 읽어온다.

    세개의 함수 모두 반환값으로 몇개의 데이터를 읽었는지를 받아오고 만약 실패했다면, EOF를 반환한다.

    scanf()의 포맷팅은 다음과 같다.

    %[*][너비][길이]서식지정자

    여기서 *를 사용하게되면 입력한 내용을 받기는 하지만 지나간다는 의미이다. '\n'을 무시하는 방법으로도 사용된다.

    scanf 를 사용할 때 문자열의 입력같은 경우 문자열에 받은 입력을 모두 넣으므로 버퍼 오버 플로우가 발생할 수 있다. 따라서 이것을 주의해야하고 fgets 를 통해 정해진 길이만큼을 받고 sscanf 를 사용하여 해당 문자열을 원하는 포맷팅으로 이용하여 사용하는 것이 좋다.

    아래는 fgetssscanf 를 사용한 예시 코드이다.

      #include <stdio.h>
    
      int		main(void)
      {
          char	buffer[20];
          int		num1;
          int		num2;
    
          while (1)
          {
              printf("Enter string : ");
              fgets(buffer, 20, stdin);
              sscanf(buffer, "hello %d %d", &num1, &num2);
              printf("answer : %s\n", buffer);
              printf("answer : %d %d\n", num1, num2);
          }
          return (0);
      }
    

    *추가정보 : clearerr() 를 통해 EOF 표시자를 리셋 할 수 있다.

  • 한 블록씩 읽기

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

    파일 스트림을 이용한다. 한꺼번에 읽기 때문에 성능적으로 더 효율적이고 자료형의 바이트 크기가 시스템마다 다르기 때문에 주의해야한다.

    다음과 같은 순서로 파일 스트림을 다룬다.

    1. 파일을 열어서 파일 스트림을 가져온다.

      FILE *fopen(const char *filename, const char *mode) 를 통하여 파일 스트림을 가져온다.

    2. 파일 스트림으로 하고 싶은 작업을 진행한다.

      fread(), fwrite(), fgets() ...

    3. 파일 스트림을 닫는다.

      int fclose(FILE *stream)

      성공하면 0, 실패하면 EOF 를 반환한다.

    *추가정보: 42에서 read 또는 write 를 사용할 때 성능을 고려한다면 버퍼의 크기를 크게하여 한번에 read 또는 write 를 사용하는것이 빠르다.

  • 에러 처리

    에러는 stdinstdout과 같은 stderr 라는 스트림을 사용한다,

    stdout 과 매우 유사하지만 stderr 는 버퍼링을 사용하지 않는다.

    따라서 stdoutstderr 를 혼용해서 사용하게된다면 stderr 버퍼에 출력하기 이전에 stdout 버퍼를 비우고 출력을 해야 콘솔 창에서 둘의 값이 섞이지 않는다.

    *추가정보: errno 라는 전역 변수를 사용하여 어떤 이유로 실패했는지 숫자로 나타낸 값을 알 수 있다. <errno.h> 에 정의되어 있으며 strerr(errno) 를 통하여 어떤 이유로 실패했는지에 대한 숫자 값을 문자열로 알려준다. perror() 라는 함수를 통해 인자로 받은 문자열과 함께 오류 메시지를 출력해볼 수도 있다.

⤧  Next post Oh-My-C-Lang - Section 7 - 06 ⤧  Previous post Oh-My-C-Lang - Section 5 - 04