IT공부/윤성우 열혈 C프로그래밍

C언어의 깊은 이해_3

doublehyun 2022. 2. 10. 15:37

Chapter.4 파일 입출력

  • 파일과 스트림(Stream), 그리고 기본적인 파일의 입출력
    • 파일에 저장되어 있는 데이터 읽기
      • 파일에 저장되어 있는 데이터를 참조하기 위해서는 파일과 프로그램 사이의 통로를 만들어야 한다
      • 이때 프로그램과 파일을 잇는 통로 역활을 하는것이 Stream 스트림 이다
      • 스트림의 진행방향은 꼭 한 방향이다
             
      • 이 스트림의 형성은 운영체제의 몫이고 우리는 스트림 생성을 요구하면 된다
    • fopen함수 호출을 통한 파일과의 스트림 형성과 FILE 구조체
      • fopen 함수는 스트림을 형성(요구) 할 때 호출하는 함수이다
        • FILE * fopen(const char* filename, const char * mode); // 성공시 FILE 구조체의 변수 주소 값, 실패시 NULL
        • 첫 인자는 스트림을 형성할 파일의 이름, 두번째 인자로는 스트림 종류에 대한 정보
      • fopen 함수 호출 -> FILE 구조체 변수 형성 -> 구조체 변수에 파일에 대한 정보가 담긴다 즉 파일을 가리키는 지시자 역활
    • 입력 스트림과 출력 스트림의 생성
      • 출력 스트림의 형성을 요구하는 fopen / FILE * fp = fopen("data.txt", "wt"); // 파일 data.txt 과 wt모드로 스트림 형성
      • 입력 스트림의 형성을 요구하는 fopen / FILE * fp = fopen("data.txt", "rt"); // 파일 data.txt 과 rt모드로 스트림 형성
        • cf) 파일과 스트림이 형성된 경우를 파일이 열렸다(개방, 오픈) 이라고도 표현한다
    • 파일에 데이터 입력하기
      1. 출력 모드로 파일 열기 / FILE * fp = fopen("data.txt", "wt"); // 이때 파일에 경로 지정 가능!
      2. 파일에 데이터 입력하기 / fput('A', fp); // fput의 두번째 인자인 stream 에 파일 구조체 포인터를 입력해 준다
      3. 스트림의 연결을 종료해 주기 / fclose(fp);
    • 스트림의 소멸을 요청하는 fclose 함수
      • int fclose(FILE * stream); // 성공시 0, 실패시 EOF 반환
      • 파일을 닫아주는 이유
        1. 운영체제가 할당한 자원의 반환
        2. 버퍼링 되었던 데이터의 출력
      • 이때 출력버퍼의 데이터를 비우기 위해선 fflush 함수를 사용해도 된다 
      • 입력 버퍼의 경우 데이터를 비우면 소멸되기 때문애 fflush 함수를 사용할 수 없다!!!
    • 파일로부터 데이터 입력받기
      1. 입력 모드로 파일 열기 / FILE * fp = fopen("data.txt", "rt"); 
      2. 파일에서 데이터 입력받기 / int ch = fgetc(fp);
      3. 스트림의 연결을 종료해 주기 / fclose(fp);
  • 파일의 개방 모드(Mode)
    • 스트림을 구분하는 기준1: 읽기위해? or 쓰기위해?
           
    • 스트림을 구분하는 기준2: 텍스트 모드와 바이너리 모드 
      • 텍스트 파일과 바이너리 파일의 구분
        1. 텍스트 파일 : 문자 데이터 ex) 도서목록, 물품내역
        2. 바이너리 파일 : 컴퓨터가 인식하는 데이터 ex) 영상파일, 음원파일
      • 개행문자 \n 에 관한 부가설명
        • C언어에서는 \n을 개행문자로 인식하지만 모든 컴퓨터 환경이 그렇진 않다
          • ex) Winodw = \r\n, MAC = \r, Unix 계열 = \n
          • 이때 텍스트 모드로 파일을 열면 자동으로 변환해준다
      • 텍스트 모드, 바이너리 모드의 구분
        모드(mode) 스트림의 성격
        b 바이너리 모드
        t 텍스트 모드
        ' '(선언X) 텍스트 모드
  • 파일 입출력 함수의 기본
    • 전에 학습한 파일 입출력 함수를 이용한 파일 입출력
      1. int fputc(int c, FILE * stream) // 문자 출력
      2. int fgetc(FILE * stream) // 문자 입력
      3. int fputs(const char * s, FILE * stream) // 문자열 출력
      4. char * fgets(char * s, int n, FILE * stream) // 문자열 입력
        • cf) 문자열을 파일에 저장할 때는 널문자로 구분 X, 개행문자(\n)으로 문자열을 구분하게 된다
        • 따라서 문자열을 읽어들이는 함수를 호출하면 개행문자(\n)을 만날때 까지 게속 읽어들인다
    • feof함수 기반의 파일복사 프로그램
      • 파일의 마지막을 확인하는 feof함수
        • int feof(FILE * stream); // 파일의 끝에 도달하면 0, 아닐경우 다른 값 반환
    • 바이너리 데이터의 입출력: fread, fwrite
      • size_t fread(void * buffer, size_t size, size_t count, FILE * stream); // 성공시 전달인자 count, 실패시 작은값
        • ex) int buf[12];, fread((void*)buf, sizeof(int), 12, fp); // 성공시 12반환, 실패시 12보다 작은 값 반환
        • sizeof(int)크기의 데이터 12개를 fp로부터 읽어들여 배열 buf에 저장 
      • size_t fwrite(void * buffer, size_t size, size_t count, FILE * stream); // 성공시 전달인자 count, 실패시 작은값
        • ex) int buf[7] = { ~ };, fwrite((void*)buf, sizeof(int), 7, fp); // 성공시 7반환, 실패시 7보다 작은 값 반환
        • sizeof(int)크기의 데이터 7개를 buf로부터 읽어들여 fp에 저장
  • 텍스트 데이터와 바이너리 데이터를 동시에 입출력 하기
    • 서식에 따른 데이터 입출력: fprintf, fscanf
      • printf, scanf 함수와 동일하게 사용한다
      • fprintf(fp, "%s %c %d", name, sex, age); // name문자열, sex문자, age바이너리 데이터를 파일에 출력함
      • fscanf(fp, "%s %c %d", name, &sex, &age); // name문자열, sex문자, age바이너리 데이터를 파일에서 읽어들임
    • 텍스트와 바이너리 데이터의 집합체인 구조체 변수의 입출력
      • 구조체로 묶여있는 여러개의 데이터들을 하나의 바이너리 데이터로 인식시켜 입출력 시킬 수 있다
      • fread, fwrite 함수 사용
  • 임의 점급을 위한 '파일 위치 지시자'의 이동
    • 파일 위치 지시자란?
      • FILE 구조체의 맴버 중 하나로, 파일의 위치 정보를 저장하고 있다
      • fgets, fputs 를 사용하면 다음 문자열을 입출력 할 수 있게 파일 내의 위치를 참조하고 있다
    • 파일 위치 지시자의 이동: fseek
      • 파일 지시자를 이동시킬 때는 fseek 함수를 호출한다
      • int fseek(FILE * stream, long offset, int wherefrom); // 성공시 0, 실패시 0이 아닌 값 반환
        • stream으로 전달된 파일 위치 지시자를 wherefrom 으로부터 offset 바이트 만큼 이동시켜라!
               
      • 현재 파일 위치 지시자의 위치는?: ftell
        • 현재의 파일 위치 지시자의 정보를 확인하고 싶다면 ftell 함수를 호출하면 된다
        • long ftell(FILE * stream) // 파일 위치 지시자의 위치 정보 반환
          • 첫번째 바이트를 가리킬 경우 0을 반환하고, 세번째 바이트를 가리킬 경우 2를 반환한다.

Chapter.5 메모리 관리와 메모리의 동적할당

  • C언어의 메모리 구조
    • 메모리의 구성
         
    1. 코드영역(Code Area) : 실행할 프로그램의 코드가 저장되는 메모리 공간
    2. 데이터 영역(Data Area) : 전역변후와 static으로 선언되는 static 변수가 할당되는 공간(프로그램 종료시 까지 유지)
    3. 힙 영역(Heap Area) : 프로그래머가 원하는 시점에 할당하여 사용하고, 반환할 수 있는 영역
    4. 스택 영역(Stack Area) : 지역변수와 매개변수가 할당되는 영역, 함수를 빠져나가면 소멸하게 된다!
  • 메모리의 동적 할당
    • 전역변수와 지역변수로 해결이 되지 않는 상황
      • 문자열 배열을 함수로 반환하는 상황 -> (함수내에서 선언된 배열은 지역변수 이므로 함수가 끝나서 반환한 후에 사라진다 이때 반환된 값은 주소 값이기 때문에 주소 값이 가리키는 곳에는 아무것도 없는 상태가 된다)
      • 배열을 전역변수로 선언했을 경우 -> 하나의 배열로 여러 값을 입력받을 수 없게 된다, 여려개의 전역변수를 할당하면 데이터 영역에서 프로그램에 부하를 주게 된다
      • Solution = 생성과 소멸의 시기가 지역변수나 전역변수와 다른 유형의 변수를 선언해 준다!
    • 힙 영역의 메모리 공간 할당과 해제: malloc 과 free 함수
      • void *  malloc(size_t size); // 힙 영역으로의 메모리 공간 할당 / -> 메모리 공간의 첫번째 바이트 주소 값 반환
      • void free(void * ptr) // 힙 영역에 할당된 메모리 공간 해제
        • malloc 함수는 성공시 할당된 메모리의 주소 값, 실패시 NULL 반환
      • 이 주소 값을 이용하여 데이터에 접근하면 된다 즉 포인터 연산으로 값에 접근(연산) 한다!
    • molloc 함수의 반환형이 void*(보이드 형 포인터)인 이유와 힙 영역의 접근
      • molloc함수의 인자로는 숫자(Byte) 만 주기 때문에 포인터 형에 대한 결정을 스스로 할 수 없다
      • 따라서 형변환을 사용하여 직접 포인터 형을 맞추어서 선언해 주어야 한다
        • ex) int * ptr = (int*)malloc(sizeof(int)*7) // int형 포인터로 28byte 동적할당
    • free 함수를 사용하지 않을 시 발행하는 문제점
      • 결국 프로그램을 종료하면 힙 영역에 있던 변수들은 전부 헤제가 된다
      • 그러나 프로그램의 효율적인 메모리 활용을 위해 사용해 주어야 하는것이 좋다;;
    • malloc 함수와 비슷한 colloc 함수
      • void * calloc(size_t elt_count, size_t elt_size); // 성공시 할당된 메모리 주소 값, 실패시 NULL
        • malloc과 기능은 동일하지만 인자의 개수가 다르다, 초기화 차이가 있다
        • calloc 은 elt_size(몇 바이트 크기의 블록) elt_count(개수) 를 힙 영역에 할당해 달라는 뜻
          • ex) colloc(30, sizeof(int)); = malloc(120);
          • malloc은 메모리 공간을 초기화 하지 않지만 colloc은 메모리 공간의 비트를 0으로 초기화 한다
    • 힙에 할당된 메모리 공간 확장 시 호출하는 realloc 함수
      • 원래라면 한번 할당된 공간은 확장할 수 없다
      • 그러나 힙 영역에 할당된 공간은 늘릴 수 있다
      • void * realloc(void * ptr, size_t size); // 성공 시 새로 할당된 메모리의 주소 값, 실패시 NULL
        • int * arr = (int*)malloc(sizeof(int)*3);
        • arr = (int*)realloc(arr, sizeof(int)*7);
        • 이때 메모리 공간 상태에 따라 실행 결과는 2가지로 나뉜다
          1. 기존 메모리를 할당한 영역 뒤에 확장할 영역이 넉넉한 경우
            • 전 molloc 함수의 반환 주소 값과 realloc 함수의 반환 주소 값이 동일하다
          2. 기존 메모리 영역 뒤 확장할 영역이 없는 경우
            • 전 molloc 함수의 반환 주소값과 다른 새로운 주소 값이 반환된다(새로운 곳에 공간을 새로 할당한다)