std::ifstream::read(char_type*, std::streamsize)
принимает количество читаемых байт с помощью знакового типа std::streamsize
, а std::fread(void*, std::size_t, std::size_t, std::FILE*)
-- с помощью беззнакового типа std::size_t
. При этом во многих примерах и там и там для количества байт используют sizeof
, который выдаёт результат в std::size_t
. Размер std::vector<char>
тоже беззнаковый. Так почему тогда эта функция принимает знаковый тип? Как правильно кастовать типы, чтобы без проблем заполнить вектор байтов?
Думаю, правильный ответ - так сложилось исторически. Вначале в си/с++ использовали активно int для всего (в старом си это вообще был "тип по умолчанию"). То, что какие то файлы будут больше 2 Гигабайт даже никто не думал ("это громадные размеры") - сейчас так думают о 2 в 64. Плюс, оказалась очень удобно использовать отрицательные числа как признак ошибки. Тот же read/recv возвращает положительное число, если что то прочитали, ноль - если файл/сокет был закрыт и отрицательное, если ошибка. Удобно же.
Но потом математики посмотрели на это и говорят "ну как размер массива может быть отрицательным?" Плюс это приводит к ошибкам, когда пытаемся сложить два размера массива, а один из них отрицательный (потому что это на самом деле было кол-во прочитанных байт с ошибкой). И решено было, что оптимально будет хранить в беззнаковом типе, а ошибку возвращать другим способом (например, исключением или кодом возврата). Но не все пошли этим путем, в java беззнаковых типов нет (или уже появлялись?).
Но даже в таком случае многие не хотели отказываться от привычной -1 для ошибок. В плюсах куча мест, где используется 0xFFF...FF для индикации именно ошибки, что иногда вызывает прикольные баги (попытка выделить такое кол-во памяти обычно ничем хорошим не заканчивается).
Потом умные дядьки с комитета стандартизации ещё раз посмотрели и решили, хватит притворятся и давайте добавим ssize - он такой же как size, но только знаковый. Там есть интересные вещи, которые объясняют, зачем это всё нужно.
Тип std :: streamsize
- это целочисленный тип со знаком, используемый для представления количества символов, переданных в операции ввода-вывода, или размера буфера ввода-вывода. Он используется как подписанный аналог std :: size_t
, аналогично типу POSIX ssize_t
.
За исключением конструкторов std :: strstreambuf
, отрицательные значения std :: streamsize
никогда не используются
В проекте стандарта C ++ есть следующая сноска 296 в разделе 27.5.2 Типы, в которой говорится:
streamsize
используется в большинстве мест, где ISO C будет использовать size_t
. В большинстве случаев streamsize
может использовать size_t
, за исключением конструкторов strstreambuf
, которые требуют отрицательных значений. Вероятно, это должен быть знаковый тип, соответствующий size_t
(это то, что Posix.2 называет ssize_t
).
И мы видим, что в разделе D.7.1.1 конструкторы strstreambuf
у нас есть следующие записи:
strstreambuf(char* gnext_arg, streamsize n, char *pbeg_arg = 0);
strstreambuf(signed char* gnext_arg, streamsize n,
signed char *pbeg_arg = 0);
strstreambuf(unsigned char* gnext_arg, streamsize n,
unsigned char *pbeg_arg = 0);
и говорится:
gnext_arg
должен указывать на первый элемент объекта массива, количество элементов N которого определяется следующим образом:
И из следующего обсуждения мы можем увидеть, что n, имеющее тип streamsize
, действительно требуется, чтобы иметь возможность принимать отрицательное значение:
Если n> 0, N равно n.
Если n == 0, N равно std :: strlen (gnext_arg)
.
Если n <0, N равно INT_MAX.
Как так вышло, что тип std::streamsize
является знаковым целочисленным типом, судить не стану, однако, замечу, что даже если бы он был беззнаковым, то основную вашу проблему — "правильное преобразование типов" — это бы никак не решило.
Стандарт языка НЕ гарантирует, что
std::size_t
представимы типом std::streamsize
,std::streamsize
представимы типом std::size_t
,std::vector<some_type>
представим типом std::streamsize
,std::streamsize
представимо типом std::vector<some_type>::difference_type
std::size_t
представимо типом std::vector<some_type>::size_type
.Таким образом, для полной уверенности, что при преобразовании значения одного типа в значение другого типа не произойдёт неожиданного усечения, следует перед преобразованием проверить, поместится ли преобразуемое значение в целевой тип.
Например, явно:
if ( char_vect.size() <= static_cast<std::uintmax_t>(std::numeric_limits<std::streamsize>::max()) )
fout.write(&char_vect[0], char_vect.size());
Или даже так:
static_assert( std::numeric_limits<std::vector<char>::difference_type>::max() <= std::numeric_limits<std::streamsize>::max() );
Максимальное количество элементов в векторе, между прочим, ограниченно сверху максимальным значением некоторого знакового целочисленного типа std::vector<some_type>::difference_type
. Это обусловлено тем, что метод max_size()
возвращает значение, равное значению distance(begin(), end())
для некоторого максимально большого возможного вектора. См.: container.requirements.general/4. А std::distance
возвращает значение знакового целочисленного типа difference_type
.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Пытаюсь подключить bass dll к проекту в c++ builderСкачал архив с
Нужно сделать таблицу 12x12 , почему выводит только первую строку ? Если я сделал вложенный цикл?
Мне нужно знать, какая установлена раскладка клавиатуры пользователя в данный момент (использую в CALLBACK функции)
Может ли новосозданный вектор после вызова метода reserve(x) иметь capacity больше, чем x? Если да, то правда ли, что чтобы получить вектор с нужным...