stdio: Standard I/O Library

 

  • Perfomance issues
    • Alignment
      • I/O performance is optimal when requests are issued on block-aligned boundaries in integer multiples of the block size
      • I/O의 성능은 OS가 관리하는 block사이즈의 배수로 관리되는 buffer의 유무에 달려있습니다. 즉 buffer의 시작주소나 길이가 block size의 양수배여야 성능이 좋아집니다.
    • Number of system calls
      • Performance degradation is exacerbated by the increased number of system calls required to read
        • A single byte 1,024 times rather than 1,024 bytes all at once
        • system call의 횟수가 매우 중요합니다.
  • User-buffered I/O
    • Buffering done in user space, either manually by the application, or transparently in a library
  • stdio
    • Provides a platform-independent, user-buffering solution

stdio

 

File Pointer (similar with file descriptor)

 

  • Inside the C library, the file pointer maps to a file descriptor
    • File pointer is represented by a pointer to the FILE typedef, which is defined in <stdio.h>

 

Opening Files

 

  • FILE *fopen(const char *path, const char *mode)
  • Opens the file path according to the given mod
  • mode
    • r: open the file for reading
    • r+: open the file for both reading and writing
    • w: open the file for writing
      • If the file exists, it is truncated to zero length; If the file does not exist, it is created
    • w+: open the file for both reading and writing
    • a: open the file for writing in append mode
      • The file is created if if does not exist
      • The stream is positioned at the EOF
    • a+: open the file both reading and writing in append mode
    • WHAT IS THE Difference between r+ and w+ in fopen()?
    • Return value
      • Upon success, a valid FILE pointer; in failure, NULL

 

 

Opening Streams via File Descriptor

 

  • FILE *fdopen(int fd, const char *mode)
  • Converts an already open file decriptor (fd) to a stream
  • mode
    • Must be compatible with the modes originally used to open the file descriptor
    • fd의 접근 mode와 fdopen에서의 mode가 반드시 같아야합니다.
  • Return value
    • Upon success, a valid FILE pointer; on failure, NULL

 

Reading from a Stream

 

  • int fgetc(FILE *stream)
    • Reads a single character from a stream
    • Returns a character as an unsigned char cast to an int
      • the return value must be stored in an int
        • Storing it in a char is a common but dangerous mistake
      • the casting is done to have a sufficient range for notification of EOF or error
      • int로 return을 받는 이유는 EOF나 error에 대한 처리를 위해서입니다. 진짜 입력을 받을때는 unsigned char타입으로 타입캐스팅을 해줘야합니다.
  • char *fgets(char *str, int size, FILE *stream)
    • Reads up to one less than size bytes from stream, and stores the results in str
    • size byte보다 1byte적게 읽어들입니다. 그 1byte는 \0을 위한 자리입니다 
      • A null character (\0) is stored in the buffer after the bytes read in
      • string을 buffer로부터 읽어들인 후에는 \0을 뒤에 붙여줘야합니다
      • Reading stops after an EOF of a newline character is reached
        • If a newline is read, the \n is stored in str
        • \n을 읽으면 읽어들이는 문자열에는 \n까지 들어가고 끝나기 때문에 또 마지막엔 \0을 붙여줘야합니다.
    • Return value
      • On success, str; on failure, NULL
  • size_t fread(void *buf, size_t size, size_t nr, FILE *stream)
    • Reads up to nr elements of data, each of size bytes, from stream into buffer pointed ay by buf
    • size크기의 data를 nr개 읽겠다는 의미로, 총 읽어들이는 길이는 size * nr입니다
    • Return value
      • The number of elements read (not the number of bytes read!) is returned
      • 읽어들인 byte수가 아닌 읽어들인 element의 수가 return됩니다.

 

Writing to a Stream

 

  • int fputc(int c, FILE *stream)
    • Writes the byte specified by c (cast to an unsigned char) to the stream pointed at by stream
    • fgetc와 마찬가지로 쓰려는 값은 integer type이어야합니다.
    • Return value
      • Upon successful, c; otherwise EOF
  • int fputs(const char *str, FILE *stream)
    • Writes a null-delimited string to a given stream
    • \0이 나올때까지 write하기 때문에 str에는 반드시 \0이 존재해야합니다.
    • Return value
      • On success, a nonnegative number; on failure, EOF
  • size_t fwrite(void *buf, size_t size, size_t nr, FILE *stream)
    • Writes to stream up to nr elements, each size bytes in length, from the data pointed at by buf
    • fread()와 마찬가지로 데이터의 크기가 size인 element를 nr개 작성하는 함수입니다.
    • Return value
      • On success, the number of elements (not the number of bytes!) successfully written will be returned
      • Return value less than nr denotes error
      • fread()같은 경우는 중간에 EOF같은 것을 만나면 중간에 read가 끊기니까 return value가 nr보다 작아도 정상적으로 수행이 된 것이지만, fwrite는 nr보다 작은 값이 return되었다면 error가 발생한 것으로 해석할 수 있습니다.

 

Flushing a Stream

 

  • int fflush(FILE *stream)
    • Writes the user buffer to the kernel, ensuring that all data written to a stream is flushed via write()
    • 사용자 수준의 buffer를 I/O에 write, read하라고 ensure하는 것입니다. 따라서 write의 경우는 OS에 write()을 호출하는 것이라서 OS 수준에서의 buffer에는 남아있을 수 있습니다. 즉 fsync()와 같은 함수까지 호출해야 OS 수준의 buffer에서도 flush가 됨을 보장합니다.
    • Return value
      • On success, 0; on failure, EOF

 

Seeking a Stream

 

  • int fseek(FILE *stream, long offset, int whence)
    • Manipulates the file position of stream in accordance with offset and whence
    • whence
      • SEEK_SET: the file position is set to offset
      • SEEK_CUR: the file position is set to the current position plus offset
      • SEEK_END: the file position is set to the end of the file plus offset
    • Return value
      • On success 0; on error -1
      • lseek과 달리 offset을 return하지 않습니다.
  • long ftell(FILE *stream)
    • Returns the current stream position of stream
    • On error, it return -1

 

Closing Streams

 

  • int fclose(FILE *stream)
    • On success, 0; on failure, EOF
  • int fcloseall(void)
    • Closes all streams associated with the current process, including stdin, stdout, and stderr
    • std fd를 포함하는 해당 프로세스가 사용하고 있는 모든 file들을 close합니다.

 

Example

int main()
{
    FILE *in, *out;
    struct univ {
        char name[100];
        unsigned int year;
    } u, my_univ = {"konkuk Univ.", 1931);
    
    out = fopen("data", "w");
    if(!out){
        perror("fopen");
        return 1;
    }
    
    if(!fwrite(&my_univ, sizeof(struct univ), 1, out)){
        perror("fwrite");
        return 1;
    }
    
    if(fclose(out)){
        perror("fclose");
        return 1;
    }
    
    in = fopen("data", "r");
    if(!in){
        perror("fopen");
        return 1;
    }
    
    if(!fread(&u, sizeof(struct univ), 1, in)){
        perror("fread");
        return 1;
    }
    
    if(fclose(out)){
        perror("fclose");
        return 1;
    }
    
    printf("name=\"%s\" year=%u\n", u.name, u.year);
    return 0;
}