C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
84: File Positioning: fseek, ftell, rewind
Up until now, we've mostly treated files like a stream—you start at the beginning and read until you hit the end. But in the real world, especially when dealing with binary files or large databases, that's incredibly inefficient. You don't want to read 10,000 records just to get to the 10,001st. That's where file positioning comes in.
How do I jump to a specific spot in a file?
This is where fseek is your best friend. Think of it like a record player needle; you're telling C exactly where to drop the needle. The function takes three arguments: the file pointer, the offset (how many bytes to move), and the "origin" (where you're counting from).
I usually recommend using fixed-size structs when you need to seek, because it makes the math trivial. If every record is 64 bytes, the 5th record is simply at 4 * 64.
typedef struct {
int id;
char name[20];
int score;
} Player;
// Let's jump to the 3rd player in a binary file
FILE *fp = fopen("scores.dat", "rb");
if (fp) {
// SEEK_SET means "start from the beginning of the file"
fseek(fp, 2 * sizeof(Player), SEEK_SET);
Player p;
fread(&p, sizeof(Player), 1, fp);
printf("Player 3: %s - %d\n", p.name, p.score);
fclose(fp);
}
You have three options for the origin: SEEK_SET (the start), SEEK_CUR (where you are right now), and SEEK_END (the very end). If you use SEEK_END with a negative offset, you can actually jump backward from the end of the file.
How can I tell where I am, or find out how big a file is?
If you've been doing a lot of reading and writing and you've lost track of your position, ftell will give you the current byte offset from the start of the file. It returns a long, which represents the number of bytes from the beginning.
A common trick I use to find the total size of a file is to "teleport" to the end, ask ftell where it is, and then jump back to where I started. It's a bit of a dance, but it's the standard way to do it in C.
FILE *fp = fopen("data.bin", "rb");
fseek(fp, 0, SEEK_END); // Jump to the end
long fileSize = ftell(fp); // Get the current position (which is the size)
rewind(fp); // Go back to the start so we can actually read it
printf("The file is %ld bytes long.\n", fileSize);
fclose(fp);
Is rewind() just a shortcut for fseek()?
Essentially, yes. Calling rewind(fp) is functionally the same as calling fseek(fp, 0, SEEK_SET). However, there is one subtle difference: rewind also clears the error indicators for the stream.
If you've hit the end of the file (EOF), ftell and fseek might behave predictably, but some internal error flags remain set. rewind wipes the slate clean. I personally use rewind because it's cleaner to read and tells anyone looking at my code exactly what my intention is: "I'm starting over."
📋 Practical Task
Exercise: Binary Record Random-Access Reader
Create a program that manages a binary file of User records. Each User struct should contain an int id and a char username[32].
- First, write a small helper function to populate a file named
users.binwith 10 dummy records. - In your
mainfunction, prompt the user to enter a record index (0-9). - Use
fseekto jump directly to that specific record without reading the preceding ones. - Read the record and print the username.
- Finally, use
ftellto print the exact byte offset where the record was read.
There are no comments for now.