3

I need to write data to a binary file using C's I/O functions. The following code causes a runtime exception :


#include "stdio.h"

int main(int argc,char* argv[]) {
    FILE *fp = fopen("path_to_file.bin","wb");
    if(fp == NULL) {
        printf("error creating file");
        return -1;
    }
    int val = 4;
    fwrite((const void*)val,sizeof(int),1,fp);
    fclose(fp);
    return 0;
}

The code dies at the fwrite. Can you spot what I'm doing wrong? Apparently, I'm, trying to access data at 0x0000004 or something like that.

Thanks !

Geo
  • 93,257
  • 117
  • 344
  • 520

6 Answers6

27

I think Neil's answer can be improved upon. I realize it's already accepted, so this is just to show some contrast (which is why I didn't just edit his).

fwrite(&val, sizeof val, 1, fp);

Two improvements:

  • No pointer casting, since it's not necessary in C and can hide errors.
  • Use sizeof directly on the object, since that is what you're passing a pointer to. Makes a lot of sense to me, and is safer than repeating yourself and using the type name.
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    +1 Good points, both. Moral of the story: prefer as little casts and use sizeof judiciously. – dirkgently Apr 21 '09 at 11:31
  • 1
    Can you please explain why the cast isn't necessary? I'm not a C programmer ... and I would like to understand as much as possible of it's "magic". – Geo Apr 21 '09 at 11:36
  • 7
    fwrite() expects a void* as its first argument. Any pointer type can be used where a void* is expected without casting. void* effectively says "I need a pointer, I don't care what type it points to". – Ferruccio Apr 21 '09 at 11:46
  • Not required in C. Especially with pointers. You can assign a pointer to void to a pointer to any object type and vice versa – dirkgently Apr 21 '09 at 18:58
20
 fwrite((const void*)val,sizeof(int),1,fp);

should be:

 fwrite((const void*) & val,sizeof(int),1,fp);

BTW, if you don't use the cast, you will get a sensible error message. Casts tend to be used by C (and C++) programmers far more often than they should be - a good rule of thumb is "if it needs a cast, it's probably wrong".

5

Adding to Neil's answer: this works when you are reading and writing the file on the same platform. Things can become weird if you are reading/writing across platforms with different endianness.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • Not only endianness, also the size may vary. – quinmars Apr 21 '09 at 11:55
  • C is soo not multi platform, in the file writing/reading you need to make a implementation for each platform most times, different end of line markers and things like that become a nightmare when dealing with file writing/reading across platforms (kindda off topic anyway :P) – Juan Techera Apr 21 '09 at 12:27
0

I also faced this kind of problem. So this is my solution.

fwrite(val, sizeof(val[0], sizeof(val)/sizeof(val[0]), fp);

Lasith Niroshan
  • 925
  • 10
  • 18
0

Apparently, I'm, trying to access data at 0x0000004 or something like that.

int val = 4

There's the issue. fwrite is designed to work with strings, and as such its first input is a pointer, the location of a string in memory. You are passing the value of val directly (4) rather than the address of val to fwrite; however, the memory at 0x00000004 is not valid program memory and thus an error is given.

To fix this, change this:

fwrite((const void*)val,sizeof(int),1,fp);

Into this:

fwrite((const void*)&val, sizeof(int), 1, fp);

The "&" operator indicates the location of val. This would be a valid address in memory.

adrian
  • 1,439
  • 1
  • 15
  • 23
0
#include "stdio.h"

int main(int argc,char* argv[]) {
    FILE *fp = fopen("path_to_file.bin","wb");
    if(fp == NULL) {
        printf("error creating file");
        return -1;
    }
    int val = 4;
    fwrite((const void*)val,sizeof(int),1,fp);

You should supply an address not integer itself.

Additionaly you should not use the integer in such way:

  1. It may differ in endianess on different computers (as mentioned)
  2. It may differ in size. On really old computers it may be 1 or 2 bytes. On most modern it will be 4 but it may be 8 as well (some 64-bit computers). On some strange architectures it may be even 36 bits. int32_t val = 4; fwrite((const void *)val, 4, 1, fp) should solve the problem.

You may think that your software will never need to be ported. Well - many designers (software and hardware) made similar assumptions. Sometimes it is too costly to not make them - but in this case it is just a matter of few additional checks.

    fclose(fp);
    return 0;
}
Maciej Piechotka
  • 7,028
  • 6
  • 39
  • 61