简体   繁体   English

为什么这个代码打印0?

[英]Why is this code printing 0?

void main()
{
    clrscr();
    float f = 3.3;

    /* In printf() I intentionaly put %d format specifier to see
       what type of output I may get */
    printf("value of variable a is: %d", f);
    getch();
}

In effect, %d tells printf to look in a certain place for an integer argument.实际上, %d告诉printf在某个位置查找整数参数。 But you passed a float argument, which is put in a different place.但是你传递了一个float参数,它被放在不同的地方。 The C standard does not specify what happens when you do this. C 标准没有指定执行此操作时会发生什么。 In this case, it may be there was a zero in the place printf looked for an integer argument, so it printed “0”.在这种情况下,可能在printf寻找整数参数的地方有一个零,所以它打印了“0”。 In other circumstances, something different may happen.在其他情况下,可能会发生不同的事情。

Using an invalid format specifier to printf invokes undefined behavior .printf使用无效的格式说明符会调用未定义的行为 This is specified in section 7.21.6.1p9 of the C standard :这在C 标准的第 7.21.6.1p9 节中指定:

If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.如果转换规范无效,则行为未定义。282) 如果任何参数不是相应转换规范的正确类型,则行为未定义。

What this means is that you can't reliably predict what the output of the program will be.这意味着您无法可靠地预测程序的输出结果。 For example, the same code on my system prints -1554224520 as the value.例如,我系统上的相同代码打印 -1554224520 作为值。

As to what's most likely happening, the %d format specifier is looking for an int as a parameter.至于最有可能发生的情况, %d格式说明符正在寻找一个int作为参数。 Assuming that an int is passed on the stack and that an int is 4 bytes long, the printf function looks at the next 4 bytes on the stack for the value given.假设一个int被堆栈和一个通过int是4个字节长,则printf功能着眼于堆栈中给出的值上的下一个4个字节。 Many implementations don't pass floating point values on the stack but in registers instead, so it instead sees whatever garbage values happen to be there.许多实现不会在堆栈上传递浮点值,而是在寄存器中传递,因此它会看到碰巧在那里的任何垃圾值。 Even if a float is passed on the stack, a float and an int have very different representations, so printing the bytes of a float as an int will most likely not give you the same value.即使在堆栈上传递floatfloatint具有非常不同的表示形式,因此将float的字节打印为int很可能不会为您提供相同的值。

Let's look at a different example for a moment.让我们看一个不同的例子。 Suppose I write假设我写

#include <string.h>

char buf[10];
float f = 3.3;
memset(buf, 'x', f);

The third argument to memset is supposed to be an integer (actually a value of type size_t ) telling memset how many characters of buf to set to 'x' . memset的第三个参数应该是一个整数(实际上是一个size_t类型的值),告诉memset要将多少个buf字符设置为'x' But I passed a float value instead.但是我传递了一个float值。 What happens?发生什么了? Well, the compiler knows that the third argument is supposed to be an integer, so it automatically performs the appropriate conversion, and the code ends up setting the first three (three point zero ) characters of buf to 'x' .好吧,编译器知道第三个参数应该是一个整数,所以它会自动执行适当的转换,代码最终将buf的前三个(三个点)字符设置为'x'

(Significantly, the way the compiler knew that the third argument of memset was supposed to be an integer was based on the prototype function declaration for memset which is part of the header <string.h> .) (重要的是,编译器知道memset的第三个参数应该是整数的方式是基于memset原型函数声明,它是头文件<string.h> 。)

Now, when you called现在,当你打电话

printf("value of variable f is: %d", f);

you might think the same thing happens.你可能认为同样的事情会发生。 You passed a float , but %d expects an int , so an automatic conversion will happen, right?您传递了一个float ,但%d需要一个int ,因此会发生自动转换,对吗?

Wrong.错误的。 Let me say that again: Wrong .让我再说一遍:错了

The perhaps surprising fact is, printf is different.也许令人惊讶的事实是, printf是不同的。 printf is special. printf很特别。 The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be, because it depends on the details of the % -specifiers buried in the format string.编译器不一定知道传递给printf的参数的正确类型应该是什么,因为这取决于隐藏在格式字符串中的%说明符的详细信息。 So there are no automatic conversions to just the right type.所以没有自动转换到正确的类型。 It's your job to make sure that the types of the arguments you actually pass are exactly right for the format specifiers.您的工作是确保您实际传递的参数类型完全适合格式说明符。 If they don't match, the compiler does not automatically perform corresponding conversions.如果它们不匹配,编译器不会自动执行相应的转换。 If they don't match, what happens is that you get crazy, wrong results.如果它们不匹配,就会发生疯狂的错误结果。

(What does the prototype function declaration for printf look like? It literally looks like this: extern int printf(const char *, ...); . Those three dots ... indicate a variable-length argument list or "varargs", they tell the compiler it can't know how many more arguments there are, or what their types are supposed to be. So the compiler performs a few basic conversions -- such as upconverting types char and short int to int , and float to double -- and leaves it at that.) printf的原型函数声明是什么样的?字面上看起来像这样: extern int printf(const char *, ...); 。这三个点...表示可变长度参数列表或“varargs”,它们告诉编译器它不知道还有多少参数,或者它们的类型应该是什么。所以编译器执行一些基本的转换——比如将类型charshort int上转换为int ,并将floatdouble ——然后就这样了。)

I said "The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be", but these days, good compilers go the extra mile and try to figure it out anyway, if they can.我说“编译器不一定知道传递给printf的参数的正确类型应该是什么”,但是现在,好的编译器会加倍努力并尝试弄清楚,如果可以的话。 They still won't perform automatic conversions (they're not really allowed to, by the rules of the language), but they can at least warn you.他们仍然不会执行自动转换(根据语言规则,他们实际上不被允许这样做),但他们至少可以警告您。 For example, I tried your code under two different compilers.例如,我在两个不同的编译器下尝试了您的代码。 Both said something along the lines of warning: format specifies type 'int' but the argument has type 'float' .两者都说了一些warning: format specifies type 'int' but the argument has type 'float' If your compiler isn't giving you warnings like these, I encourage you to find out if those warnings can be enabled, or consider switching to a better compiler.如果您的编译器没有给您这样的警告,我鼓励您查明是否可以启用这些警告,或者考虑切换到更好的编译器。

Try尝试

printf("... %f",f); 

That's how you print float numbers.这就是您打印浮点数的方式 Maybe you only want to print x digits of f , eg.:也许您只想打印f 的x 位数字,例如:

printf("... %.3f" f);

That will print your float number with 3 digits after the dot.这将在点后打印 3 位数字的浮点数。 Please read through this list:请通读此列表:

%c - Character %c - 字符

%d or %i - Signed decimal integer %d 或 %i - 有符号十进制整数

%e - Scientific notation (mantissa/exponent) using e character %e - 使用 e 字符的科学记数法(尾数/指数)

%E - Scientific notation (mantissa/exponent) using E character %E - 使用 E 字符的科学记数法(尾数/指数)

%f - Decimal floating point %f - 十进制浮点数

%g - Uses the shorter of %e or %f %g - 使用 %e 或 %f 中的较短者

%G - Uses the shorter of %E or %f %G - 使用 %E 或 %f 中的较短者

%o - Signed octal %o - 有符号八进制

%s - String of characters %s - 字符串

%u - Unsigned decimal integer %u - 无符号十进制整数

%x - Unsigned hexadecimal integer %x - 无符号十六进制整数

%X - Unsigned hexadecimal integer (capital letters) %X - 无符号十六进制整数(大写字母)

%p - Pointer address %p - 指针地址

%n - Nothing printed %n - 未打印任何内容

The code is printing a 0, because you are using the format tag %d, which represents Signed decimal integer ( http://devdocs.io ).代码打印 0,因为您使用的是格式标记 %d,它表示有符号十进制整数 ( http://devdocs.io )。

Could you please try你能试试吗

void main() {

   clrscr();
   float f=3.3;

   /* In printf() I intentionaly put %d format specifier to see what type of output I may get */

   printf("value of variable a is: %f",f); 
   getch();

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM