应该总是转换printf参数吗?

时间:2013-08-30 10:31:41

标签: c c99

前一段时间我正在寻找导致错误的数字数据被写入日志文件的bug。原来问题是代码相当于以下代码:

int main(void) {
    struct {
        double a;
        int b;
    } s = { 1, 2 };

    printf("%lf\n", s.a);
    printf("%lf\n", s.b);
}

正在输出

1.000000
1.000000

显然printf期望浮点寄存器中的第二个值,而不是堆栈中的第二个值。为了防止将来发生这样的错误,是否应该抛出所有printf参数以确保它们确实属于预期类型?

5 个答案:

答案 0 :(得分:5)

根据C99标准。 要打印的实际数据的格式说明符和数据类型不匹配为Undefined behavior

此处b是和int,因此如果您在第二个"%d"函数中提供printf()。 它将打印正确的值,否则为Behavior is Undefined

来自c99

7.19.6 9 If a conversion specification is invalid, the behavior is undefined.242) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined

答案 1 :(得分:2)

  

是否应该抛出所有printf参数以确保它们确实属于预期类型?

如果类型与预期的类型不同,如果您为编译器启用了警告,它会告诉您。

我怀疑你的行为是相信printf的模板说明符表示你想要一个值显示的样式。这是部分正确的,并且(通过铸造)可以用于主要目的,但的主要目的是指示printf正在考虑什么样的值

因此,如果你有一个整数类型,你可以使用%d,但不能使用%f,因为%f会是谎言,而且骗到计算机是一项棘手的事情。如果%f的目的是因为你想要用小数位打印的值,那么你应该把它投出来;这会将整数值正确转换为浮点数。 (您也可以使用%d.000000,因为整数没有任何分数。)

否则,您应该只使用适合该类型的说明符。

答案 2 :(得分:2)

  

应该播放所有printf参数以确保它们确实存在   期望的类型?

一般情况下。您应该为要传递的每个参数使用格式说明符和参数类型的正确组合。当然,如果你有一个“错误类型”的参数(例如你想用浮点格式说明符打印的int)那么它需要以某种方式转换,这通常意味着投。但是,在printf行上散布一大堆演员表“以防万一”在这里不是正确的解决方案。

请注意,编译器无需“理解”printf(或scanfstrftime等)的格式字符串,因此编译器只是必须通过根据一组限制的参数(float转换为double,短整数(charshort)转换为int,以及一些其他的事情)。然后归结为printf来理清你所拥有的东西。当然,如果你没有正确的参数格式规范,那么printf可能确实在错误的位置查找参数。

因此,总而言之,您需要匹配printf的参数类型。如果那意味着你偶尔需要一个演员,那么是的,使用演员。但它不应该是“常规的东西”。

答案 3 :(得分:1)

在你的代码printf中取整数2的内部二进制表示,并将其视为浮点数。

您应该确保放在cast all printf parameters to be sure that they really are of the expected type中的任何说明符都是正确的类型,而不是printf。您知道s.bint,为什么要使用%f

答案 4 :(得分:0)

老实说,如果更改数据类型 从     struct {         双倍;         int b;     } s = {1,2}; 至     struct {         双d_a;         int n_b;     } s = {1,2};

即使您不使用匈牙利表示法,只需在变量中使用某些标记即可提供类型信息。 然后当你输入printf(“%lf \ n”,s.n_b);时,很容易在这里找到陷阱。

C型演员在不同平台上有不良副作用。它们很棘手,很邋.. 如果还有其他方法可以解决问题,请不要使用类型转换。