Question

How to Get a File's Size in C

cfilefilesize

Question

I am writing a C program that opens a file, and I need to determine the file's size.

I want to know the size because I plan to read the file's contents into a dynamically allocated character buffer using malloc().

Using a fixed allocation such as the following seems like a poor approach:

char *buffer = malloc(10000 * sizeof(char));

How can I correctly find the size of a file in C before allocating memory for its contents?

Short Answer

By the end of this page, you will understand how to determine a file's size in C, why that matters before calling malloc(), and how to safely read a whole file into memory. You will also see common mistakes, practical patterns, and a complete mini project you can reuse.

Concept

In C, file size is often needed when you want to read an entire file into memory at once. A common beginner pattern is:

  1. Open the file
  2. Find its size
  3. Allocate enough memory with malloc()
  4. Read the file into the buffer
  5. Add a null terminator if you want to treat it as a C string

A common way to get the size of a regular file is to move the file position to the end using fseek(), then ask for the current position with ftell(). That position is typically the file size in bytes.

Basic idea:

fseek(file, 0, SEEK_END);
long size = ftell(file);
rewind(file);

Why this matters:

  • It helps you allocate the correct amount of memory.
  • It avoids hardcoded buffer sizes.
  • It reduces the risk of buffer overflows or wasted memory.
  • It makes file-reading code more reliable.

Important caveat: this approach works well for regular files, but not all streams behave the same way. For example, pipes, terminals, or some special devices may not support seeking. For normal disk files, it is a standard beginner-friendly solution.

Mental Model

Think of a file like a book with a bookmark.

  • fseek() moves the bookmark.
  • ftell() tells you where the bookmark currently is.
  • If you move the bookmark to the end of the book, the bookmark's position tells you how many bytes are in the file.
  • Then you move the bookmark back to the beginning and start reading.

So the process is:

  • Jump to the end
  • Check the position
  • Jump back to the start
  • Read exactly that many bytes
Take Quiz

Syntax and Examples

The classic pattern in C uses fopen(), fseek(), ftell(), and rewind().

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

int main(void) {
    FILE *file = fopen("example.txt", "rb");
    if (file == NULL) {
        perror("fopen");
        return 1;
    }

    if (fseek(file, 0, SEEK_END) != 0) {
        perror("fseek");
        fclose(file);
        return 1;
    }

    long size = ftell(file);
    if (size < 0) {
        perror("ftell");
        fclose(file);
        return 1;
    }

    rewind(file);

    char *buffer = malloc((size_t)size + 1);
    if (buffer == NULL) {
        perror("malloc");
        fclose(file);
        return 1;
    }

    size_t bytes_read = fread(buffer, 1, (size_t)size, file);
    if (bytes_read != (size_t)size) {
        perror("fread");
        free(buffer);
        fclose(file);
        return 1;
    }

    buffer[size] = '\0';

    printf("File size: %ld bytes\n", size);
    printf("Content:\n%s\n", buffer);

    free(buffer);
    fclose(file);
    return 0;
}

What this example does

  • Opens the file in binary mode with "rb"
  • Moves to the end of the file
  • Uses ftell() to get the size
  • Returns to the beginning with rewind()
  • Allocates size + 1 bytes so the content can be null-terminated
  • Reads the whole file into memory
  • Prints the size and contents

Why + 1?

If you want to treat the file content as a C string, you need one extra byte for \0.

Why use "rb"?

Binary mode avoids text translation on some systems. That makes byte counts more predictable when you care about exact file size.

Step by Step Execution

Consider this file content:

Hi

That file contains 2 bytes if it is exactly H and i, or 3 bytes if it includes a newline. Suppose the file contains Hi only.

Now trace this code:

FILE *file = fopen("example.txt", "rb");
fseek(file, 0, SEEK_END);
long size = ftell(file);
rewind(file);
char *buffer = malloc((size_t)size + 1);
size_t bytes_read = fread(buffer, 1, (size_t)size, file);
buffer[size] = '\0';

Step by step:

  1. fopen("example.txt", "rb")

    • Opens the file for reading.
    • The file position starts at the beginning.
  2. fseek(file, 0, SEEK_END)

    • Moves the file position to the end.
  3. long size = ftell(file)

    • Gets the current position.
    • If the file has 2 bytes, size becomes 2.
  4. rewind(file)

    • Moves the file position back to the start.
  5. malloc((size_t)size + 1)

    • Allocates 3 bytes: 2 for file data and 1 for \0.
  6. fread(buffer, 1, (size_t)size, file)

    • Reads 2 bytes into buffer.
    • buffer[0] = 'H'
    • buffer[1] = 'i'
  7. buffer[size] = '\0'

    • buffer[2] = '\0'
    • Now buffer is a valid C string: "Hi"

This is the key pattern for reading a whole regular file into memory safely.

Real World Use Cases

Getting a file's size is useful in many practical programs:

  • Loading configuration files

    • Read a JSON, text, or INI file into memory before parsing it.
  • Reading templates or static assets

    • Server-side programs may load HTML templates or email bodies.
  • Processing small data files

    • Import CSV or log files for parsing.
  • Copying file contents

    • Allocate a buffer large enough for the whole file.
  • Testing and tooling

    • Read fixture files in unit tests.
  • Binary file handling

    • Load images, custom formats, or serialized data where exact byte count matters.

This pattern is most appropriate when the file is small enough to fit comfortably in memory. For very large files, developers usually read in chunks instead.

Take Quiz

Real Codebase Usage

In real projects, developers usually wrap this logic in a helper function instead of repeating it everywhere.

Common patterns include:

Guard clauses

Return early if something fails:

if (file == NULL) return NULL;
if (fseek(file, 0, SEEK_END) != 0) return NULL;

This keeps code flatter and easier to read.

Validation before allocation

Check that ftell() did not fail before converting to size_t:

long size = ftell(file);
if (size < 0) {
    fclose(file);
    return NULL;
}

Reading whole files into a reusable utility

Many codebases use a helper like:

char *read_file(const char *path, long *out_size);

That helper can:

  • open the file
  • get the size
  • allocate memory
  • read contents
  • return both buffer and size

Error handling

Real programs must handle:

  • file not found
  • permission denied
  • allocation failure
  • short reads
  • non-seekable input

Chunked reading for large inputs

Even if file-size lookup is available, production code may prefer chunked reading to avoid large allocations or to support streams.

Common Mistakes

Here are common beginner mistakes when working with file sizes in C.

1. Forgetting to go back to the start before reading

Broken code:

fseek(file, 0, SEEK_END);
long size = ftell(file);
char *buffer = malloc((size_t)size + 1);
fread(buffer, 1, (size_t)size, file);

Problem:

  • The file position is still at the end.
  • fread() reads zero bytes.

Fix:

rewind(file);

2. Not checking whether ftell() failed

Broken code:

long size = ftell(file);
char *buffer = malloc((size_t)size + 1);

Problem:

  • ftell() returns -1L on error.
  • Converting that to size_t can create a huge number.

Fix:

if (size < 0) {
    // handle error
}

3. Forgetting space for the null terminator

Broken code:

char *buffer = malloc((size_t)size);
buffer[size] = '\0';

Problem:

  • Writing buffer[size] goes past the allocated memory.

Fix:

char *buffer = malloc((size_t)size + 1);

4. Assuming every file can be treated as text

If the file is binary data, adding \0 does not make it meaningful as a string.

Use the size separately:

void *buffer = malloc((size_t)size);

5. Ignoring the return value of fread()

Broken code:

fread(buffer, 1, (size_t)size, file);

Problem:

  • Reads can fail or be shorter than expected.

Fix:

size_t bytes_read = fread(buffer, 1, (size_t)size, file);
if (bytes_read != (size_t)size) {
    // handle error
}

6. Using a fixed-size buffer for unknown file sizes

Broken code:

char *buffer = malloc(10000);

Problem:

  • Too small for large files
  • Wasteful for tiny files
  • Fragile and hard to maintain

Fix:

  • Measure the file first, or read in chunks.

Comparisons

ApproachHow it worksGood forLimitations
fseek() + ftell()Move to end, ask for current positionRegular files, simple programsNot suitable for all streams
Read in chunksRepeatedly call fread() into a smaller bufferLarge files, streams, unknown sizesSlightly more code
Fixed-size bufferAllocate a guessed amountQuick experiments onlyUnsafe and unreliable

rewind() vs fseek(file, 0, SEEK_SET)

FunctionPurposeNotes
rewind(file)Move back to the beginningSimple and readable
fseek(file, 0, SEEK_SET)Move to byte 0 from the startMore general-purpose

Text mode vs binary mode

ModeMeaningWhen to use
"r"Read text fileSimple text input when exact byte handling is not critical
"rb"Read binary fileSafer when you care about exact bytes or portability
Take Quiz

Cheat Sheet

FILE *file = fopen("example.txt", "rb");
if (file == NULL) {
    // handle error
}

if (fseek(file, 0, SEEK_END) != 0) {
    // handle error
}

long size = ftell(file);
if (size < 0) {
    // handle error
}

rewind(file);

char *buffer = malloc((size_t)size + 1);
if (buffer == NULL) {
    // handle error
}

size_t bytes_read = fread(buffer, 1, (size_t)size, file);
if (bytes_read != (size_t)size) {
    // handle error
}

buffer[size] = '\0';

Key rules

  • Use fseek(file, 0, SEEK_END) to move to the end.
  • Use ftell(file) to get the position in bytes.
  • Check if ftell() returned a negative value.
  • Use rewind(file) before reading.
  • Allocate size + 1 if you need a C string.
  • Check the return values of fopen(), fseek(), ftell(), malloc(), and fread().
  • Use chunked reading for large files or non-seekable input.

Edge cases

  • Empty file: size is 0
  • Binary file: content may contain \0 bytes
  • Non-seekable stream: fseek() may fail

FAQ

How do I get a file's size in C?

A common method is to use fseek(file, 0, SEEK_END), then call ftell(file), and finally return to the beginning with rewind(file).

Why should I not use a fixed malloc(10000) buffer?

Because the file may be larger than 10,000 bytes or much smaller. Fixed guesses are unreliable and can waste memory or cause bugs.

Do I need to add 1 when allocating memory for a file?

Yes, if you want to treat the data as a C string. The extra byte stores the null terminator \0.

What happens if ftell() returns -1?

That means an error occurred. You should not pass that value to malloc().

Can I use this method for binary files?

Yes. The size still represents the number of bytes. But binary data should not automatically be treated as text.

Does this work for every kind of input stream?

No. Some streams, such as pipes or terminals, may not support seeking.

Is sizeof(char) necessary in malloc()?

No. sizeof(char) is always 1, so malloc(size) is enough for a character buffer.

What should I do for very large files?

Read the file in chunks instead of loading the entire file into memory at once.

Take Quiz

Mini Project

Description

Build a small C program that reads an entire text file into memory and prints both its size and contents. This demonstrates the full workflow of opening a file, measuring it safely, allocating memory dynamically, reading data, and cleaning up resources properly.

Goal

Create a program that loads a file into a dynamically allocated buffer using the file's actual size.

Requirements

  • Open a file from a path provided in the code.
  • Determine the file size before allocation.
  • Allocate enough memory for the file contents plus a null terminator.
  • Read the entire file into memory.
  • Print the file size and contents, then free the memory and close the file.
Take Quiz

Keep learning

Related questions

View more