Как стать автором
Обновить

Комментарии 21

> size_t value = ....;
> printf("%u", value);

Ещё несколько симпатичных вариантов предлагают здесь:
stackoverflow.com/q/174612
в т.ч.:
1) макрос PRIuPTR из ‹cinttypes›;
2) всегда расширять значение кастом до size_t;
3) специальный формат: %Iu, %zu.

> static_assert(sizeof(long)>=8

В модели LLP64 (например, Windows) sizeof(long) == 4.
Да, с распечаткой size_t как-то путано в С++ получилось. Вот краткая шпаргалка из нашей базы 64-битных знаний: "Как правильно распечатать значение типа __int64, size_t и ptrdiff_t. Если нужна переносимость — придётся использовать вариант 1 или 2. Хотя, они мне не нравятся.

sizeof(long) == 4 в Win64. Я знаю :) Надеюсь и другие знают. Это просто пример static_assert. Не более того.
P.S. Хорошая табличка по поводу размеров типов: www.assembla.com/spaces/zsync-windows/wiki/Types_sizes
Спасибо за табличку (и за пост, кстати, тоже; действительно хотелось отдохнуть от анализаторов :).
Единственное, мне кажется, они там что-то напутали:
char size: 8 bits (MAX VALUE: signed char = 255, unsigned char = 511)
Одно маленькое замечание: пожалуйста, не устраивайте себе проблем — не используйте cout. Оно того не стоит: мало того, что это обычно медленнее, чем printf, так у вас ещё и проблемы с локализацией возникнут. Варианта типа boost::format — это ещё куда ни шло, но использовать cout и std::stringstream — это себя не уважать.
А какая проблема в stringstream? Просто я всегда когда нужно делать конкатенацию строк, чисел итд использую именно ostringstream с последующим вызовом str() и альтернатив толком не вижу, за исключением оператора + для строк и всякие to_string() для конвертации чисел.
А вы, собственно, зачем вообще с C++ связались? Если вас не интересует эффективность вашего творения, то есть много других, куда более безопасных и простых в использовании языков.

У stringstream'а очень дорогой «старт» и «финиш». Сравните:

Скрытый текст
$ cat concat1.cc
#include <string>

std::string concat(std::string a, std::string b) {
  return a + b;
}
$ cat concat2.cc
#include <string>
#include <sstream>

std::string concat(std::string a, std::string b) {
  std::stringstream out;
  out << a << b;
  return out.str();
}
$ cat main.cc
#include <string>

extern std::string concat(std::string, std::string);

int main() {
  std::string hello = "Hello";
  std::string world = "World";

  for (int i=0; i<100000000; ++i) {
    concat(hello, world);
  }
}
$ g++ -O3 concat1.cc main.cc -o test1
$ time ./test1

real 0m8.822s
user 0m8.784s
sys 0m0.000s
$ g++ -O3 concat2.cc main.cc -o test2
$ time ./test2

real 1m8.027s
user 1m7.572s
sys 0m0.064s


Конечно чем более сложные манипуляции выполняются внутри, тем меньше это заметно:

Скрытый текст
$ cat concat1.cc
#include <stdio.h>

#include <string>

std::string concat(int a, int b) {
char buf[50];
snprintf(buf, sizeof(buf), "%d %d", a, b);
return buf;
}
$ cat concat2.cc
#include <string>
#include <sstream>

std::string concat(int a, int b) {
std::stringstream out;
out << a << b;
return out.str();
}
$ cat main.cc
#include <string>

extern std::string concat(int, int);

int main() {
int hello = 42;
int world = 57;

for (int i=0; i<100000000; ++i) {
concat(hello, world);
}
}
$ g++ -O3 concat1.cc main.cc -o test1
$ time ./test1

real 0m20.216s
user 0m20.152s
sys 0m0.000s
$ g++ -O3 concat2.cc main.cc -o test2
$ time ./test2

real 1m11.129s
user 1m10.852s
sys 0m0.012s


В принципе можно предположить и случаи где stringstream будет вполне уместен (например если вы хотите сформировать большой XML документ и положить его в базу), но в большинстве случаев — лучше что-то другое. Медленно и те же самые проблемы с локализацией, что и у cout'а.
Очень агрессивно. И главное бесполезно. Нужно менять взгляды, политику. Если идиот делает заплатку и Вы ее находите, флаг Вам в руки, только америКанский.
Вообще, использовать типы фиксированной длины можно было еще в С99 — достаточно подключить stdint.h. Не уверен, что что-то принципиально поменялось от того, что они теперь прямо в языке присутствуют.
Если я не ошибаюсь, они были стандартны для C (C99, как вы и сказали), но не C++.
И фишка в том, что теперь это и стандарт C++.
Ничего кардинально в C++11 не поменялось. Обратите внимание, что стандарт не требует обязательного наличия типов с точным размером (ISO C++ 18.4.1), так что теоретически их может и не быть (хоть в известных примерах они есть).
А вот смотрите. В C++§18.4.1 действительно все fixed width integer types объявлены как optional. Но ещё там есть пометка:
The header defines all functions, types, and macros the same as 7.18 in the C standard.
Ок, находим ˂stdint.h˃ в C§7.20 (я нашёл черновики стандартов, наверное этим и объясняется несостыковка?). Самое интересное:
For each type described herein that the implementation provides, ˂stdint.h˃ shall declare that typedef name and define the associated macros. … An implementation shall provide those types described as ‘‘required’’, but need not provide any of the others (described as ‘‘optional’’).

C§7.20.1.1 Exact-width integer types

These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, no padding bits, and (for the signed types) that have a two’s complement representation, it shall define the corresponding typedef names.
(Выделил я.) Итого, если я правильно всё понял: если реализация стандарта поддерживает некоторые из этих типов, соответствующие typedef должны (shall) быть объявлены. А необязательными в стандарте они прописаны, просто потому что некоторые реализации могут не поддерживать, например, 8-битные целые числа.

cppreference.com тоже пишет про эти типы: «provided only if the implementation directly supports the type».

Поправите меня? :)
Все верно, я говорю лишь о том, что теоретически их может и не быть.
Практически это может быть обусловлено тем, что на целевой платформе отсутствует поддержка, например, 64-битных целых. На счёт возможности отсутствия поддержки 8- битных сильно сомневаюсь, есть примеры таких реализаций? char — обязательный интегральный тип, и по стандарту он обязательно имеет ширину 8 бит.
Char по стандарту занимает один байт, его sizeof равен 1, а бит в нём может быть сколько угодно.
И правда, спасибо.
en.cppreference.com/w/cpp/language/types
Здесь даже сноска есть на эту тему, не стоит путать байты и октеты.
Вы забываете про то, что бывают всякие 10-битовые CPU с дополнение до одного. Другое дело что на всём этом зоопарке вряд ли будут запускать 99% программ, написанных на С++.

Типы intXX_t хороши тем, что либо вы получаете хорошо предсказуемый тип (известного размера, с известными границами, etc), либо получаете ошибку компиляции (и начинаете думать что вам делать раз уж вы увязли в такой экзотике где int64_t отсутствует).
Я вовсе не предлагал отказаться от их использования, активно пользуюсь типами из cstdint сам, особенно где идёт общение с другими системами посредством IO. Мой поинт был в основном о том, что и до C++11 этот заголовочный файл был и им пользовались.
А можете привести пример платформы с дополнением до одного? Мне казалось, что все они закончились еще лет 20 назад.
На сегодняшний день таки существуют платформы без восьмибитного типа — в основном это DSP (например, TI C62xx и C64xx). Но мне сложно сказать, насколько С или С++ востребован для DSP…

Еще вот эта ссылка говорит, что компиляторы для WinCE вообще не поддерживали тип char (вместо того, чтобы сделать CHAR_BITS = 16).
Вообще, я совершенно не задумывался, когда выбрал в качестве примера отсутствия exact-width integer type именно 8-битные целые (т. е. [u]int8_t). Так уж вышло :) А из сего факта получилась целая дискуссия. Это интересно.
Числовой литерал 1 имеет тип int. Значит, его нельзя сдвигать более чем на 31 разряд (подробнее). Про это часто забывают, и в программах можно встретить вот такой код:

ptrdiff_t mask = 1 << bitNum;

Если, значение bitNum будем равно, скажем 40, то результат будет непредсказуем. Формально, это приведёт к undefined behavior (подробнее).

Может нам помочь C++11? К сожалению, ничем.

Эммм… Но ведь в литералах можно суффиксами задавать конкретный тип:

ptrdiff_t mask = 1U << bitNum; // 1 - unsigned int


Или так:

ptrdiff_t mask = 1UL << bitNum; // 1 - unsigned long


Или даже так:

ptrdiff_t mask = 1ULL << bitNum; // 1 - unsigned long long


Более того, в С++11 были добавлены user-defined суффиксы для более точной типизации литералов, так что можно задавать вообще свой конкретный суффикс:

ptrdiff_t mask = 1_ptrdiff << bitNum; // 1 - ptrdiff_t
А причём здесь C++11? 1UL, 1ULL можно было и раньше написать. Идея именно в том, что сам по себе стандарт не помогает как-то особенно по-новому безопасно писать подобные конструкции.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий