printf("%f/n",5)的问题

本文探讨了C语言中不同类型变量(如int与float/double)混合使用时的输出行为,通过具体示例和代码实验揭示了printf函数在不同情况下如何处理类型转换及其背后的原因。
2,之所以输出0,这是计算机体系结构的问题。具体来说: printf函数不会进行任何类型转换,它只是从内存中读出你所提供的元素的值(按照%d,%f等控制字符提示的格式)。
C语言设计中,int类型一般是32bit或者16bit,而float一般是64bit,并且有可能使用科学计数保存。这点就和huhugo88所
说一样,5在内存中为00000000,00000101。而且5一般都在静态区,程序的静态存储区默认是0,那么当用%f来读时,就会读
64bit,也就是会读之前的很多位0,最后按照(有效数字)×(基数2)pow(指数)的方式来取数,自然结果是0

之所以Vc中不允许这种情况,而有些编译器就允许这么输出就是编译器设置的问题。按理说,这样访问内存是属于越界访问,
应该禁止。不过只是读,伤害性不大而已。
 
 
对于单精度浮点数(32bit),不少c语言编译系统以24位表示小数部分(包括1bit符号位),以8位表示指数部分。
 
==========================
printf("%d/n",5.01);  为什么输出一个大数?

在讲这个题目之前,预备知识,讲一下,printf函数,输入参数是读入缓冲区保存,再按照%?的格式从缓冲区中读出数据,并据此格式解释数据。

有了这个知识之后,在讲程序员面试宝典上看到一个题:

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4.     printf("%d/n",5.01);  
  5. return 0;  
  6. }  
  7. 输出结果为:188978561 

然后开始研究为什么会是这个数?

5.01是double类型,内存中占8个字节,保存在缓冲区。而%d为整型,占4个字节,printf从缓冲区中读入4字节,先读到低32位的数据。也就是说printf输出的应该是5.01以double类型保存数剧的低32位。为了检验此结果是否正确,对比5.01在内存中的表示与输出。

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4. double d = 5.01;  
  5. int *p = (int *)(&d);  
  6. int rst = 1889785610;  
  7.     printf("1).%x/n",*p);  
  8.     printf("2).%x/n",*(p+1));  
  9.     printf("3).%x/n",rst);  
  10. return 0;  
  11. }  
  12. 输出为:  
  13. 1).0x70a3d70a  
  14. 2).0x40140a3d  
  15. 3).0x70a3d70a 

这也就证明了%d输出了5.01的低32低。5.01的double类型,在内存的的表示为0x40140a3d70a3d70a。

事情看似也就完成了。

我又想,如果输入是浮点类型的5.01f,又会发生什么呢?

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4. float f = 5.01f;    
  5. int *p = (int *)(&f);       
  6.     printf("1).0x%x/n",*p);    
  7.     printf("2).0x%x/n",5.01f);    
  8. return 0;    
  9. }  
  10. 输出:  
  11. 1).0x40a051ec  
  12. 2).0x80000000 

我们发现,此时输出的并不是浮点类型5.01f的内存的表示,这是为什么呢?

然后看到一个说法,是printf会把%f按double类型输出,也就是说会把参数float型的转成double型在输出。

但现在并不是%f,当然用%f显示的是正确的结果。于是我猜测,printf是将所在float型读入的数据都自动的转化为double型了,然后%f就按double处理,而我们这是%d,所以显示的为float转化为double型后的低4字节。

验证此想法:

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4. double f = 5.01;    
  5. int *p = (int *)(&f);  
  6.     printf("1).0x%x/n",*p);  
  7.     printf("2).0x%x/n",*(p+1));    
  8.     printf("3).0x%x/n",5.01f);    
  9. return 0;    
  10. }  
  11. 输出:  
  12. 1).0x70a3d70a  
  13. 2).0x40140a3d  
  14. 3).0x80000000 

但是我们发现结果并不一样,于是我又猜想,也是许printf将float转化为double的方式与默认的方式不一样

5.01d的默认的表示为:0x40140a3d70a3d70a,在上面已经说明了

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4.     printf("0x%8x/n0x%8x/n",5.01f);  
  5. return 0;  
  6. }  
  7. 输出为:  
  8. 0x80000000  
  9. 0x40140a3d 

与是发现printf将5.01f->5.01d的表示是:0x40140a3d80000000

接着就是看这两个值是否都是为5.01了:

view plaincopy to clipboardprint?

  1. #include "stdio.h"
  2. int main(int argc, char* argv[])  
  3. {  
  4. int d1[2], d2[2];  
  5.     d1[0]=0x80000000;  
  6.     d1[1]=0x40140a3d;  
  7.     d2[0]=0x70a3d70a;  
  8.     d2[1]=0x40140a3d;  
  9. double *p1 = (double *)d1;  
  10. double *p2 = (double *)d2;  
  11.     printf("1).%f/n",*p1);  
  12.     printf("2).%f/n",*p2);  
  13. return 0;    
  14. }  
  15. 输出为:  
  16. 1).5.010000  
  17. 2).5.010000 

也就证明了0x40140a3d80000000,与0x40140a3d70a3d70a都是5.01d在机器中的表示。前者为5.01f(0x40a051ec)由printf转化为double后的表示,后者为5.01d的默认的表示。

总结:printf将输的浮点型参数全都自动转化为双精度型,且与默认的双精度的表示方法是不同的。最重要一点,printf不安全,类型不安全,要是类型不对了,也许我们就挂了^_^

### C语言 `printf` 函数 格式化字符串 用法 示例 `printf` 是 C 语言中用于格式化输出的函数,支持多种格式化选项以控制输出内容的排列和样式。以下是一些常见的格式化字符串及其用法示例: #### 基本格式 `printf` 的基本语法为: ```c printf(format_string, argument1, argument2, ...); ``` 其中,`format_string` 包含普通字符和格式说明符。格式说明符以 `%` 开头,用于指定输出的数据类型和格式。 #### 常见格式说明符 | 格式说明符 | 描述 | |------------|--------------------------| | `%d` | 输出十进制整数 | | `%f` | 输出浮点数 | | `%c` | 输出单个字符 | | `%s` | 输出字符串 | | `%x` | 输出十六进制整数 | | `%o` | 输出八进制整数 | #### 格式化选项 格式化字符串可以包含宽度、精度和其他修饰符,具体形式为: ``` %[flags][width][.precision][length]specifier ``` - **宽度 (`width`)**:指定输出字段的最小宽度。 - **精度 (`precision`)**:对于浮点数,表示小数点后的位数;对于字符串,表示最大输出长度。 - **标志 (`flags`)**:如 `-` 表示左对齐,`0` 表示用零填充等。 #### 示例代码 以下是一些 `printf` 的使用示例: ```c #include <stdio.h> int main() { int num = 42; float pi = 3.14159; char str[] = "Hello, World!"; // 普通输出 printf("Number: %d\n", num); // 输出整数 [^1] printf("Pi: %f\n", pi); // 输出浮点数 [^2] printf("String: %s\n", str); // 输出字符串 [^3] // 设置宽度 printf("Aligned: %-10d|%10d\n", num, num); // 左对齐与右对齐 [^4] // 设置精度 printf("Precision: %.2f\n", pi); // 小数点后保留两位 [^5] // 使用 * 动态设置宽度或精度 int width = 10; int precision = 2; printf("Dynamic Width: %*d\n", width, num); // 动态宽度 [^6] printf("Dynamic Precision: %.*f\n", precision, pi); // 动态精度 [^7] return 0; } ``` #### 输出结果 假设上述代码运行时,输出如下: ``` Number: 42 Pi: 3.141590 String: Hello, World! Aligned: 42 | 42 Precision: 3.14 Dynamic Width: 42 Dynamic Precision: 3.14 ``` #### 注意事项 1. 格式化字符串中的普通字符会直接输出。 2. 如果提供的参数数量或类型与格式说明符不匹配,可能导致未定义行为。 3. 使用动态宽度或精度时,`*` 会被后续参数替换,需确保参数顺序正确[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值