Як можна друкувати змінну size_t портативно за допомогою сім'ї printf?


403

У мене є змінна тип 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. Це працювало б в обох випадках. Я дивлюсь, чи є якась інша ідея.


4
кастинг на unsigned long- найкращий варіант, якщо ваша реалізація libc не підтримує zмодифікатор; стандарт C99 рекомендує size_tне мати цілого числу конверсій, що перевищує long, так що ви досить безпечні
Крістоф

можливий дублікат незалежних
maxschlepzig


1
На платформі Windows розмір_t може бути більшим, ніж довгий. З міркувань сумісності довгий завжди 32-розрядний, але розмір_t може бути 64-розрядним. Таким чином, лиття на неподписаний довгий може втратити половину бітів. Вибачте :-)
Брюс Доусон

Відповіді:


481

Використовуйте 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

7
+1. Це додаток C99 чи це стосується і C ++ (у мене немає C90 під рукою)?
авакар

6
це додаток C99 і не міститься у списку printf()модифікаторів довжини проекту C ++ 0x від 2009-11-09 (таблиця 84 на сторінці 672)
Крістоф,

3
@Christoph: Немає в останньому проекті, n3035.
GManNickG

11
@avakar @Adam Rosenfield @Christoph @GMan: Однак у n3035 §1.2 Нормативні посилання посилається лише на стандарт C99, а в §17.6.1.2 / 3 того ж штату "Доступні засоби стандартної бібліотеки С". Я би інтерпретував це так, що, якщо не вказано інше, все в стандартній бібліотеці C99 є частиною стандартної бібліотеки C ++ 0x, включаючи додаткові специфікатори формату в C99.
Джеймс Мак-Нілліс

9
@ArunSaha: Це лише функція C99, а не C ++. Якщо ви хочете, щоб він компілювався -pedantic, вам потрібно буде отримати компілятор, що підтримує чернетку C ++ 1x (вкрай малоймовірно), або вам потрібно буде перемістити свій код у файл, складений як C99. В іншому випадку ваш єдиний варіант - передавати свої змінні unsigned long longта використовувати їх %lluдля максимального перенесення.
Адам Розенфілд

88

Схоже, він змінюється залежно від того, який компілятор ви використовуєте (blech):

... і звичайно, якщо ви використовуєте C ++, ви можете використовувати coutзамість цього, як пропонує AraK .


3
zтакож підтримується newlib (тобто cygwin)
Крістоф

7
%zdневірно для size_t; це правильно для відповідного типу, який підписується size_t, але size_tсам це непідписаний тип.
Кіт Томпсон

1
@KeithThompson: Я також згадував %zu%zxякщо вони хочуть шістнадцятковий). Правда, що, %zuмабуть, мав би бути першим у списку. Виправлено.
TJ Crowder

10
@TJCrowder: Я не думаю, що це взагалі %zdповинно бути в списку. Я не можу придумати жодної причини використовувати, %zdа не %zuнадрукувати size_tзначення. Це навіть не вірно (має невизначене поведінку), якщо значення перевищує SIZE_MAX / 2. (Для повноти ви можете згадати %zoвосьмери.)
Кіт Томпсон,

2
@FUZxxl: POSIX не вимагає ssize_tвідповідного підписаного типу size_t, тому відповідність не гарантується "%zd". ( Можливо, це стосується більшості реалізацій.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Кіт Томпсон,

59

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

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

Для C99 та новіших версій використовуйте %zu:

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

7
З 2013 року запропонуйте "Для C99 і далі" та "Для попереднього C99:". Найкраща відповідь.
chux

8
Не роби цього. Він вийде з ладу в 64-бітних Windows, де size_t - 64 біт, а довгий - 32 біт.
Yttrill

1
@Yttrill: Яка відповідь для 64-розрядних вікон?
Джон Боде

1
@JohnBode Можливо unsigned long long?
Джеймс Ко

2
Або: ви можете передати на a, uint64_tа потім використовувати PRIu64макрос з inttypes.h, який містить специфікатор формату.
Джеймс Ко

9

Розширення відповіді Адама Розенфілда для 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, говорить:

zu
zx
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>тепер включений до останнього попереднього попереднього перегляду.

Для отримання більш детальної інформації див. Цей старий та новий список для стандартної відповідності.


VS2013 оновлення 5 дає ті самі результати, що і оновлення 4.
Натан Кідд

6

Для тих, хто говорить про це в C ++, який не обов'язково підтримує розширення C99, я щиро рекомендую формат boost ::. Таким чином, питання розміру типу size_t стає запитом:

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

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


4
Напевно, хочу %uтоді.
GManNickG

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

8
Так, але запитуючий запитує конкретно printfспецифікатора. Я б здогадувався, що у них є деякі інші невстановлені обмеження, які викликають використання std::coutпроблеми.
Стипендіати Дональ

1
@Donal Цікаво, яку проблему можуть створити потоки C ++ у проекті C ++!
AraK

9
@AraK. Вони дуже повільні? Вони додають багато байтів з не великої причини. Арунсаха просто хоче знати про свої особисті знання? Особисті переваги (я вважаю за краще stdio для fstream себе). Причин багато.
KitsuneYMG

1
@TKCrowder: Ну, в початковому запиті було сказано, що потрібне рішення C (через теги), і є вагомі причини не використовувати потоки в C ++, наприклад, якщо дескриптор формату виведення витягується з каталогу повідомлень. (Ви можете написати парсер для повідомлень і використовувати потоки, якщо хочете, але це велика робота, коли ви можете просто скористатись наявним кодом.)
Дональд стипендіатів

1
@Donal: Теги були C та C ++. Я жодним чином не виступаю за течію потоку вводу / виводу C ++ (я не прихильник цього), просто вказую, що питання спочатку не було * "... задайте специфікацію для printfспецифікатора."
TJ Crowder


2

Як сказав AraK, інтерфейс потоків c ++ завжди працюватиме портативно.

std :: size_t s = 1024; std :: cout << s; // або будь-який інший вид потоку, як stringstream!

Якщо ви хочете, щоб C stdio, для деяких випадків "портативного" відповіді на це немає, відповідь на це не існує. І це стає некрасиво, оскільки, як ви вже бачили, вибір прапорів неправильного формату може спричинити попередження компілятора або дати неправильний вихід.

C99 намагався вирішити цю проблему за допомогою inttypes.h форматів, таких як "%" PRIdMAX "\ n". Але так само, як і у "% zu", не всі підтримують c99 (як MSVS до 2013 року). Існують файли "msinttypes.h", які плавають навколо, щоб вирішити це.

Якщо ви перейдете до іншого типу, залежно від прапорів, ви можете отримати попередження компілятора про усічення або зміну знака. Якщо ви йдете цим маршрутом, виберіть більший відповідний тип фіксованого розміру. Один з неподписаних довгих довгих і "% llu" або неподписаних довгих "% lu" повинен працювати, але llu також може сповільнювати роботу в 32-бітовому світі як надмірно великий. (Редагувати - мій mac видає попередження у 64 бітах для% llu, що не відповідає size_t, навіть якщо% lu,% llu та size_t - однаковий розмір. І% lu та% llu - не однаковий розмір на моєму MSVS2012. Отже Вам може знадобитися передати + використовувати формат, який відповідає.)

З цього питання ви можете скористатися типами фіксованого розміру, такими як int64_t. Але зачекайте! Тепер ми повернулися до c99 / c ++ 11, і старіший MSVS знову не працює. Крім того, у вас також є касти (наприклад, map.size () не є фіксованим розміром)!

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

Таким чином, ви переходите до потоків c ++, умовної компіляції, сторонніх фреймворків чи чогось такого портативного, що може працювати для вас.


-1

Чи попередить вас, якщо ви передасте 32-бітне ціле число без підпису у формат% lu? Це має бути добре, оскільки конверсія чітко визначена і не втрачає жодної інформації.

Я чув, що деякі платформи визначають макроси за тим, <inttypes.h>що ви можете вставити в рядковий формат буквально, але я не бачу цього заголовка в моєму компіляторі Windows C ++, що означає, що він може не бути кросплатформенним.


1
Більшість компіляторів не попередить вас, якщо ви передасте щось невірного розміру в printf. GCC - виняток. inttypes.h було визначено в C99, тому будь-який компілятор C, сумісний із C99, матиме його, який має бути на сьогоднішній день. Тим не менш, можливо, вам доведеться ввімкнути C99 за допомогою прапора компілятора. У будь-якому випадку, intttypes.h не визначає конкретний формат для size_t або ptrdiff_t, оскільки вони вирішили бути досить важливими для отримання власних специфікаторів розмірів відповідно з 'z' і 't'.
підгортання

Якщо ви використовуєте %lu, вам слід size_tпередати це значення unsigned long. Немає неявного перетворення (крім рекламних) для аргументів до printf.
Кіт Томпсон

-2

Для цього C99 визначає "% zd" тощо. (спасибі коментаторам) Для цього в C ++ немає специфічного портативного специфікатора формату - ви можете використовувати %p, яке слово було б у цих двох сценаріях, але не є і портативним вибором, і надає значення у шістнадцятковій формі.

Крім того, використовуйте деяку потокову передачу (наприклад, потоковий потік) або безпечну заміну printf, наприклад формат Boost . Я розумію, що ця порада має обмежене використання (і вимагає C ++). (Ми використовували аналогічний підхід, відповідний нашим потребам при впровадженні підтримки unicode.)

Основна проблема для C полягає в тому, що printf, що використовує еліпсис, не є безпечним за конструкцією - йому потрібно визначити розмір додаткового аргументу з відомих аргументів, тому його не можна зафіксувати, щоб підтримувати "все, що у вас є". Тож якщо ваш компілятор не реалізує деякі власні розширення, вам не пощастить.


2
zрозмір modidfier є стандартним C, але деякі Libc реалізації застрягла в 1990 році з різних причин (наприклад , Microsoft в основному занедбані C на користь C ++ і - зовсім недавно - C #)
Christoph

3
C99 визначив специфікатор розміру 'z', який буде розміром значення size_t, а 't' - розміром значення ptrdiff_t.
підгортання

2
%zdнеправильно, він не підписаний, так і має бути %zu.
Девід Конрад

-3

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

Я тут задокументував цю складну проблему з прикладом коду: http://www.pixelbeat.org/programming/gcc/int_types/ та періодично оновлював її інформацією про нові платформи та типи.


1
Зауважте, що відповіді, що стосуються лише посилань, не відштовхують, відповіді на відповідність повинні бути кінцевою точкою пошуку рішення (проти ще однієї зупинки посилань, яка, як правило, з часом зависла). Просимо додати тут окремий конспект, зберігаючи посилання як орієнтир.
Клеопатра

-5

якщо ви хочете надрукувати значення 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 на різних машинах. Дивіться це питання https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/

size_t непідписаний int на 32-бітній машині, а unsigned long long int на 64bit,
але% ll завжди очікує неподписаного long long int.

size_t змінюється по довжині в різних операційних системах, тоді як% llu однаковий


4
Що за дурниці це ?!
Антті Хаапала

лиття перших 8 байтів масиву char у безпідписаний довгий 64-бітний через покажчик size_t та друк їх як номер за допомогою printf% I64d насправді не вражаючий, я знаю, звичайно, я не знав код, щоб запобігти переповненню типу, але це не в обсязі питання.
Андре
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.