0xAA55 发表于 2019-3-27 04:51:59

【C】在不同的平台使用各种printf来打印size_t类型

size_t类型在stddef.h里面被定义,用于表示“长度”。它在32位的机器上是一个32位的无符号整数类型,而在64位的机器上则是64位的无符号整数类型,很多时候使用起来都很方便,具备跨位数兼容的可移植性,而且不用担心速度问题(在32位机器上使用64位整数比使用32位整数慢一些),十分优雅。

但由于size_t的长度是随位数变化的,如何才能优雅地使用printf来打印它呢?对于GCC编译器,使用 %zu 就可以打印size_t类型的参数,或者用scanf来获取一个size_t类型的参数的值。但对于Visual Studio 2015以前(不包括2015)带的MSVC的各种printf则不支持%zu。对于这些版本,你应该使用 %Iu 。

由于各个平台的printf对于格式字符的定义略有差异,C99推出了inttypes.h标准库头文件标准。你可以使用PRI或者SCN开头的宏来设置你想要打印的东西,而且它具有一定的规律,一般都能被猜中。它给每一个stdint.h里面定义的xintyy_t类型的整数都给出了对应的PRI或SCN版本的宏,比如PRIi64可以用于int64_t的打印,PRIu32可以用于uint32_t的打印。看下例代码:#include<inttypes.h>
#include<stdio.h>

int main()
{
        int32_t a = 1234;
        uint64_t b = 5678;
        printf("a = %"PRIi32" and b = %"PRIu64".\n", a, b);
        return 0;
}会正确输出“a = 1234 and b = 5678.[换行]
”。

然而不幸的是,VS直到2012,都没有inttypes.h这个头文件,直到VS2013才加入它。而且用VS2017打开VS2012的工程也不能用,除非升级工程。此时我们可以从这个帖子里拿一份谷歌提供的针对MSVC的inttypes.h头文件。不过我觉得比起把它添加到自己的MSVC的includes里面,不如把它存为msc_inttypes.h然后放到自己的工程里面,再使用宏来判断是包含它还是包含<inttypes.h>比较好。

话说回来,inttypes.h里面并没有包含针对size_t的PRI和SCN的版本。虽说并不配套,但我还是想自己造一个“PRIsize_t”来用于打印size_t。至于为什么要这么做……我才不会告诉你我喜欢把for循环的那个i定义为size_t或者ptrdiff_t。

所以我会在我的工程的配置头文件里加上这么一堆:#if defined(_MSC_VER)
#include"msc_inttypes.h"
#if _MSC_VER >= 1800
#    define PRIsize_t "zu"
#else
#    define PRIsize_t "Iu"
#endif
#else
#include<inttypes.h>
#define PRIsize_t "zu"
#endif并复制一份msc_inttypes.h进去。这份msc_inttypes.h我是不会去动它的了,所以就到处复制了。

作为对比,我的数学库mathutil则要做成一个单独的repository(注意不是resporitory),然后被别的库引用头文件和静态库。一方面我可以随时更新mathutil,只要既有接口不变动,基本上都是能兼容各种引用它的工程的,除非行为上变动很大。另一方面,静态库允许进行链接时间优化,可以避免很多优化上的问题。

参考资料:
https://en.cppreference.com/w/c/types/integer
https://devblogs.microsoft.com/cppblog/whats-new-for-visual-c-developers-in-vs2013-preview/
https://www.0xaa55.com/thread-25686-1-1.html
https://github.com/0xAA55/mathutil

Golden Blonde 发表于 2019-3-27 05:30:52

我直接用%p
页: [1]
查看完整版本: 【C】在不同的平台使用各种printf来打印size_t类型