Как можно напечатать переменную размера t с помощью семейства printf?

у меня есть переменная типа size_t и я хочу, чтобы распечатать его с помощью printf(). Какой спецификатор формата я использую для его переносимости?

в 32-разрядной машине, %u кажется правильным. Я скомпилировал с g++- g-W-Wall-Werror-ansi-pedantic, и не было никакого предупреждения. Но когда я компилирую этот код на 64-битной машине, он выдает предупреждение.

size_t x = <something>;
printf( "size = %un", x );

warning: format '%u' expects type 'unsigned int', 
    but argument 2 has type 'long unsigned int'

предупреждение уходит, как и ожидалось, если я изменю это на %lu.

вопрос в том, как я могу написать код, чтобы он компилировал предупреждение бесплатно на 32 - и 64-битных машинах?

Edit: я думаю, одним из ответов может быть "приведение" переменной в unsigned long, и печать с помощью %lu. Это сработает в обоих случаях. Я смотрю, есть ли какая-нибудь другая идея.

13 ответов


использовать z модификатор:

size_t x = ...;
ssize_t y = ...;
printf("%zu\n", x);  // prints as unsigned decimal
printf("%zx\n", x);  // prints as hex
printf("%zd\n", y);  // prints as signed decimal

похоже, что это зависит от того, какой компилятор вы используете (blech):

...и конечно, если вы используете c++, вы можете использовать cout вместо as предложил Арак.


для C89 используйте %lu и приведите значение к unsigned long:

size_t foo;
...
printf("foo = %lu\n", (unsigned long) foo);

для C99 и позже, используйте %zu:

size_t foo;
...
printf("foo = %zu\n", foo);

расширение ответа Адама Розенфилда для Windows.

Я тестировал этот код как на VS2013 Update 4, так и на VS2015 preview:

// test.c

#include <stdio.h>
#include <BaseTsd.h> // see the note below

int main()
{
    size_t x = 1;
    SSIZE_T y = 2;
    printf("%zu\n", x);  // prints as unsigned decimal
    printf("%zx\n", x);  // prints as hex
    printf("%zd\n", y);  // prints as signed decimal
    return 0;
}

VS2015 сгенерированные двоичные выходы:

1
1
2

в то время как созданный VS2013 говорит:

ЗУ
на ZX
зд

Примечание: ssize_t является расширением POSIX и SSIZE_T аналогичная вещь в Типы Данных Windows, следовательно, я добавил <BaseTsd.h> ссылка.

кроме того, за исключением следующих заголовков C99/C11, все заголовки C99 доступны в VS2015 preview:

C11 - <stdalign.h>
C11 - <stdatomic.h>
C11 - <stdnoreturn.h>
C99 - <tgmath.h>
C11 - <threads.h>

также, по С11 <uchar.h> теперь включен в последний просмотр.

для получения более подробной информации смотрите старый и новая список для стандартного соответствия.


std::size_t s = 1024;
std::cout << s; // or any other kind of stream like stringstream!

для тех, кто говорит об этом в C++ , который не обязательно поддерживает расширения C99, я сердечно рекомендую boost:: format. Это делает вопрос размера типа size_t спорным:

std::cout << boost::format("Sizeof(Var) is %d\n") % sizeof(Var);

поскольку вам не нужны спецификаторы размера в формате boost::, вы можете просто беспокоиться о том, как вы хотите отобразить значение.


printf("size = %zu\n", sizeof(thing) );

Как сказал Арак, интерфейс потоков c++ всегда будет работать переносимо.

std:: size_t s = 1024; std:: cout

Если вы хотите c stdio, нет портативного ответа на это для определенных случаев " portable."И это становится уродливым, так как, как вы видели, выбор неправильных флагов формата может дать предупреждение компилятора или дать неправильный вывод.

C99 попытался решить эту проблему с помощью inttypes.H форматы, такие как "%"PRIdMAX"\n". Но так же, как и с" %zu", не все поддерживают c99 (например, MSVS до 2013 года). Есть " msinttypes.h " файлы, плавающие вокруг, чтобы справиться с этим.

Если вы используете другой тип, в зависимости от флагов вы можете получить предупреждение компилятора об усечении или изменении знака. Если вы идете по этому маршруту, выберите более крупный тип фиксированного размера. Один из unsigned long long и "%llu "или unsigned long" %lu " должен работать, но llu также может замедлить работу в 32-битном мир как чрезмерно большой. (Edit-мой mac выдает предупреждение в 64 бит для %llu, не соответствующего size_t, хотя %lu, %llu и size_t имеют одинаковый размер. И %lu и %llu не имеют одинакового размера на моем MSVS2012. Поэтому вам может потребоваться cast + использовать формат, который соответствует.)

Если на то пошло, вы можете пойти с типами фиксированного размера, такими как int64_t. Но подождите! Теперь мы вернулись к c99/c++11, и старые MSVS снова терпят неудачу. Кроме того, у вас также есть слепки (например, карта.size () не является фиксированным размером типа)!

вы можете использовать сторонний заголовок или библиотеку, такие как boost. Если вы еще не используете его, вы можете не захотеть раздувать свой проект таким образом. Если вы хотите добавить его только для этой проблемы, почему бы не использовать потоки c++ или условную компиляцию?

Итак, вы дошли до потоков c++, условной компиляции, сторонних фреймворков или чего-то портативного, что работает для вас.


он предупредит вас, если вы передадите 32-разрядное целое число без знака в формат %lu? Это должно быть хорошо, так как преобразование четко определено и не теряет никакой информации.

Я слышал, что некоторые платформы определяют макросы в <inttypes.h> Что вы можете вставить в строковый литерал формата, но я не вижу этого заголовка в моем компиляторе Windows C++, что означает, что он не может быть кросс-платформенным.


Если вы используете C++11, вы можете привести size_t к std::string по std::to_string, но это не элегантно(создайте временную строку).

например:

size_t st = 555;
printf("%s", std::to_string(st).c_str());

enter image description here


C99 определяет "%zd " и т. д. для этого. (спасибо комментаторам) для этого в C++ нет портативного спецификатора формата-you может использовать %p, который woulkd word в этих двух сценариях, но также не является переносным выбором и дает значение в hex.

в качестве альтернативы используйте потоковую передачу (например, stringstream) или безопасную замену printf, такую как Увеличить Формат. Я понимаю, что этот совет имеет ограниченное использование (и требует C++). (Мы использовал аналогичный подход, подходящий для наших нужд при реализации поддержки unicode.)

основная проблема для C заключается в том, что printf с использованием многоточия небезопасен по дизайну - ему нужно определить размер дополнительного аргумента из известных аргументов, поэтому его нельзя исправить Для поддержки "все, что у вас есть". Поэтому, если ваш компилятор не реализует некоторые собственные расширения, вам не повезло.


на некоторых платформах и для некоторых типов доступны определенные спецификаторы преобразования printf, но иногда приходится прибегать к кастингу более крупных типов.

Я задокументировал эту сложную проблему здесь, с примером кода: http://www.pixelbeat.org/programming/gcc/int_types/ и периодически обновляйте его информацией о новых платформах и типах.


Если вы хотите напечатать значение size_t в виде строки, вы можете сделать следующее:

char text[] = "Lets go fishing in stead of sitting on our but !!";
size_t line = 2337200120702199116;

/* on windows I64x or I64d others %lld or %llx if it works %zd or %zx */
printf("number: %I64d\n",*(size_t*)&text);
printf("text: %s\n",*(char(*)[])&line);

результат:

номер: 2337200120702199116

текст: давайте порыбачим вместо того, чтобы сидеть на нашем но !!

Edit: перечитывая вопрос из-за голосов вниз, я отметил, что его проблема не %llu или %I64d, но тип size_t на разных машинах см. Этот вопрос https://stackoverflow.com/a/918909/1755797
http://www.cplusplus.com/reference/cstdio/printf/

size_t-это unsigned int на 32-битной машине и unsigned long long int на 64-битном
но %ll всегда ожидает длинный длинный int без знака.

size_t изменяется по длине в разных операционных системах, в то время как %llu одинаковый