C - file handling and others

innocentzero

2026-03-14

C - file handling

The day is about to end, and here I am, grinding C

File handling

FILE * is a pointer to a file in C. fprintf and fscanf take the first arguments as the file pointer and the rest is the same as printf and scanf.

To open a file, use fopen("file_path", "mode"). Mode can be r or w (for read or write).

Note

fgetc returns an int. This is because EOF doesn't fit in char.

fscanf and fprintf take the file pointer as the first argument. fputc, fputs, fgetc and fgets take them as the last argument.

Binary files

Use fread and fwrite to read and write from files. While writing structs and stuff, serialize your data because of endianness. Append b after the mode to indicate binary data.

fread returns the number of bytes read so useful to check if something has ben read or not.

typedef

Basically creates an alias for an existing type. Scoped. Useful for structs and arrays and pointers.

//  Anonymous struct! It has no name!
//         |
//         v
//      |----|
typedef struct {
    char *name;
    int leg_count, speed;
} animal;                         // <-- new name

//struct animal y;  // ERROR: this no longer works--no such struct!
animal z;           // This works because "animal" is an alias
typedef int *intptr;

int a = 10;
intptr x = &a, y = &a;  // "intptr" is type "int*"
// Make type five_ints an array of 5 ints
typedef int five_ints[5];

five_ints x = {11, 22, 33, 44, 55};

Manual Memory management

Allocate on heap manually, free manually.

malloc()

int *p = malloc(sizeof(*p)) is a common method to allocate memory. It returns NULL if memory can't be allocated so it is a good safety check.

int *x;

if ((x = malloc(sizeof(int) * 10)) == NULL)
    printf("Error allocating 10 ints\n");
    // do something here to handle it
}

Array allocation

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    // Allocate space for 10 ints
    int *p = malloc(sizeof(int) * 10);

    // Assign them values 0-45:
    for (int i = 0; i < 10; i++)
        p[i] = i * 5;

    // Print all values 0, 5, 10, 15, ..., 40, 45
    for (int i = 0; i < 10; i++)
        printf("%d\n", p[i]);

    // Free the space
    free(p);
}

calloc()

Similar to malloc, though it has slightly higher overhead than malloc(). Also returns NULL when nothing can be returned. First argument takes the number of elements to store in memory, second one takes the size of elements.

realloc()

Extend or shorten the existing ptr. Returns the new pointer.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  // Allocate space for 20 floats
  float *p = malloc(sizeof *p * 20); // sizeof *p same as sizeof(float)

  // Assign them fractional values 0.0-1.0:
  for (int i = 0; i < 20; i++)
    p[i] = i / 20.0;

  {
    // But wait! Let's actually make this an array of 40 elements
    float *new_p = realloc(p, sizeof *p * 40);

    // Check to see if we successfully reallocated
    if (new_p == NULL) {
      printf("Error reallocing\n");
      return 1;
    }

    // If we did, we can just reassign p
    p = new_p;
  }
  // And assign the new elements values in the range 1.0-2.0
  for (int i = 20; i < 40; i++)
    p[i] = 1.0 + (i - 20) / 20.0;

  // Print all values 0.0-2.0 in the 40 elements:
  for (int i = 0; i < 40; i++)
    printf("%f\n", p[i]);

  // Free the space
  free(p);
}

Here is a really good example to read a line of arbitrary length with realloc()

#include <stdio.h>
#include <stdlib.h>

// Read a line of arbitrary size from a file
//
// Returns a pointer to the line.
// Returns NULL on EOF or error.
//
// It's up to the caller to free() this pointer when done with it.
//
// Note that this strips the newline from the result. If you need
// it in there, probably best to switch this to a do-while.

char *readline(FILE *fp)
{
    int offset = 0;   // Index next char goes in the buffer
    int bufsize = 4;  // Preferably power of 2 initial size
    char *buf;        // The buffer
    int c;            // The character we've read in

    buf = malloc(bufsize);  // Allocate initial buffer

    if (buf == NULL)   // Error check
        return NULL;

    // Main loop--read until newline or EOF
    while (c = fgetc(fp), c != '\n' && c != EOF) {

        // Check if we're out of room in the buffer accounting
        // for the extra byte for the NUL terminator
        if (offset == bufsize - 1) {  // -1 for the NUL terminator
            bufsize *= 2;  // 2x the space

            char *new_buf = realloc(buf, bufsize);

            if (new_buf == NULL) {
                free(buf);   // On error, free and bail
                return NULL;
            }

            buf = new_buf;  // Successful realloc
        }

        buf[offset++] = c;  // Add the byte onto the buffer
    }

    // We hit newline or EOF...

    // If at EOF and we read no bytes, free the buffer and
    // return NULL to indicate we're at EOF:
    if (c == EOF && offset == 0) {
        free(buf);
        return NULL;
    }

    // Shrink to fit
    if (offset < bufsize - 1) {  // If we're short of the end
        char *new_buf = realloc(buf, offset + 1); // +1 for NUL terminator

        // If successful, point buf to new_buf;
        // otherwise we'll just leave buf where it is
        if (new_buf != NULL)
            buf = new_buf;
    }

    // Add the NUL terminator
    buf[offset] = '\0';

    return buf;
}

int main(void)
{
    FILE *fp = fopen("foo.txt", "r");

    char *line;

    while ((line = readline(fp)) != NULL) {
        printf("%s\n", line);
        free(line);
    }

    fclose(fp);
}

Variadic functions

va_arg is a macro.

#include <stdio.h>
#include <stdarg.h>

int add(int count, ...)
{
    int total = 0;
    va_list va;

    va_start(va, count);   // Start with arguments after "count"

    for (int i = 0; i < count; i++) {
        int n = va_arg(va, int);   // Get the next int

        total += n;
    }

    va_end(va);  // All done

    return total;
}

int main(void)
{
    printf("%d\n", add(4, 6, 2, -4, 17));  // 6 + 2 - 4 + 17 = 21
    printf("%d\n", add(2, 22, 44));        // 22 + 44 = 66
}