What is the use of zero offset in fseek() function with SEEK_CUR?

2.5k Views Asked by At
while (fread(&product, sizeof(Product), 1, file) == 1) {
        product.price *= 2.0;
        fseek(file, -sizeof(Product), SEEK_CUR);
        fwrite(&product, sizeof(Product), 1, file);
        fseek(file, 0, SEEK_CUR);
}

Using the code above, I tried to double the price of each product in the binary file, where each record is an instance of a Product struct. When I ran this code, it updated all the prices in the file correctly.

As far as I know, fread automatically moves the file pointer to the next record. Using the first fseek, the code moves the file pointer back to the beginning of the record. Then it updates it using fwrite. fwrite automatically moves the file pointer to the next record. In the while loop, fread should continue to read the next record, and so on.

It seems that fseek(file, 0, SEEK_CUR); is unnecessary here. However, if I remove it, the code enters an infinite loop. I cannot figure out why this happens.

I tried to observe the value of file pointer using ftell. But I did not see any change its value after fseek(file, 0, SEEK_CUR);.

2

There are 2 best solutions below

5
Andreas Wenzel On BEST ANSWER

When a stream is opened for both reading and writing, you are not allowed to directly switch between reading and writing. §7.21.5.3 ¶7 of the ISO C11 standard states the following:

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function or to a file positioning function (fseek, fsetpos, or rewind), and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

If you remove the line

fseek(file, 0, SEEK_CUR);

then your program violates this rule, which invokes undefined behavior. This means that anything can happen, which includes the possibility of your program getting stuck in an infinite loop.

2
supercat On

Some file systems which are rather slow to perform random seeks are designed to allow efficiently process read-modify-write operations on a sequence of records by using separate read- and write pointers. A program such as yours might be able to omit the fseek operations entirely provided the amount of data read matched the amount of data written, and might run much faster without the fseek() operations than it would run if they were included.

A C Standard Library implementation would need to keep track of whether the last operation performed on a file was a write or a read, so that a relative fseek() could operate relative to either the the current read position or the current write position, depending upon which kind of operation was performed last, but could allow programs written to exploit dual file pointers to reap the associated performance gains when targeting systems that use them.

Although the Standard could have categorized the behavior of doing a write and read without an intervening fseek() as Implementation-Defined, that would have been seen as implying that implementations should try to behave in a consistent documented fashion even when targeting platforms with corner-case behaviors that aren't always predictable.