Что происходит, когда определяемый пользователем тип приводится к char *?

Я наткнулся на урок, в котором мы использовали определяемый пользователем тип под названием «Person», который хранит имя человека в массиве символов и возраст в виде целого числа. Урок был по написанию и чтению двоичных файлов. Основная программа выглядит так:

int main()
{
Person anil("anil",24); //initialize with name and age

fstream file("person.bin", ios::binary | ios::in | ios::out | ios::trunc);

if (!file.is_open())
    cout << "Error while opening file.";
else
{
    file.write((char*)&anil, sizeof(Person));
    file.seekg(0); // go back to beginning

    Person anjali;  //declare new person object

    //first argument is memory block, second argument is byte size
    file.read((char*)&anjali, sizeof(Person));

    anil.whoAreYou();  //outputs the name and age
    anjali.whoAreYou();
}

return 0;
}

Я не понимаю, что происходит с этими строками:

file.write((char*)&anil, sizeof(Person));
file.read((char*)&anjali, sizeof(Person));

Я понимаю, что функции записи и чтения fstream требуют memory_block в качестве первого аргумента ... может ли кто-нибудь объяснить, что именно происходит, когда ссылка на определяемый пользователем тип приводится к char *?


person ajw170    schedule 03.12.2016    source источник
comment
Вероятно, это плохая идея - просто выгружать память на диск и думать, что у этого есть надежда на работу позже. Это может содержать указатели или структуры, подобные std::string, которые хранят данные в другом месте. Попробуйте написать правильный метод сериализации как operator<<, чтобы вы могли сделать file << anil.   -  person tadman    schedule 03.12.2016
comment
Урок был по написанию и чтению двоичных файлов. - Ага! Где преподается этот плохой урок? Я спрашиваю об этом, поскольку здесь должны быть тысячи сообщений, где автор пишет код именно так, и не может понять, почему его программа не работает. Я предположил, что кто-то, какая-то книга или какой-то веб-сайт передает эту информацию новичкам в программировании на C ++.   -  person PaulMcKenzie    schedule 03.12.2016
comment
У меня было ощущение, что это плохая практика.   -  person ajw170    schedule 03.12.2016


Ответы (2)


В C / C ++ char * часто используется как общий тип данных, поскольку он всегда указывает на 1 байт. Код преобразования (char*)&anil в вашем коде получает указатель на anil с & и преобразует его в указатель на chars. Он записывает эту последовательность char в файл, а затем считывает ее обратно в память для anjali.

Это делает точную копию памяти, что вызывает проблемы, если у вас есть указатель на что-то еще в этой памяти, поскольку этот указатель почти никогда не будет действителен при загрузке объекта обратно. Это может служить примером, но реальная сериализация довольно сложна, и ее часто лучше оставить какой-нибудь библиотеке сериализации. Возможна ручная сериализация, но это следует делать очень осторожно в библиотеке или подобном.

Если вы хотите выполнить эту сериализацию вручную, вам необходимо определить формат для сериализации и записать базовые значения в файл. Вам также нужно будет определить, действительно ли вам нужна двоичная сериализация или лучше подходит текстовая сериализация. В большинстве случаев запись объекта в формат файла, такой как JSON, немного проще, а также имеет преимущество переносимости между языками и удобочитаемости.

person Daniel Underwood    schedule 03.12.2016
comment
Я не знаю, может ли приведение к файлу и обратно, особенно с разных платформ, скомпилированных с разными компиляторами, вызвать проблемы с выравниванием. По крайней мере, выполните static_cast. - person Peregring-lk; 03.12.2016
comment
Да, это определенно могло быть так. Я даже не рассматривал проблемы с выравниванием / упорядочиванием, которые могли возникнуть с разными компиляторами. Я могу предположить, что такие проблемы могут возникнуть в зависимости от параметров компиляции даже на одной машине с одним и тем же компилятором. Но метод слепого преобразования в char * и записи в любом случае может вызвать проблемы даже в самых простых случаях. - person Daniel Underwood; 03.12.2016

std::fstream происходит от std::istream, который в основном является typedef basic_istream<T> с char as T.

Итак, fstream::read ожидает указатель char в качестве первого аргумента, а приведение преобразует начальный адрес anil в char*. Так как sizeof(char) == 1 в большинстве систем, необходимо побайтно копировать содержимое экземпляра Person по указанному адресу.

Это непереносимо и непросто в долгосрочной перспективе, особенно если у вас есть указатель в качестве переменных-членов в ваших классах, поскольку вы, по сути, сериализуете необработанные адреса памяти ваших переменных.

person Heliosophist    schedule 03.12.2016