1

I tried writing to a file and I am unable to write to it due to "Access violation reading location 0x0000000F" I am able to isolate the problem here is the sample code:

void test_1() {
    std::fstream fio{ "xyz.dat",std::ios::in | std::ios::out | std::ios::binary | std::ios::app };
    if (!fio) {
        std::cerr << "sorry no file";
        return;
    }
    std::string s_test{ "xyz hii \n workf" };
    fio.write( ( char* )(s_test.length()), sizeof( size_t ) );  //the write causing issue
}
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Amit kh
  • 59
  • 1
  • 9
  • 3
    `( char* )(s_test.length())` is treating the size as though it's a pointer. Sort of like `(char *) 10`. As soon as write tries to use the memory at 10, [The Bad](https://en.cppreference.com/w/cpp/language/ub) happens and the program probably goes Ka-BOOOM! Based on the error message, the length of the string is 15. – user4581301 Aug 07 '20 at 21:19
  • 2
    Why so many tagged versions of C++? – Thomas Matthews Aug 07 '20 at 21:22
  • https://stackoverflow.com/questions/49492259/writing-binary-data-to-fstream-in-c – Vlad Feinstein Aug 07 '20 at 23:50

2 Answers2

2

( char* )(s_test.length()) is treating the length of the string as though it's a pointer and passing address 15 into write. Since there is no valid character at address 15, this triggers Undefined Behaviour, and the behaviour in this case is the program crashes.

This is always a problem when forced to use such a wide cast to force re-interpretation of a type. You can screw up horribly and all the compiler's defenses have been turned off. I don't have a good solution for this.

You need to pass in a legitimate address containing the length for write to operate on. To get this, you'll need to create a variable you can take the address of. &s_test.length(); isn't good enough here because you cannot take the address of a prvalue returned by a function.

auto len = s_test.length();
fio.write( reinterpret_cast<const char*>(&len), sizeof( len ) );

Note that writing a variable of automatically deduced type or a variable of a type that can change between compiler implementations is risky. It's hard to be sure how many bytes you're going to need to read at the other side.

uint32_t len = x.length();

Would be safer, and probably more compact, but at the risk of overflow with strings greater than 4.4 billion characters in length. That's a risk I'm willing to put up with.

Another concern is endian. It's not as common a problem as it used to be, but both the writer and reader need to agree on the byte order of the integer. htonl and ntohl can help mitigate this threat by guaranteeing a byte order.

user4581301
  • 33,082
  • 7
  • 33
  • 54
1

Assuming that you are trying to write the length of the string to your output file, you can do it like this:

size_t len = s_test.length();
fio.write( ( const char * ) &len, sizeof( size_t ) );
Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • thanks and nicely pointed out, but I would like to add that using a cstdint would be better since it wont change its size across x64 compilers. I know I made that mistake in the first place but the other answer helped me understand. – Amit kh Aug 08 '20 at 07:15