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

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

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

size_t x = <something>;
printf( "size = %u\n", x );

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

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

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

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

Ответ 1

Используйте модификатор 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

Ответ 2

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

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

Ответ 3

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

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

Для C99 и более поздних версий используйте %zu:

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

Ответ 4

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

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

// 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, говорит:

цу
гх
ZD

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

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

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

Кроме того, C11 <uchar.h> теперь включен в последний предварительный просмотр.

Для получения дополнительной информации см. этот старый и new для стандартного соответствия.

Ответ 5

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

std:: size_t s = 1024; std:: cout < s;//или любой другой поток, например stringstream!

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

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

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

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

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

Итак, вы попадаете в потоки С++, условную компиляцию, сторонние структуры или что-то вроде портативного компьютера, который, как вам кажется, работает для вас.

Ответ 10

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

В качестве альтернативы используйте некоторую поточную передачу (например, stringstream) или безопасную замену printf, такую ​​как Boost Format. Я понимаю, что этот совет ограничен только для использования (и требует С++). (Мы использовали аналогичный подход для наших нужд при внедрении поддержки юникода.)

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

Ответ 11

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

Например:

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

enter image description here

Ответ 12

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

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

Ответ 13

если вы хотите напечатать значение 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

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

Изменить: перечитывая вопрос из-за понижательных голосов, я заметил, что его проблема не в% llu или% I64d, а тип size_t на разных машинах. Этот вопрос fooobar.com/info/27500/...
http://www.cplusplus.com/reference/cstdio/printf/

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

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