Mam zmienną typu size_t
i chcę ją wydrukować za pomocą printf()
. Jakiego specyfikatora formatu używam do przenośnego drukowania?
W 32-bitowej maszynie %u
wydaje się słuszna. Kompilowałem się g++ -g -W -Wall -Werror -ansi -pedantic
i nie było ostrzeżenia. Ale kiedy kompiluję ten kod na komputerze 64-bitowym, wyświetla ostrzeżenie.
size_t x = <something>;
printf("size = %u\n", x);
warning: format '%u' expects type 'unsigned int',
but argument 2 has type 'long unsigned int'
Ostrzeżenie zniknie zgodnie z oczekiwaniami, jeśli zmienię to na %lu
.
Pytanie brzmi: jak mogę napisać kod, aby kompilował się bez ostrzeżenia na komputerach 32- i 64-bitowych?
Edycja: Jako obejście, jedną z odpowiedzi może być „rzutowanie” zmiennej na liczbę całkowitą, która jest wystarczająco duża, powiedzmy unsigned long
i drukuj za pomocą %lu
. To działałoby w obu przypadkach. Szukam, czy jest jakiś inny pomysł.
unsigned long
jest najlepszą opcją, jeśli twoja implementacja libc nie obsługujez
modyfikatora; standard C99 zaleca,size_t
aby nie mieć rangi konwersji liczb całkowitych większych niżlong
, więc jesteś względnie bezpiecznyOdpowiedzi:
Użyj
z
modyfikatora:źródło
printf()
modyfikatorów długości wersji C ++ 0x z 2009-11-09 (tabela 84 na stronie 672)-pedantic
, musisz albo uzyskać kompilator obsługujący wersję roboczą C ++ 1x (bardzo mało prawdopodobne), albo musisz przenieść kod do pliku skompilowanego jako C99. W przeciwnym razie jedyną opcją jest rzutowanie zmiennychunsigned long long
i używanie ich,%llu
aby były maksymalnie przenośne.Wygląda na to, że różni się w zależności od używanego kompilatora (blech):
%zu
(lub%zx
,%zd
ale wyświetla to tak, jakby było podpisane itp.)%Iu
(lub%Ix
,%Id
ale znowu jest podpisany itp.) - ale od wersji v19 (w Visual Studio 2015), Microsoft obsługuje%zu
(zobacz tę odpowiedź na ten komentarz )... i oczywiście, jeśli używasz C ++, możesz użyć
cout
zamiast tego, jak sugeruje AraK .źródło
z
jest również wspierany przez newlib (tj. cygwin)%zd
jest niepoprawny dlasize_t
; jest poprawny dla typu podpisanego odpowiadającegosize_t
, alesize_t
sam jest typem niepodpisanym.%zu
również (i%zx
na wypadek, gdyby chcieli hex). To prawda, że%zu
prawdopodobnie powinien być pierwszy na liście. Naprawiony.%zd
powinien być w ogóle na liście. Nie mogę wymyślić żadnego powodu do użycia%zd
zamiast%zu
drukowaniasize_t
wartości. To nie jest nawet poprawne (ma niezdefiniowane zachowanie), jeśli wartość przekraczaSIZE_MAX / 2
. (Dla kompletności możesz wymienić%zo
ósemkowy.)ssize_t
aby odpowiadał podpisanemu typowisize_t
, więc nie można zagwarantować zgodności"%zd"
. ( Prawdopodobnie jest na większości implementacji.) Pubs.opengroup.org/onlinepubs/9699919799/basedefs/…W przypadku C89 użyj
%lu
i rzutuj wartość naunsigned long
:W przypadku C99 i nowszych użyj
%zu
:źródło
unsigned long long
?uint64_t
a następnie użyćPRIu64
makra z inttypes.h, który zawiera specyfikator formatu.Rozszerzenie odpowiedzi Adama Rosenfielda dla systemu Windows.
Testowałem ten kod zarówno w wersji VS2013 Update 4, jak i w wersji zapoznawczej VS2015:
Generowane przez binarne wyjścia VS2015:
podczas gdy ten wygenerowany przez VS2013 mówi:
Uwaga:
ssize_t
jest rozszerzeniem POSIX iSSIZE_T
jest podobna w typach danych systemu Windows , dlatego dodałem<BaseTsd.h>
odwołanie.Dodatkowo, z wyjątkiem następujących nagłówków C99 / C11, wszystkie nagłówki C99 są dostępne w wersji zapoznawczej VS2015:
Ponadto C11
<uchar.h>
jest teraz uwzględnione w najnowszej wersji zapoznawczej.Aby uzyskać więcej informacji, zobacz tę starą i nową listę standardowej zgodności.
źródło
Dla tych, którzy mówią o robieniu tego w C ++, który niekoniecznie obsługuje rozszerzenia C99, serdecznie polecam format :: ::. To sprawia, że pytanie size size rozmiar size może być dyskusyjne:
Ponieważ nie potrzebujesz specyfikatorów rozmiaru w formacie boost ::, możesz się martwić, jak chcesz wyświetlić wartość.
źródło
%u
.źródło
printf
specyfikator. Sądzę, że mają pewne inne nieokreślone ograniczenia, które sprawiają, że używaniestd::cout
problemu.printf
specyfikację”.źródło
Jak powiedział AraK, interfejs strumieni c ++ zawsze będzie działał przenośnie.
Jeśli chcesz C stdio, nie ma przenośnej odpowiedzi na to pytanie w niektórych przypadkach „przenośnych”. I robi się brzydko, ponieważ, jak widzieliście, wybranie flag w niewłaściwym formacie może wygenerować ostrzeżenie kompilatora lub dać niepoprawne dane wyjściowe.
C99 próbował rozwiązać ten problem za pomocą formatów inttypes.h, takich jak „%” PRIdMAX ”\ n”. Ale podobnie jak w przypadku „% zu”, nie każdy obsługuje c99 (jak MSVS przed 2013 rokiem). Istnieją pliki „msinttypes.h” pływające wokół, aby sobie z tym poradzić.
Jeśli wybierzesz inny typ, w zależności od flag możesz otrzymać ostrzeżenie kompilatora o obcięciu lub zmianie znaku. Jeśli wybierzesz tę trasę, wybierz większy odpowiedni stały rozmiar. Jeden z niepodpisanych długich długich i „% llu” lub niepodpisanych długich „% lu” powinien działać, ale llu może również spowolnić rzeczy w 32-bitowym świecie jako zbyt dużym. (Edycja - mój Mac wyświetla ostrzeżenie w 64-bitowej wersji, jeśli% llu nie pasuje do size_t, mimo że% lu,% llu i size_t są tego samego rozmiaru. A% lu i% llu nie są tego samego rozmiaru na moim MSVS2012. być może będziesz musiał przesłać + użyć formatu, który pasuje.)
W tym przypadku możesz przejść ze stałymi typami rozmiarów, takimi jak int64_t. Ale poczekaj! Teraz wróciliśmy do c99 / c ++ 11, a starszy MSVS ponownie zawiedzie. Dodatkowo masz również casty (np. Map.size () nie jest stałym typem rozmiaru)!
Możesz użyć nagłówka lub biblioteki innej firmy, takiej jak boost. Jeśli jeszcze go nie używasz, możesz nie chcieć zawyżać swojego projektu w ten sposób. Jeśli chcesz dodać jeden tylko dla tego problemu, dlaczego nie użyć strumieni c ++ lub kompilacji warunkowej?
Więc sprowadzasz się do strumieni c ++, kompilacji warunkowej, frameworków zewnętrznych lub czegoś w rodzaju przenośnego urządzenia, które akurat działa.
źródło
Czy ostrzeże Cię, jeśli przekażesz 32-bitową liczbę całkowitą bez znaku do formatu% lu? Powinno być dobrze, ponieważ konwersja jest dobrze zdefiniowana i nie traci żadnych informacji.
Słyszałem, że niektóre platformy definiują makra
<inttypes.h>
, które można wstawić do literału ciągu formatu, ale nie widzę tego nagłówka w moim kompilatorze Windows C ++, co oznacza, że może nie być wieloplatformowy.źródło
%lu
, powinieneś rzucićsize_t
wartość naunsigned long
. Nie ma niejawnej konwersji (innej niż promocje) dla argumentów naprintf
.C99 definiuje w tym celu „% zd” itp. (dzięki komentujących) nie istnieje przenośny format specifier za to w C ++ - Państwo mogli używać
%p
, które woulkd słowo w tych dwóch scenariuszy, ale nie jest przenośnym wybór albo i daje wartość w Hex.Możesz też użyć transmisji strumieniowej (np. Stringstream) lub bezpiecznej wymiany printf, takiej jak Boost Format . Rozumiem, że ta rada ma jedynie ograniczone zastosowanie (i wymaga C ++). (Zastosowaliśmy podobne podejście dostosowane do naszych potrzeb przy wdrażaniu obsługi Unicode).
Podstawowym problemem dla C jest to, że printf wykorzystujący wielokropek jest z założenia niebezpieczny - musi określić dodatkowy rozmiar argumentu na podstawie znanych argumentów, więc nie można go naprawić, aby obsługiwał „cokolwiek masz”. Jeśli więc kompilator nie zaimplementuje zastrzeżonych rozszerzeń, nie będziesz miał szczęścia.
źródło
z
modidfier Rozmiar jest standardowy C, ale niektóre libc implementacje tkwią w 1990 roku z różnych powodów (np Microsoft zasadzie opuszczonym C na rzecz C ++ i - od niedawna - C #)%zd
jest źle, jest niepodpisane, więc powinno być%zu
.Na niektórych platformach i dla niektórych typów dostępne są określone specyfikatory konwersji printf, ale czasami trzeba uciekać się do rzutowania na większe typy.
Udokumentowałem tutaj ten podchwytliwy problem na przykładowym kodzie: http://www.pixelbeat.org/programming/gcc/int_types/ i okresowo aktualizowałem go o informacje na temat nowych platform i typów.
źródło
jeśli chcesz wydrukować wartość size_t jako ciąg, możesz to zrobić:
wynikiem jest:
numer: 2337200120702199116
tekst: Chodźmy łowić ryby zamiast siedzieć na naszym ale !!
Edycja: ponowne przeczytanie pytania z powodu zmniejszonej liczby głosów zauważyłem, że jego problemem nie jest% llu lub% I64d, ale typ size_t na różnych komputerach zobacz to pytanie https://stackoverflow.com/a/918909/1755797
http: // www. cplusplus.com/reference/cstdio/printf/
size_t jest bez znaku int na maszynie 32-bitowej i bez znaku long long int na 64-bit,
ale% ll zawsze oczekuje bez znaku long long int.
size_t różni się długością w różnych systemach operacyjnych, podczas gdy% llu jest taki sam
źródło