101

我有一些 C++ 代码打印size_t

size_t a;
printf("%lu", a);

我希望在 32 位和 64 位架构上编译时不会出现警告。

如果这是 C99,我可以使用printf("%z", a);. 但是 AFAICT%z不存在于任何标准的 C++ 方言中。所以相反,我必须做

printf("%lu", (unsigned long) a);

这真的很难看。

如果语言中没有size_t内置打印 s 的工具,我想知道是否可以编写一个 printf 包装器或类似的东西,size_t以便在 s 上插入适当的强制转换,以消除虚假的编译器警告,同时仍然保持良好的警告。

有任何想法吗?


编辑澄清我为什么使用 printf:我有一个相对较大的代码库,我正在清理。它使用 printf 包装器来执行诸如“写入警告,将其记录到文件,并可能以错误退出代码”之类的操作。我可能能够收集足够的 C++-foo 来使用 cout 包装器来执行此操作,但我不想更改程序中的每个 warn() 调用只是为了摆脱一些编译器警告。

4

9 回答 9

71

printf格式说明符在%zuC++ 系统上可以正常工作;没有必要让它更复杂。

于 2009-11-03T18:28:50.410 回答
61

大多数编译器都有自己的说明符size_tptrdiff_t参数,例如 Visual C++ 分别使用 %Iu 和 %Id,我认为 gcc 将允许您使用 %zu 和 %zd。

您可以创建一个宏:

#if defined(_MSC_VER) || defined(__MINGW32__) //__MINGW32__ should goes before __GNUC__
  #define JL_SIZE_T_SPECIFIER    "%Iu"
  #define JL_SSIZE_T_SPECIFIER   "%Id"
  #define JL_PTRDIFF_T_SPECIFIER "%Id"
#elif defined(__GNUC__)
  #define JL_SIZE_T_SPECIFIER    "%zu"
  #define JL_SSIZE_T_SPECIFIER   "%zd"
  #define JL_PTRDIFF_T_SPECIFIER "%zd"
#else
  // TODO figure out which to use.
  #if NUMBITS == 32
    #define JL_SIZE_T_SPECIFIER    something_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_signed
    #define JL_PTRDIFF_T_SPECIFIER something_signed
  #else
    #define JL_SIZE_T_SPECIFIER    something_bigger_unsigned
    #define JL_SSIZE_T_SPECIFIER   something_bigger_signed
    #define JL_PTRDIFF_T_SPECIFIER something-bigger_signed
  #endif
#endif

用法:

size_t a;
printf(JL_SIZE_T_SPECIFIER, a);
printf("The size of a is " JL_SIZE_T_SPECIFIER " bytes", a);
于 2009-11-03T18:26:55.423 回答
20

C++11

C++11 导入 C99,因此std::printf应该支持 C99%zu格式说明符。

C++98

在大多数平台上,size_t并且uintptr_t是等效的,在这种情况下,您可以使用PRIuPTR定义的宏<cinttypes>

size_t a = 42;
printf("If the answer is %" PRIuPTR " then what is the question?\n", a);

如果您真的想安全,请投射uintmax_t并使用PRIuMAX

printf("If the answer is %" PRIuMAX " then what is the question?\n", static_cast<uintmax_t>(a));
于 2014-06-21T13:32:55.903 回答
16

在 windows 和 printf 的 Visual Studio 实现

 %Iu

为我工作。见 msdn

于 2011-02-25T14:57:01.760 回答
12

既然您使用的是 C++,为什么不使用 IOStreams?只要您不使用没有定义operator <<for的脑死 C++ 实现,它就应该在没有警告的情况下编译并执行正确的类型感知操作size_t

当实际输出必须使用 完成时printf(),您仍然可以将其与 IOStreams 结合使用以获得类型安全的行为:

size_t foo = bar;
ostringstream os;
os << foo;
printf("%s", os.str().c_str());

它不是超级高效,但是您上面的案例处理文件 I/O,所以这是您的瓶颈,而不是这个字符串格式化代码。

于 2009-10-10T01:40:30.703 回答
8

fmt 库提供了一种快速可移植(且安全)的实现,printf包括以下z修饰符size_t

#include "fmt/printf.h"

size_t a = 42;

int main() {
  fmt::printf("%zu", a);
}

除此之外,它还支持类似 Python 的格式字符串语法并捕获类型信息,因此您不必手动提供它:

fmt::print("{}", a);

它已经过主要编译器的测试,并提供跨平台的一致输出。

免责声明:我是这个库的作者。

于 2015-10-27T03:11:25.800 回答
7

这是一个可能的解决方案,但它不是一个漂亮的..

template< class T >
struct GetPrintfID
{
  static const char* id;
};

template< class T >
const char* GetPrintfID< T >::id = "%u";


template<>
struct GetPrintfID< unsigned long long > //or whatever the 64bit unsigned is called..
{
  static const char* id;
};

const char* GetPrintfID< unsigned long long >::id = "%lu";

//should be repeated for any type size_t can ever have


printf( GetPrintfID< size_t >::id, sizeof( x ) );
于 2009-10-10T09:11:35.980 回答
3

底层size_t 的有效类型取决于实现。C 标准将其定义为 sizeof 运算符返回的类型;除了无符号和一种整数类型之外,size_t 几乎可以是任何大小可以容纳 sizeof() 预期返回的最大值的任何东西。

因此,用于 size_t 的格式字符串可能因服务器而异。它应该总是有“u”,但可能是 l 或 d 或者其他东西......

一个技巧可能是将其转换为机器上最大的整数类型,确保转换中不会丢失,然后使用与此已知类型关联的格式字符串。

于 2009-10-10T01:48:37.600 回答
0
#include <cstdio>
#include <string>
#include <type_traits>

namespace my{
    template<typename ty>
    auto get_string(ty&& arg){
        using rty=typename::std::decay_t<::std::add_const_t<ty>>;
        if constexpr(::std::is_same_v<char, rty>)
            return ::std::string{1,arg};
        else if constexpr(::std::is_same_v<bool, rty>)
            return ::std::string(arg?"true":"false");
        else if constexpr(::std::is_same_v<char const*, rty>)
            return ::std::string{arg};
        else if constexpr(::std::is_same_v<::std::string, rty>)
            return ::std::forward<ty&&>(arg);
        else
            return ::std::to_string(arg);
    };

    template<typename T1, typename ... Args>
    auto printf(T1&& a1, Args&&...arg){
        auto str{(get_string(a1)+ ... + get_string(arg))};
        return ::std::printf(str.c_str());
    };
};

稍后在代码中:

my::printf("test ", 1, '\t', 2.0);
于 2020-03-17T18:46:42.477 回答