Кроссплатформенная строка формата для переменных типа size_t? - PullRequest
32 голосов
/ 06 октября 2008

В кроссплатформенном проекте c / c ++ (Win32, Linux, OSX) мне нужно использовать функции * printf для печати некоторых переменных типа size_t. В некоторых средах size_t имеют размер 8 байт, а в других - 4. На glibc у меня есть% zd, а на Win32 я могу использовать % Id . Есть ли элегантный способ справиться с этим?

Ответы [ 10 ]

20 голосов
/ 06 октября 2008

Макрос PRIuPTR (из ) определяет десятичный формат для uintptr_t, который всегда должен быть достаточно большим, чтобы можно было привести к нему size_t без усечения, например

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);
13 голосов
/ 25 августа 2009

Здесь действительно два вопроса. Первый вопрос: какова правильная строка спецификатора printf для трех платформ? Обратите внимание, что size_t является беззнаковым типом.

Вкл. Windows , используйте «%Iu».

В Linux и OSX , используйте «%zu».

Второй вопрос - как поддерживать несколько платформ, учитывая, что такие вещи, как строки формата, могут быть разными на каждой платформе. Как отмечали другие люди, использование #ifdef быстро становится уродливым.

Вместо этого напишите отдельный make-файл или файл проекта для каждой целевой платформы. Затем обратитесь к спецификатору по имени некоторого макроса в ваших исходных файлах, определив макрос соответствующим образом в каждом make-файле. В частности, и GCC, и Visual Studio принимают переключатель «D» для определения макросов в командной строке.

Если ваша система сборки очень сложна (несколько вариантов сборки, сгенерированные источники и т. Д.), Поддержание 3-х отдельных make-файлов может оказаться громоздким, и вам придется использовать какую-то продвинутую систему сборки, такую ​​как CMake или GNU autotools , Но основной принцип тот же - используйте систему сборки для определения макросов, специфичных для платформы, вместо того, чтобы помещать логику обнаружения платформы в исходные файлы.

8 голосов
/ 06 октября 2008

Единственное, о чем я могу думать, это типично:

#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 голосов
/ 06 октября 2008

Дэн Сакс написал статью в Embedded Systems Design, в которой охватил этот вопрос. По словам Дана,% zu является стандартным способом, но лишь немногие компиляторы поддерживают это. В качестве альтернативы он рекомендовал использовать% lu вместе с явным приведением аргумента к unsigned long:

size_t n;
...
printf("%lu", (unsigned long)n);
2 голосов
/ 06 октября 2008

Используйте boost::format. Он безопасен для типов, поэтому он будет печатать size_t правильно с %d, также вам не нужно помнить, чтобы при его использовании ставить c_str() на std::string с, и даже если вы передадите число %s или наоборот, это сработает.

1 голос
/ 25 августа 2009

Вам просто нужно найти целочисленный тип с наибольшим классом хранения, привести его значение и затем использовать соответствующую строку формата для большего типа. Обратите внимание, что это решение будет работать для любого типа (ptrdiff_t и т. Д.), А не только для size_t.

То, что вы хотите использовать, это uintmax_t и макрос формата PRIuMAX. Для Visual C ++ вам потребуется загрузить c99-совместимые заголовки stdint.h и inttypes.h, потому что Microsoft их не предоставляет.

Также см.

http://www.embedded.com/columns/technicalinsights/204700432

Эта статья исправляет ошибки в статье, цитируемой Фредерико.

1 голос
/ 06 октября 2008

Я не знаю ни одного удовлетворительного решения, но вы могли бы рассмотреть специализированную функцию для форматирования элементов size_t в строку и печати строки.

(В качестве альтернативы, если вам это удастся, boost :: format легко справляется с подобными вещами.)

0 голосов
/ 02 мая 2019

Вариант 1:

Поскольку в большинстве (если не во всех?) Системах строка формата PRIuPTR printf из также достаточно длинна для хранения типа 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

Пример использования:

size_t my_variable;
printf("%" PRIuSZT "\n", my_variable);

Вариант 2:

Однако, где это возможно, просто используйте спецификатор длины %zu "z", , как показано здесь , для size_t типов:

enter image description here

Пример использования:

size_t my_variable;
printf("%zu\n", my_variable);

Однако в некоторых системах, таких как микроконтроллеры STM32, использующие gcc в качестве компилятора, спецификатор длины %z не обязательно реализован, и выполнение чего-то вроде printf("%zu\n", my_size_t_num); может просто привести к выводу литерала " % zu "(я лично проверил это и обнаружил, что это правда) вместо значения вашей size_t переменной.

Вариант 3:

Где вам нужно, чтобы гарантированно работало , однако, или где вы не уверены в своей конкретной архитектуре, просто приведите и распечатайте как uint64_t и все будет готово, как это гарантированно работает, но требует дополнительного этапа кастинга.

Пример использования:

#include <stdint.h>    // for uint64_t
#include <inttypes.h>  // for PRIu64

size_t my_variable;
printf("%" PRIu64 "\n", (uint64_t)my_variable);

Источники цитирования:

  1. http://www.cplusplus.com/reference/cstdio/printf/
  2. http://www.cplusplus.com/reference/cinttypes/
  3. http://www.cplusplus.com/reference/cstdint/
0 голосов
/ 31 июля 2017

size_t - это тип без знака , по крайней мере, 16 бит. Часто видны ширины 32 и 64.

printf("%zu\n", some_size_t_object); // Standard since C99

Выше приведен лучший способ продвижения вперед, однако, если код должен также портировать на платформы до 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);
0 голосов
/ 16 марта 2011

Мой выбор для этой проблемы - просто привести аргумент size_t к unsigned long и использовать% lu везде - это, конечно, только в тех случаях, когда значения не должны превышать 2 ^ 32-1. Если это слишком коротко для вас, вы всегда можете привести к длинному без знака и отформатировать его как% llu.

В любом случае, ваши струны никогда не будут неловкими.

...