printf 패밀리를 사용하여 size_t 변수를 이식 가능하게 인쇄하는 방법은 무엇입니까?


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 longlibc 구현이 z수정자를 지원하지 않는 경우 캐스팅 은 최선의 옵션입니다 . C99 표준 size_t은보다 큰 정수 변환 등급을 가지지 말 것을 권장 long하므로 합리적으로 안전합니다
Christoph



1
Windows 플랫폼에서 size_t는 long보다 클 수 있습니다. 호환성을 위해 long은 항상 32 비트이지만 size_t는 64 비트 일 수 있습니다. 따라서 부호없는 long으로 캐스트하면 비트의 절반이 손실 될 수 있습니다. 죄송합니다 :-)
Bruce Dawson

답변:


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이 없습니까)?
avakar

6
C99 추가이며 printf()2009-11-09의 C ++ 0x 초안 길이 수정 자 목록에 포함되지 않았습니다 (672 페이지의 표 84)
Christoph

3
@Christoph : 최신 초안 인 n3035에도 없습니다.
GManNickG

11
@avakar @Adam Rosenfield @Christoph @GMan : 그러나 n3035 §1.2 규범 참조에서는 C99 표준 만 참조되며 동일한 상태의 §17.6.1.2 / 3 "C 표준 라이브러리의 기능이 제공됩니다." 달리 명시하지 않는 한 C99 표준 라이브러리의 모든 것은 C99의 추가 형식 지정자를 포함하여 C ++ 0x 표준 라이브러리의 일부임을 의미합니다.
James McNellis

9
@ ArunSaha : C ++이 아닌 C99의 기능입니다. 로 컴파일 -pedantic하려면 C ++ 1x 초안을 지원하는 컴파일러를 얻거나 코드를 C99로 컴파일 된 파일로 이동해야합니다. 그렇지 않으면, 유일한 옵션은 변수를 캐스트 unsigned long long하고 %llu최대한 이식 하기 위해 사용 하는 것입니다.
Adam Rosenfield 1

88

사용중인 컴파일러 (블리치)에 따라 다릅니다.

... 물론 C ++를 사용 하는 경우 AraK에서 제안한cout 대로 대신 사용할 수 있습니다 .


3
z또한 newlib (예 : cygwin)
Christoph

7
%zd에 대해 잘못되었습니다 size_t. 에 해당 하는 부호있는 유형에 size_t대해서는 맞지만 size_t자체는 부호없는 유형입니다.
Keith Thompson

1
@ KeithThompson : 나는 %zu또한 언급 했다 (그리고 %zx그들이 16 진수를 원한다면). 진정한 충분히 %zu아마 목록에 처음으로되어 있어야합니다. 결정된.
TJ Crowder

10
@ TJCrowder : 나는 %zd목록에 전혀 있어야 한다고 생각하지 않습니다 . 나는 가치 를 인쇄 하기 %zd보다는 사용해야 할 이유를 생각할 수 없다 . 값이을 초과하면 유효하지 않습니다 (정의되지 않은 동작이 있음) . (완벽 성을 위해 8 진법을 언급 할 수 있습니다 .)%zusize_tSIZE_MAX / 2%zo
Keith Thompson

2
@FUZxxl : POSIX에서는에 ssize_t해당하는 부호있는 유형이 필요하지 않으므로 size_t일치하지 않을 수 "%zd"있습니다. ( 아마도 대부분의 구현에 있을 것 입니다.) pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Keith Thompson

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-복원 Monica Monica

8
이러지 마십시오. size_t가 64 비트이고 long이 32 비트 인 64 비트 Windows에서는 실패합니다.
Yttrill

1
@Yttrill : 그렇다면 64 비트 윈도우에 대한 답은 무엇입니까?
John Bode

1
@JohnBode 아마도 unsigned long long?
James Ko

2
또는 : a로 캐스팅 uint64_t한 다음 PRIu64형식 지정자를 포함하는 inttypes.h 의 매크로 를 사용할 수 있습니다.
James Ko

9

Windows에 대한 Adam Rosenfield의 답변을 확장합니다.

VS2013 업데이트 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에 의해 생성 된 것은 말합니다 :

ZZ
ZD
ZD

참고 : ssize_tPOSIX 확장이며 Windows Data TypesSSIZE_T 와 비슷한 것이므로 참조를 추가했습니다 .<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와 동일한 결과를 제공합니다.
Nathan Kidd

6

C99 확장을 반드시 지원하지 않는 C ++에서이 작업을 수행하는 사람들은 boost :: format을 진심으로 권장합니다. 이것은 size_t 유형의 크기 질문을 혼란스럽게 만듭니다.

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

boost :: format에는 크기 지정자가 필요하지 않으므로 값을 표시하려는 방법에 대해서만 걱정할 수 있습니다.


4
아마 %u그때를 원할 것 입니다.
GManNickG

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

8
네,하지만 질문자는 구체적으로 printf지정자를 요청 합니다. 나는 그들이 std::cout문제를 사용하게 만드는 다른 언급되지 않은 제약 조건이 있다고 생각합니다 .
Donal Fellows

1
@Donal C ++ 스트림이 C ++ 프로젝트에서 어떤 종류의 문제를 일으킬 수 있는지 궁금합니다!
AraK

9
@ 아라크. 그들은 매우 느리다? 별다른 이유없이 많은 바이트를 추가합니다. ArunSaha는 자신의 개인 지식을 알고 싶어합니까? 개인적 취향 (나는 stdio를 fstream보다 선호한다). 여러 가지 이유가 있습니다.
KitsuneYMG

1
@TKCrowder : 원래 요청에 따르면 C 태그를 통해 C 솔루션을 원했고 출력 카탈로그 디스크립터에서 출력 형식 디스크립터를 가져 오는 경우와 같이 C ++에서 스트림을 사용하지 않는 것이 좋습니다. (원하는 경우 메시지 파서를 작성하고 스트림을 사용할 수 있지만 기존 코드를 활용하면 많은 작업이 가능합니다.)
Donal Fellows

1
@Donal : 태그는 C와 C ++입니다. 나는 어떤 식 으로든 C ++의 I / O 스트림을 옹호하지 않고 (나는 그것을 좋아하지 않는다 ), 그 질문 원래 * "... printf지정자에 대한 사양을 요구 하지 않았다 "고 지적했다 .
TJ Crowder


2

AraK가 말했듯이 c ++ 스트림 인터페이스는 항상 이식 가능하게 작동합니다.

std :: size_t s = 1024; std :: cout << s; // 또는 stringstream과 같은 다른 종류의 스트림!

C stdio를 원한다면 특정 "휴대용"사례에 대한 휴대용 답변이 없습니다. 보시다시피 잘못된 형식 플래그를 선택하면 컴파일러 경고가 발생하거나 출력이 잘못 될 수 있습니다.

C99는 "%"PRIdMAX "\ n"과 같은 inttypes.h 형식으로이 문제를 해결하려고했습니다. 그러나 "% zu"와 마찬가지로 모든 사람이 c99 (2013 이전의 MSVS)를 지원하는 것은 아닙니다. 이 문제를 해결하기 위해 "msinttypes.h"파일이 떠 있습니다.

다른 유형으로 캐스트하는 경우 플래그에 따라 잘림 또는 부호 변경에 대한 컴파일러 경고가 표시 될 수 있습니다. 이 경로를 사용하면 더 큰 관련 고정 크기 유형을 선택하십시오. 부호없는 long long 및 "% llu"또는 부호없는 long "% lu"중 하나가 작동해야하지만 llu는 32 비트 세계에서 속도가 너무 느려질 수도 있습니다. (편집-내 Mac은 % lu, % llu 및 size_t가 모두 같은 크기인데도 % llu가 size_t와 일치하지 않음에 대해 64 비트로 경고를 표시합니다. MSVS2012에서 % lu와 % llu는 같은 크기가 아닙니다. 캐스팅하고 일치하는 형식을 사용해야 할 수도 있습니다.)

이를 위해 int64_t와 같은 고정 크기 유형을 사용할 수 있습니다. 하지만 기다려! 이제 c99 / c ++ 11로 돌아가고 이전 MSVS가 다시 실패합니다. 또한 캐스트가 있습니다 (예 : map.size ()는 고정 크기 유형이 아닙니다)!

부스트와 같은 타사 헤더 또는 라이브러리를 사용할 수 있습니다. 아직 사용하고 있지 않다면 프로젝트를 그런 식으로 팽창시키고 싶지 않을 수 있습니다. 이 문제에 대해서만 하나를 추가하려는 경우 c ++ 스트림 또는 조건부 컴파일을 사용하지 않는 이유는 무엇입니까?

따라서 C ++ 스트림, 조건부 컴파일, 타사 프레임 워크 또는 휴대용으로 작동합니다.


-1

부호없는 32 비트 정수를 % lu 형식으로 전달하면 경고합니까? 변환이 잘 정의되어 있고 정보를 잃지 않기 때문에 괜찮습니다.

일부 플랫폼 <inttypes.h>은 형식 문자열 리터럴에 삽입 할 수 있다는 점 에서 매크로를 정의한다고 들었지만 Windows C ++ 컴파일러에서 해당 헤더를 볼 수 없으므로 크로스 플랫폼이 아닐 수도 있습니다.


1
printf에 잘못된 크기의 것을 전달하면 대부분의 컴파일러는 경고하지 않습니다. GCC는 예외입니다. inttypes.h는 C99에 정의되었으므로 C99를 준수하는 모든 C 컴파일러가이를 갖습니다. 여전히 컴파일러 플래그를 사용하여 C99를 켜야 할 수도 있습니다. 어쨌든 intttypes.h는 size_t 또는 ptrdiff_t에 대해 특정 형식을 정의하지 않습니다. 왜냐하면 각각 크기 지정자를 'z'와 't'로 가져 오기에 충분히 중요하다고 결정했기 때문입니다.
swestrup

를 사용하는 경우 값을로 %lu캐스팅해야 size_t합니다 unsigned long. 에 대한 인수에 대한 암시 적 변환 (프로모션 제외)은 없습니다 printf.
Keith Thompson

-2

C99는 "% zd"등을 정의합니다. C ++에는 이식 가능한 형식 지정자가 없습니다 .이 두 시나리오에서 단어를 사용할 %p 는 있지만 이식 가능한 선택은 아니며 16 진수로 값을 제공하는을 사용할 수 있습니다.

또는 일부 스트리밍 (예 : 문자열 스트림) 또는 Boost Format 과 같은 안전한 printf 교체를 사용하십시오 . 이 조언은 제한적이며 C ++이 필요하다는 것을 알고 있습니다. (우리는 유니 코드 지원을 구현할 때 우리의 요구에 맞는 유사한 접근법을 사용했습니다.)

C의 근본적인 문제는 줄임표를 사용하는 printf가 설계 상 안전하지 않다는 것입니다. 알려진 인수에서 추가 인수의 크기를 결정해야하므로 "무엇을 얻었는지"지원하도록 수정할 수 없습니다. 따라서 컴파일러가 독점적 인 확장을 구현하지 않으면 운이 없습니다.


2
z크기 modidfier 표준 C이지만, 일부 libc의 구현은 여러 가지 이유로 1990 년에 붙어있는 (C #을 C 찬성 예를 들어, 마이크로 소프트 기본적으로 포기 C ++와 - - 최근)
크리스토프

3
C99는 크기 지정자 'z'를 size_t 값의 크기로 정의하고 't'를 ptrdiff_t 값의 크기로 정의했습니다.
swestrup

2
%zd잘못되었으므로 서명되지 않으므로이어야합니다 %zu.
David Conrad

-3

일부 플랫폼 및 일부 유형에는 사용 가능한 특정 printf 변환 지정자가 있지만 때로는 더 큰 유형으로 캐스트해야합니다.

이 까다로운 문제를 예제 코드 : http://www.pixelbeat.org/programming/gcc/int_types/ 와 함께 여기에 문서화 하고 새로운 플랫폼 및 유형에 대한 정보로 주기적으로 업데이트하십시오.


1
링크 전용 답변은 사용하지 않는 것이 좋습니다. 따라서 SO 답변은 솔루션 검색의 종점이어야합니다 (단, 시간이 지남에 따라 또 다른 참조 중단). 링크를 참조로 유지하면서 여기에 독립형 시놉시스를 추가하십시오.
kleopatra

-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는 32 비트 시스템에서 부호없는 int이고 64 비트
에서는 부호없는 long long int 이지만 % ll은 항상 부호없는 long long int를 기대합니다.

size_t는 운영 체제마다 길이가 다르지만 % llu는 동일합니다


4
이건 말도 안돼!
Antti Haapala

char 배열의 첫 8 바이트를 size_t 포인터를 통해 부호없는 long long 64bit로 캐스팅하고 printf % I64d를 사용하여 숫자로 인쇄하십시오. 그것은 질문의 범위에 있지 않습니다.
Andre
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.