Подтвердить что ты не робот

Почему ostream:: write() требует 'const char_type * вместо' const void * в С++?

Функция fwrite() в C использует const void *restrict buffer в качестве первого аргумента, поэтому вы можете напрямую передать указатель на свой struct в качестве первого параметра.
http://en.cppreference.com/w/c/io/fwrite
например fwrite(&someStruct, sizeof(someStruct), 1, file);

Но в С++ для ostream::write() требуется const char_type*, что заставляет вас использовать reinterpret_cast. (В Visual Studio 2013 он const char*.)
http://en.cppreference.com/w/cpp/io/basic_ostream/write
например file.write(reinterpret_cast<char*>(&someStruct), sizeof(someStruct));

Почти во всех случаях двоичные данные, которые должны быть записаны в файлы, не являются массивом char, поэтому почему стандарт предпочитает стиль, который кажется более сложным?

P.S.
1. Фактически я использовал метод write() в ofstream с режимом ios::binary, но, согласно ссылке, он наследует ofstream. Поэтому я использую ostream::write() выше.
2. Если вы хотите напечатать поток символов, вы можете использовать operator<<(). Не метод write(), предназначенный для записи необработанных данных?
3. Если write() не способ записи двоичных данных, то каков способ сделать это в стандарте? (Хотя это может затруднить переносимость кода из-за различных стратегий выравнивания памяти на разных платформах)

4b9b3361

Ответ 1

Отображение этого как объекта C vs С++ вводит в заблуждение. С++ предоставляет std::fwrite(const void*, ...), как и C. Где С++ предпочитает быть более защитным, в частности версии std::iostream.

"Почти во всех случаях двоичные данные, которые должны быть записаны в файлы, не являются char array"

Это спорно. В С++ нет ничего необычного, чтобы добавить уровень косвенности в I/O, поэтому объекты передаются потоком или сериализуются в удобную и, возможно, переносимую (например, стандартизованную по стандарту, без или со стандартным дополнением структуры) - представление, затем десериализованное/анализируется при повторном чтении. Логика обычно локализована с участием отдельных объектов, так что объекту верхнего уровня не нужно знать детали расположения макетов своих членов. Сериализация и потоковая передача, как правило, считаются/буферизованы и т.д. На уровне байтов - лучше подходят для буферов символов, а read() и write() возвращают несколько символов, которые в настоящее время могут быть переданы, - снова на символе, а не на объекте уровень - так что не очень продуктивно притворяться иначе, иначе у вас будет беспорядок, возобновляющий частично успешные операции ввода-вывода.

Необработанные двоичные записи/чтения выполняются наивно, поскольку они не справляются с этими проблемами, поэтому, вероятно, хорошо, что использование этих функций выполняется немного сложнее, при этом reinterpret_cast<> является немного запахом кода/предупреждение.

Тем не менее, один неудачный аспект использования С++ для char* заключается в том, что он может побудить некоторых программистов сначала прочитать массив символов, а затем использовать неулокальные приведения для "переинтерпретации" данных "на лету" - например, int*, предназначенный для символьного буфера способом, который может быть неправильно выровнен.

Если вы хотите напечатать поток символов, вы можете использовать operator<<(). Не метод write(), предназначенный для записи необработанных данных?

Для печати потока символов с operator<<() является проблематичным, так как единственная релевантная перегрузка занимает const char* и ожидает буфер '\0'/NUL-terminated. Это делает его бесполезным, если вы хотите напечатать один или несколько NUL на выходе. Кроме того, при запуске с более длинным символьным буфером operator<< часто бывают неуклюжие, подробные и подверженные ошибкам, нуждающиеся в NUL, обмениваемые и обратные вокруг потоковой передачи, и иногда это может быть значительная проблема с производительностью и/или памятью, например. при написании некоторого - но не конца - длинного строкового литерала, в который вы не можете поменять NUL или когда буфер символов может быть прочитан из других потоков, которые не должны видеть NUL.

Предоставленная функция std::ostream::write(p, n) позволяет избежать этих проблем, позволяя точно указать, сколько вы хотите напечатать.

Ответ 2

char_type не точно char *, это параметр шаблона потока, который представляет тип символа потока:

template<typename _CharT, typename _Traits>
class basic_ostream : virtual public basic_ios<_CharT, _Traits>
{
public:
    // Types (inherited from basic_ios):
    typedef _CharT                  char_type;
    <...>

И std::ostream является всего лишь char экземпляром:

typedef basic_ostream<char> ostream;

Ответ 3

В C/С++ char - это тип данных для представления байта, поэтому char[] - это естественный тип данных для двоичных данных.

Ваш вопрос, я думаю, лучше направлен на то, что C/С++ не был разработан для того, чтобы иметь разные типы данных для "байтов" и "символов", а не при разработке библиотек потоков.

Ответ 4

Рэ,

С сайта cplusplus.com подпись ostream:: write:

ostream& write (const char* s, streamsize n);

Я только что проверил его на VS2013, вы можете легко писать:

std::ofstream outfile("new.txt", std::ofstream::binary);
char buffer[] = "This is a string";
outfile.write(buffer, strlen(buffer));