programing

size_t 유형의 변수에 대한 교차 플랫폼 형식 문자열?

powerit 2023. 9. 15. 21:27
반응형

size_t 유형의 변수에 대한 교차 플랫폼 형식 문자열?

크로스 플랫폼 c/c++ 프로젝트(Win32, Linux, OSX)에서 *printf 함수를 사용하여 size_t 타입의 변수를 인쇄해야 합니다.어떤 환경에서는 size_t가 8바이트이고 어떤 환경에서는 4바이트입니다.glibc에서는 %zd를 사용하고 Win32에서는 %Id를 사용할 수 있습니다.이 문제를 해결할 수 있는 우아한 방법이 있을까요?

PRIuPTRmacroo(<inttype에서))에서는 h>)의 합니다.uintptr_ta, 이 할 이 할 size_t예를 들어, 자르지 않고 사용할 수 있습니다.

fprintf(stream, "Your size_t var has value %" PRIuPTR ".", (uintptr_t) your_var);

여기 정말 두 가지 질문이 있습니다.첫 번째 질문은 세 개의 플랫폼에 맞는 인쇄물 지정자 문자열이냐는 것입니다.:size_t는 부호가 없는 형식입니다.

Windows에서 "을(를) 사용합니다.%Iu".

Linux 및 OSX에서는 "을(를) 사용합니다.%zu".

두 번째 질문은 포맷 스트링과 같은 것들이 플랫폼마다 다를 수 있다는 것을 고려할 때, 어떻게 여러 플랫폼을 지원할 것인가 하는 것입니다.다른 사람들이 지적한 것처럼, 사용하는 것은#ifdef금방 못생겨집니다.

대신 대상 플랫폼별로 별도의 makefile이나 프로젝트 파일을 작성합니다.그런 다음 소스 파일의 매크로 이름을 기준으로 지정자를 참조하여 각 makefile에 매크로를 적절하게 정의합니다.특히 GCC와 Visual Studio 모두 'D' 스위치를 사용하여 명령줄에 매크로를 정의합니다.

빌드 시스템이 매우 복잡하다면(여러 빌드 옵션, 생성된 소스 등), 3개의 개별 메이크 파일을 유지하는 것이 무리일 수 있으며, CMake나 GNU 자동 도구와 같은 일종의 고급 빌드 시스템을 사용해야 합니다.그러나 기본 원리는 동일합니다. 소스 파일에 플랫폼 탐지 로직을 넣는 대신 빌드 시스템을 사용하여 플랫폼별 매크로를 정의합니다.

제가 생각할 수 있는 유일한 것은 전형적인 것입니다.

#ifdef __WIN32__ // or whatever
#define SSIZET_FMT "%ld"
#else
#define SSIZET_FMT "%zd"
#endif

그리고 나서 일정한 접힘의 이점을 활용합니다.

fprintf(stream, "Your size_t var has value " SSIZET_FMT ".", your_var);

Dan Saks는 Embedded Systems Design에 이 문제를 다룬 기사를 썼습니다.Dan에 따르면 %zu가 표준 방식이지만 이를 지원하는 컴파일러는 거의 없었습니다.대안으로 %lu를 명시적인 인수 캐스트와 함께 사용할 것을 권장했습니다.

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

사용하다boost::format. 타이프 세이프라 프린트가 가능합니다.size_t%d, 또한 당신은 기억할 필요가 없습니다.c_str()위에std::string할 때, 에게 해도,%s아니면 그 반대면 효과가 있을 겁니다

만족스러운 해결책은 없지만, size_t 항목을 문자열로 포맷하고 문자열을 인쇄하는 전문 기능을 고려해 볼 수 있습니다.

(또는 사용자가 피할 수 있다면 boost::format을 사용하면 이러한 작업을 쉽게 처리할 수 있습니다.)

저장 클래스가 가장 큰 정수 유형을 찾아서 값을 지정한 다음 큰 유형에 적합한 형식 문자열을 사용하면 됩니다.이 솔루션은 size_t뿐만 아니라 모든 유형(ptrdiff_t 등)에 적용됩니다.

당신이 사용하고 싶은 것은 uintmax_t와 macro priuMAX 포맷입니다.Visual C++의 경우 c99 호환 stdint.handinttype을 다운로드해야 합니다.마이크로소프트에서 제공하지 않기 때문입니다.

참고

http://www.embedded.com/columns/technicalinsights/204700432

이 기사는 프레데리코가 인용한 기사의 오류를 수정한 것입니다.

옵션 1(크로스 플랫폼 사용에 가장 적합하고 강력한 답변):inttypes.h을 이용하여 해킹하다PRIuPTR 지정자

에서, 는 ( 에서 의 에서 PRIuPTR(부호화되지 않은 포인터) intttypes.hprintf 형식 문자열은 다음을 유지할 수 있을 정도로 깁니다.size_t 다음 정의를 사용하여하는,을다를음다reged형,를,gs음size_t stringsprintf식다입니다 .

그러나 C 및 C++ 언어 표준에서는 이를 적용하지 않으므로 특정 아키텍처(컴파일러, 하드웨어 등)에서 이 기능이 작동하는지 확인하는 것이 중요합니다.

#include <inttypes.h>

// Custom printf format strings for `size_t` variable types, which are nearly
// always the same size as pointers on any given architecture (though this is
// NOT enforced by the standard!)
//
// `size_t` is an unsigned decimal integer (ex: usually 8 bytes on a 64-bit
// system, 4 bytes on a 32-bit system, or 2 bytes on an 8-bit system) so you
// can simply print it as though it was a pointer!:
#define PRIuSZT PRIuPTR  // u = unsigned decimal integer
#define PRIxSZT PRIxPTR  // x = unsigned decimal integer in lower-case hex
#define PRIXSZT PRIXPTR  // X = unsigned decimal integer in upper-case hex
#define PRIoSZT PRIoPTR  // o = unsigned decimal integer in octal

// The above representations make the most sense. Other representations are
// below, though these will interpret the `size_t` type as though it was
// signed, which doesn't make much sense, since it's not:
#define PRIdSZT PRIdPTR  // d = signed decimal integer
#define PRIiSZT PRIiPTR  // i = signed decimal integer

// For `ssize_t` (signed size_t) types, however, the d and i specifiers *do*
// make sense, so you could do this:
#define PRIdSSZT PRIdPTR  // d = signed decimal integer
#define PRIiSSZT PRIiPTR  // i = signed decimal integer

은 의 인 은 를 하는 을 합니다 합니다 을 하는 의 은 인 를 size_t다양한 표현을 입력합니다.

size_t my_variable = 123456789;

// print the `size_t` type as an unsigned decimal integer
printf("%" PRIuSZT "\n", my_variable); 
// print the `size_t` type as an unsigned decimal integer in lower-case hex
printf("0x%" PRIxSZT "\n", my_variable); 
// print the `size_t` type as an unsigned decimal integer in upper-case hex
printf("0X%" PRIXSZT "\n", my_variable); 
// print the `size_t` type as an unsigned decimal integer in octal
printf("0%" PRIoSZT "\n", my_variable); 

// Print the hex values again, this time with leading zero-padding to print a
// full 8-bytes (16 chars) worth of data
printf("\n");
printf("0x%016" PRIxSZT "\n", my_variable); // lower-case unsigned hex 
printf("0X%016" PRIXSZT "\n", my_variable); // upper-case unsigned hex

// Print the octal value again, this time with leading zero-padding to print a
// full 8-bytes (22 chars) worth of data
printf("\n");
printf("0%022" PRIoSZT "\n", my_variable);  // unsigned octal

64비트 Linux 시스템에서의 출력 예:

123456789
0x75bcd15
0X75BCD15
0726746425

0x00000000075bcd15
0X00000000075BCD15

00000000000000726746425

2: 2 사용: %zu그것이 뒷받침되는 곳에

로 사용하는 에서는 "Gcc"와 "STM32"가 .%z길이 지정자가 반드시 구현되는 것은 아니며, 다음과 같은 작업을 수행합니다.printf("%zu\n", my_size_t_num);단순히 문자를 출력하는 것으로 끝날지도 모릅니다.%zu(개인적으로 테스트를 해보니 사실이었습니다.) 당신의 가치 대신에size_t변수.

그러나 가능한 경우에는 그냥 사용합니다.%zu다음과 같이, "z" 길이 지정자:size_t선택사항:

enter image description here

사용 예시:

size_t my_variable = 123456789;

printf("%zu\n", my_variable);

옵션 3(이후 작동이 사실상 보장됨)uint64_t알려진 모든 것을 담을 수 있을 만큼 충분히 큽니다.size_t기존의 모든 아키텍처에 대한 types)inttypes.h PRIu64더 구체적으로, 당신을 캐스팅하는 것과 함께.size_t에 가변적인uint64_t인쇄하기 전에

그러나 모든 아키텍처에서 작동하는 것을 보장하기 위해 필요한 경우 또는 특정 아키텍처에 대해 확신할 수 없는 경우에는 주조 및 인쇄만 가능합니다.uint64_t이 작업은 모든 시스템에서 작동할 것이 사실상 보장되지만 주조 단계가 추가로 필요하기 때문에 소규모 아키텍처에서는 불필요하게 필요 이상의 크기(필요 이상의 프로세서 명령을 필요로 함)로 주조할 수 있습니다.

사용 예시:

#include <stdint.h>    // for uint64_t
#include <inttypes.h>  // for PRIu64

size_t my_variable = 123456789;

// print the `size_t` type as an unsigned decimal integer
printf("%" PRIu64 "\n", (uint64_t)my_variable);
// print the `size_t` type as an unsigned decimal integer in lower-case hex
printf("0x%" PRIx64 "\n", (uint64_t)my_variable);
// print the `size_t` type as an unsigned decimal integer in upper-case hex
printf("0X%" PRIX64 "\n", (uint64_t)my_variable);

// etc etc. See the Option 1 examples and follow the same patterns.

위의 3가지 옵션을 모두 보여주는 전체 예시 프로그램

나의 eRCaGuy_hello_world repo에서:

print_size_t.c(C와 C++에서 모두 완벽하게 실행됨):

#include <stdio.h>
#include <inttypes.h>

// Custom printf format strings for `size_t` variable types, which are nearly
// always the same size as pointers on any given architecture (though this is
// NOT enforced by the standard!)
//
// `size_t` is an unsigned decimal integer (ex: usually 8 bytes on a 64-bit
// system, 4 bytes on a 32-bit system, or 2 bytes on an 8-bit system) so you
// can simply print it as though it was a pointer!:
#define PRIuSZT PRIuPTR  // u = unsigned decimal integer
#define PRIxSZT PRIxPTR  // x = unsigned decimal integer in lower-case hex
#define PRIXSZT PRIXPTR  // X = unsigned decimal integer in upper-case hex
#define PRIoSZT PRIoPTR  // o = unsigned decimal integer in octal

// The above representations make the most sense. Other representations are
// below, though these will interpret the `size_t` type as though it was
// signed, which doesn't make much sense, since it's not:
#define PRIdSZT PRIdPTR  // d = signed decimal integer
#define PRIiSZT PRIiPTR  // i = signed decimal integer

// For `ssize_t` (signed size_t) types, however, the d and i specifiers *do*
// make sense, so you could do this:
#define PRIdSSZT PRIdPTR  // d = signed decimal integer
#define PRIiSSZT PRIiPTR  // i = signed decimal integer


int main()
{
    printf("Hello World\n");

    size_t my_variable = 123456789;
    printf("sizeof(my_variable) = %" PRIuSZT "\n", sizeof(my_variable));

    // -------------------------------------------------------------------------
    // Option 1 (best and most-robust answer for cross-platform usage):
    // `inttypes.h` hack using `PRIuPTR` type specifiers
    // -------------------------------------------------------------------------
    printf("\n===== Option 1 =====\n");

    // print the `size_t` type as an unsigned decimal integer
    printf("%" PRIuSZT "\n", my_variable);
    // print the `size_t` type as an unsigned decimal integer in lower-case hex
    printf("0x%" PRIxSZT "\n", my_variable);
    // print the `size_t` type as an unsigned decimal integer in upper-case hex
    printf("0X%" PRIXSZT "\n", my_variable);
    // print the `size_t` type as an unsigned decimal integer in octal
    printf("0%" PRIoSZT "\n", my_variable);

    // Print the hex values again, this time with leading zero-padding to print
    // a full 8-bytes (16 chars) worth of data
    printf("\n");
    printf("0x%016" PRIxSZT "\n", my_variable); // lower-case unsigned hex
    printf("0X%016" PRIXSZT "\n", my_variable); // upper-case unsigned hex

    // Print the octal value again, this time with leading zero-padding to print
    // a full 8-bytes (22 chars) worth of data
    printf("\n");
    printf("0%022" PRIoSZT "\n", my_variable);  // unsigned octal


    // -------------------------------------------------------------------------
    // Option 2: use `%zu` where it is supported
    // -------------------------------------------------------------------------
    printf("\n===== Option 2 =====\n");

    printf("%zu\n", my_variable);


    // -------------------------------------------------------------------------
    // Option 3 (virtually guaranteed to work since `uint64_t` is large enough
    // to hold all known `size_t` types on all existing architectures): use the
    // `inttypes.h` `PRIu64` specifier, with the addition of casting your
    // `size_t` variable to `uint64_t` before printing
    // -------------------------------------------------------------------------
    printf("\n===== Option 3 =====\n");

    // print the `size_t` type as an unsigned decimal integer
    printf("%" PRIu64 "\n", (uint64_t)my_variable);
    // print the `size_t` type as an unsigned decimal integer in lower-case hex
    printf("0x%" PRIx64 "\n", (uint64_t)my_variable);
    // print the `size_t` type as an unsigned decimal integer in upper-case hex
    printf("0X%" PRIX64 "\n", (uint64_t)my_variable);

    // etc etc. See the Option 1 examples and follow the same patterns.


    return 0;
}

실행 및 출력 예제:

eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 print_size_t.c -o bin/a -lm && bin/a
Hello World
sizeof(my_variable) = 8

===== Option 1 =====
123456789
0x75bcd15
0X75BCD15
0726746425

0x00000000075bcd15
0X00000000075BCD15

00000000000000726746425

===== Option 2 =====
123456789

===== Option 3 =====
123456789
0x75bcd15
0X75BCD15

출처 인용:

  1. http://www.cplusplus.com/reference/cstdio/printf/
  2. http://www.cplusplus.com/reference/cinttypes/
  3. http://www.cplusplus.com/reference/cstdint/

이 문제에 대한 제 선택은 단순히 size_t 인수를 부호 없는 긴 길이로 캐스팅하고 %lu를 모든 곳에 사용하는 것입니다. 물론 이 값이 2^32-1을 초과하지 않을 것으로 예상되는 경우에만 해당됩니다.이 길이가 너무 짧을 경우 항상 부호 없는 긴 길이로 캐스팅하여 %llu로 포맷할 수 있습니다.

어느 쪽이든, 당신의 끈은 절대 어색하지 않을 겁니다.

size_t16비트 이상의 부호 없는 유형입니다.폭이 32와 64인 경우가 많습니다.

printf("%zu\n", some_size_t_object); // Standard since C99

앞으로 가장 좋은 방법이지만 코드가 C99 이전 플랫폼으로 포팅해야 할 경우 값을 넓은 유형으로 숨겨야 합니다.unsigned long합리적인 후보인지는 몰라도 부족할 수도 있습니다.

// OK, yet insufficient with large sizes > ULONG_MAX
printf("%lu\n", (unsigned long) some_size_t_object); 

조건부 코드를 사용하거나

#ifdef ULLONG_MAX
  printf("%llu\n", (unsigned long long) some_size_t_object); 
#else
  printf("%lu\n", (unsigned long) some_size_t_object); 
#endif

마지막으로 고려해보세요.double. 다소 비효율적이지만 무어의 법칙을 고려할 때 2030년에서 2040년까지 모든 고대 및 새로운 플랫폼을 처리해야 합니다.double정확한 결과가 없을 수도 있습니다.

printf("%.0f\n", (double) some_size_t_object);

언급URL : https://stackoverflow.com/questions/174612/cross-platform-format-string-for-variables-of-type-size-t

반응형