2

Code

#include <iostream>
#include <fstream>

struct emp
        {
            char name[20];
            int age;
        };

int main()
{
    emp e1={"Abhishek", 22},e2;
    
    std::ofstream fout;
    fout.open("vicky.dat", std::ios::out | std::ios::binary);
    fout.write(reinterpret_cast<const char*>(&e1),24);
    fout.close();

    std::ifstream fin;
    fin.open("vicky.dat", std::ios::in | std::ios::binary);
    fin.read(reinterpret_cast<char*>(&e2),24);
    fin.close();

    std::cout<<e2.name<<" "<<e2.age<<"\n";

    return 0;
}

Why it is necessary to do reinterpret_cast with 1st argument of write and read function ?

why we casting address of type emp particularly to const char* and char* in write and read function respectively ?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Abhishek Mane
  • 619
  • 7
  • 20
  • Because that is what the function requires. – Retired Ninja Sep 02 '21 at 08:42
  • 2
    Because `write`/`read` take respectively `const char*`/`char*` as argument and you are telling compiler "I know `emp*` is not `char*`, but I want you to treat it like `char*`, I don't care about consequences". – Yksisarvinen Sep 02 '21 at 08:44
  • @Yksisarvinen got it. – Abhishek Mane Sep 02 '21 at 08:48
  • 1
    `char` is a misnomer in C++. A better name would be `byte`: these two functions (like all low-level IO functions) operate on *byte buffers*. – Konrad Rudolph Sep 02 '21 at 08:49
  • @KonradRudolph what is meant by *byte buffers* exactly ? – Abhishek Mane Sep 02 '21 at 08:58
  • @KonradRudolph except that `char` is not required to be 8-bits. – Richard Critten Sep 02 '21 at 09:07
  • @KonradRudolph sorry I am not getting can you tell in little bit brief please ? – Abhishek Mane Sep 02 '21 at 09:11
  • The data in memory (and in your binary file) representing an emp is made out of bytes. A sequence of bytes of a certain size is called a byte buffer. So anything a char* points to is a sequence of bytes. To be able to write an emp structure to the file, the write operation want a char* but you have an emp* and they are not the same thing. An emp is (hopefully) represented by 24 bytes in memory (you might want to check sizeof(emp)==24). And you basically have to say to write, use the address of emp as first the adress of your first byte (a char), which you do by static_casting. – Pepijn Kramer Sep 02 '21 at 09:20
  • 1
    @RichardCritten Neither is a byte (byte ≠ octet!). In C and C++, `char` = byte, by definition. Both in size and in (aliasing and addressing) behaviour. – Konrad Rudolph Sep 02 '21 at 09:40
  • @AbhishekMane Sorry, the explanation goes *way* beyond the scope of a comment. You’ll need to [find a book](https://stackoverflow.com/a/388282/1968/) that explains the fundamentals of memory in C++. – Konrad Rudolph Sep 02 '21 at 09:42

1 Answers1

2

Why it is necessary to do reinterpret_cast with 1st argument of write and read function ?

Because the first argument to write() is a const char* and the first argument to read() is a char*.

why we casting address of type emp particularly to const char* and char* in write and read function respectively ?

You can cast both to char* in your case - but, in the case of write, which doesn't need a mutable object since it's not going to modify it in any way, you would not be able to write a const object if you try to cast to char*.

Works:

const emp e1 = {"Abhishek", 22};
fout.write(reinterpret_cast<const char*>(&e1), sizeof e1);

emp e2 = {"Foo", 23};
fout.write(reinterpret_cast<char*>(&e2), sizeof e2);
fout.write(reinterpret_cast<const char*>(&e2), sizeof e2);

Doesn't even compile:

const emp e1 = {"Abhishek", 22};
fout.write(reinterpret_cast<char*>(&e1), sizeof e1);

Unrelated to the question but worth a note: You have hardcoded the size of emp to 24. Don't. Use sizeof to get the actual size. Even if an int is 4 bytes on your current platform, it may not be if you compile it on a different platform.

Related to the above: Use fixed width types (like uint32_t for a 4 byte unsigned integer) if you want the file format to work on different platforms and take care of endianness - but still use sizeof, since counting the sizes manually when you add/remove members is prone to lead to mistakes.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • `fout.write(reinterpret_cast(&e2), sizeof e2);` how it works ? – Abhishek Mane Sep 02 '21 at 10:49
  • 1
    @AbhishekMane A `char*` can be implicitly converted to a `const char*` (from non-`const` to `const` is considered safe) and `sizeof e2` gives the size of `e2` in bytes. – Ted Lyngmo Sep 02 '21 at 11:01
  • got it. and one thing `sizeof e2` it should be `sizeof(e2)` isn't it ? – Abhishek Mane Sep 02 '21 at 11:23
  • @AbhishekMane Great! ... and no, the [`sizeof` operator](https://en.cppreference.com/w/cpp/language/sizeof) has two forms. `sizeof(type)` and `sizeof expression`. Well, `sizeof (expression)` would work too but it'd still use the expression form. – Ted Lyngmo Sep 02 '21 at 11:35
  • @AbhishekMane You have `e1` and `e2` in your program. `sizeof e1` and `sizeof e2` should work if your compiler is ok. – Ted Lyngmo Sep 04 '21 at 06:49
  • @AbhishekMane You are using a _type_ (the first `sizeof` form). What I showed in my answer was the _expression_ form, like this: https://ideone.com/WNoHIp – Ted Lyngmo Sep 04 '21 at 07:32
  • 1
    Now got it. Thanks – Abhishek Mane Sep 04 '21 at 07:37