【问题标题】:C++ printf field width specifier ‘.*’ expects int not size_tC++ printf 字段宽度说明符‘.*’期望 int 不是 size_t
【发布时间】:2016-06-11 01:37:07
【问题描述】:

我正在修复我继承的遗留项目中的编译器警告。新编译器是 gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)。

他们有很多类似下面的代码:

#include <cstdio>
#include <cstring>

struct foobar
{
    char field1[10];
    char field2[5];
};

int main()
{
    struct foobar foo;
    memset(&foo, ' ', sizeof(foo));
    strncpy(foo.field1, "1234567890", sizeof(foo.field1));

    // Produces warning
    printf("[%.*s]", sizeof(foo.field1), foo.field1);

    return 0;
}

这会产生警告消息:

1_test.c: In function ‘int main()’:
1_test.c:16:49: warning: field precision specifier ‘.*’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat=]
  printf("[%.*s]", sizeof(foo.field1), foo.field1);

这对我来说似乎是错误的,因为 '.*' 应该期望 size_t 但显然它不会......

除了必须执行以下操作之外,还有什么方法可以全局解决此问题:

    // Fixes
    printf("[%.10s]", foo.field1);

    // Fixes
    printf("[%.*s]", static_cast<int>(sizeof(foo.field1)), foo.field1);

【问题讨论】:

  • 我正要说“使用static_cast”,但你是对的,这是一个丑陋的解决方案。
  • @Slava 它不是 C++,但它是它的一部分
  • @KABoissonneault 上面哪部分代码使它成为 C++ 而不是 C?
  • @Slava 'C' 什么时候得到 MessageBox 不是 C++,但这并不意味着 Windows 用户在使用 Microsoft 的库时不能问 C++ 问题。最后,printf 本身在 C++ 标准中被多次使用,这是否使 C++ 标准不是 C++?
  • printf() 中的功能早于size_t 作为类型的存在;这就是为什么它使用int 而不是size_t。 (标准 I/O 库是 70 年代后期的一个特性——例如,在第 7 版 UNIX 中。在设计标准 I/O 库时,size_t 类型不是 C 或 C 支持库的一部分,所以该类型无法使用。)

标签: c++ printf


【解决方案1】:

正确的解决方案是:

std::cout << std::string( foo.field1, sizeof( foo.field1 ) );

这会输出你想要的并且不会产生任何警告。但是更好的解决方案当然是在struct foobar中使用std::string

【讨论】:

    【解决方案2】:

    你总是有一些不好玩但有时是最好的解决方案来包装宏

    #define INT_SIZEOF( x ) static_cast< int >( sizeof( (x) ) )
    
    printf("[%.*s]", INT_SIZEOF(foo.field1), foo.field1);
    

    如果你的代码库对宏过敏,你可以为 static_cast 做一个简单的语法包装,像这样

    int AsInt(size_t n) { return static_cast<int>( n ) }
    printf("[%.*s]", AsInt(sizeof(foo.field1)), foo.field1);
    

    正如 cmets 中所讨论的,此解决方案只能采用大小值,因此需要单独的 sizeof 运算符。另外,如果sizeof 没有返回适合整数的值(尽管可能性很小),您将失去任何编译时错误检查的机会。

    请注意,在这两种情况下,您的代码仍然很丑陋,但至少有一个更短,并通知读者正在发生一些丑陋的事情。

    最后,另一种可能的不错的选择。

    // constexpr is optional, you simply won't be able to use it in as many places if you don't use it
    template<typename T> constexpr int intSizeof( T = T{} /*replace with () if necessary*/ ) 
    { 
        return static_cast<int>( sizeof( T ) ); 
    }
    
    printf("[%.*s]", intSizeof<decltype(foo.field1)>(), foo.field1);
    

    这个解决方案绝对是更多的 C++ 和更现代的,没有实际成本

    【讨论】:

    • 这怎么会是“最佳解决方案”?宏在这里做了哪些函数不能做的事情?
    • @CodyGray 在编译时评估。另外,宏可以同时接受类型和表达式,而函数或类型特征只能接受其中任何一个。 (是的,函数和类型特征也可以在某些条件下评估编译时间)
    • 如果您使用的编译器无法内联包含静态转换和 sizeof 运算符的函数,那么您基本上应该放弃。您没有使用宏在这里传递类型,而是使用它来传递值,所以我看不出它是如何应用的......?
    • 我没有在当前用例中将类型传递给它,但可能会有更多用例。不过,我仍然可以展示您的解决方案,只是好不了多少。
    • @KABoissonneault 您可以将类型传递给模板函数
    猜你喜欢
    • 1970-01-01
    • 2011-03-11
    • 2013-03-14
    • 2011-02-17
    • 1970-01-01
    • 2018-05-23
    • 2013-10-09
    • 2013-05-26
    相关资源
    最近更新 更多