Кросс-платформенный формат строки для переменных типа size_t?
В проекте кросс-платформы c/С++ (Win32, Linux, OSX) мне нужно использовать функции * printf для печати некоторых переменных типа size_t. В некоторых средах size_t составляет 8 байт, а на других - 4. На glibc у меня есть% zd, а на Win32 я могу использовать % Id. Есть ли элегантный способ справиться с этим?
Ответы
Ответ 1
Макрос PRIuPTR
(from < inttypes.h > ) определяет десятичный формат для uintptr_t
, который всегда должен быть достаточно большим, чтобы вы могли отличать его size_t
без усечения, например
fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);
Ответ 2
Здесь есть два вопроса. Первый вопрос - правильная строка спецификатора printf для трех платформ. Обратите внимание, что size_t
является неподписанным типом.
В Windows используйте "%Iu
".
В Linux и OSX, используйте "%zu
".
Второй вопрос заключается в том, как поддерживать несколько платформ, учитывая, что на каждой платформе могут быть разные строки форматирования. Как отмечали другие люди, использование #ifdef
становится уродливым быстро.
Вместо этого напишите отдельный файл makefile или project для каждой целевой платформы. Затем обратитесь к спецификатору с помощью некоторого имени макроса в исходных файлах, соответствующим образом определяя макрос в каждом make файле. В частности, как GCC, так и Visual Studio принимают переключатель "D" для определения макросов в командной строке.
Если ваша система сборки очень сложная (несколько вариантов сборки, сгенерированные источники и т.д.), поддержка 3 отдельных make файлов может стать необоснованной, и вам придется использовать какую-то усовершенствованную систему сборки, такую как CMake или GNU autotools, Но основной принцип тот же: используйте систему сборки для определения макросов, специфичных для платформы, вместо того, чтобы размещать логику обнаружения платформы в ваших исходных файлах.
Ответ 3
Единственное, что я могу придумать, это типичное:
#ifdef __WIN32__ // or whatever
#define SSIZET_FMT "%ld"
#else
#define SSIZET_FMT "%zd"
#endif
а затем воспользовавшись постоянным складыванием:
fprintf(stream, "Your size_t var has value " SSIZET_FMT ".", your_var);
Ответ 4
Дэн Сакс написал статью в Embedded Systems Design, которая охватывала этот вопрос. По словам Дэна,% zu является стандартным способом, но немногие компиляторы поддержали это. В качестве альтернативы он рекомендовал использовать% lu вместе с явным приведением аргумента в unsigned long:
size_t n;
...
printf("%lu", (unsigned long)n);
Ответ 5
Используйте boost::format
. Это типично, поэтому он будет правильно печатать size_t
с помощью %d
, также вам не нужно будет помещать c_str()
на std::string
при его использовании, и даже если вы передадите номер на %s
или наоборот, это сработает.
Ответ 6
Я не знаю ни одного удовлетворяющего решения, но вы можете рассмотреть специализированную функцию для форматирования элементов size_t для строки и напечатать строку.
(В качестве альтернативы, если вы можете уйти от него, boost:: format справляется с таким типом с легкостью.)
Ответ 7
Вам просто нужно найти целочисленный тип с наибольшим классом хранения, указать значение для него и затем использовать соответствующую строку формата для более крупного типа. Обратите внимание, что это решение будет работать для любого типа (ptrdiff_t и т.д.), А не только size_t.
То, что вы хотите использовать, - uintmax_t и макрос формата PRIuMAX. Для Visual С++ вам понадобится загрузить c99-совместимые заголовки stdint.h и inttypes.h, поскольку Microsoft не предоставляет их.
Также см.
http://www.embedded.com/columns/technicalinsights/204700432
В этой статье исправлены ошибки в статье, цитируемой Фредерико.
Ответ 8
Мой выбор для этой проблемы состоит в том, чтобы просто привести аргумент size_t к unsigned long и использовать% lu всюду - это, конечно, только там, где значения не должны превышать 2 ^ 32-1. Если это слишком коротко для вас, вы всегда можете использовать unsigned long long и отформатировать его как% llu.
В любом случае, ваши строки никогда не будут неудобными.
Ответ 9
Опция 1:
Поскольку в большинстве (если не во всех?) PRIuPTR
строка формата printf PRIuPTR
из также достаточно длинный, чтобы содержать тип size_t
, я рекомендую использовать следующие определения для строк формата size_t
printf.
Тем не менее, важно убедиться, что это будет работать для вашего конкретного оборудования, так как стандарт не обеспечивает это.
#include <inttypes.h>
// Printf format strings for 'size_t' variable types.
#define PRIdSZT PRIdPTR
#define PRIiSZT PRIiPTR
#define PRIoSZT PRIoPTR
#define PRIuSZT PRIuPTR
#define PRIxSZT PRIxPTR
#define PRIXSZT PRIXPTR
Вариант 2:
Однако, где это возможно, просто используйте спецификатор длины %zu
"z", как показано здесь, для типов size_t
:
Однако в некоторых системах, таких как микроконтроллеры STM32, использующие в качестве компилятора gcc, спецификатор длины %z
не обязательно реализован и выполняет что-то вроде printf("%zu\n", my_size_t_num);
может просто закончить выводом литерала "% zu" (я лично проверил это и обнаружил, что это правда) вместо значения вашей переменной size_t
.
Вариант 3:
Однако там, где вам нужно, чтобы это было абсолютно гарантировано, или если вы не уверены в своей конкретной архитектуре, просто uint64_t
и распечатайте как uint64_t
и все будет сделано, поскольку это гарантированно сработает, но требует дополнительного шага приведения., Пример:
#include <stdint.h>
#include <inttypes.h>
size_t my_variable;
printf("%" PRIu64 "\n", (uint64_t)my_variable);
Источники цитируются:
- http://www.cplusplus.com/reference/cstdio/printf/
- http://www.cplusplus.com/reference/cinttypes/
- http://www.cplusplus.com/reference/cstdint/
Ответ 10
size_t
является неподписанным типом по меньшей мере 16 бит. Часто видны ширины 32 и 64.
printf("%zu\n", some_size_t_object); // Standard since C99
Выше - это лучший способ продвижения вперед, но если код должен также переноситься на платформы pre-C99, скрывайте значение до некоторого широкого типа. unsigned long
является разумным кандидатом, но может отсутствовать.
// OK, yet insufficient with large sizes > ULONG_MAX
printf("%lu\n", (unsigned long) some_size_t_object);
или с условным кодом
#ifdef ULLONG_MAX
printf("%llu\n", (unsigned long long) some_size_t_object);
#else
printf("%lu\n", (unsigned long) some_size_t_object);
#endif
Наконец, рассмотрим double
. Он немного неэффективен, но должен обрабатывать все древние и новые платформы до 2030-2040 годов, рассматривая закон Мура, когда double
может не иметь точного результата.
printf("%.0f\n", (double) some_size_t_object);