printf学习笔记(你真的了解printf函数吗)

最近几天浅略地看了Andrew Koenig的著作《C陷阱与缺陷》,虽然写于上世纪80年代,但里面的重要思想依然熠熠生辉,附录里对printf函数的深入剖析,确实使我受益良多,遂写下这篇学习笔记。

首先看一下printf函数的官方定义:

printf 是指格式化输出函数,主要功能是向标准输出设备按规定格式输出信息。printf 是C语言标准库函数,定义于头文件 <stdio.h>。printf 函数的一般调用格式为:printf("<格式化字符串>", <参量表>)。输出的字符串除了可以是字母、数字、空格和一些数字符号以外,还可以使用一些转义字符表示特殊的含义。

我们当然会调用它:

printf("hello world\n");

它的作用是向标准输出设备(stdout)输出字符串"hello world\n",这里的标准输出设备,对我们来说主要是显示屏,进一步地,就是所谓的控制台。

当然,今天我们讲的是printf函数难以察觉的一些细节,相信看完你会对printf函数有一个更深的理解。

以下测试环境均为vs2022

1.printf的同族函数fprintf

printf函数把数据写到标准输出,而fprintf函数则可以把数据写到任何文件中,所以它的第一个参数必须是一个文件指针。

例如:

fprintf(stdout,"hello world\n");//向标准输出写入字符串
fprintf(stderr,"error\n");//向标准错误打印错误消息

其中,以下二者在意义上等价:

fprintf(stdout,"hello world\n");
printf("hello world\n");

 因为在C语言中,printf函数的默认输出是标准输出流。

2.printf的同族函数sprintf

sprintf函数因为不安全被vs2022禁止使用,下面介绍它的安全形式:sprintf_s

其函数原型为:int sprintf_s(char *buffer,size_t sizeOfBuffer,const char *format,[argument] ... ); 

通过指定缓存区长度sizeOfBuffer确保函数使用安全。

char s[20] = { 0 };
sprintf_s(s, sizeof(s), "%d %.1f\n", 100, 0.1);
printf("%s\n", s);//输出:100 0.1

3.有符号整型转为无符号整型

printf("%u\n", -37);//输出:4294967259

计算机内部存储为补码。-37的补码为11111111 11111111 11111111 11011011,解释为无符号整型时,最高位不再表示符号位。

4.char和short型的参数自动扩展

char c = -37;
short h = -37;
printf("%d %u %d %u\n", c,c,h,h);//输出:-37 4294967259 -37 4294967259

这种情况下,char和short型的参数会被自动扩展为int型。

5.条件运算符表达式作为参数

条件运算符表达式可以作为printf函数的参数。

int n = 0;
scanf_s("%d", &n);
printf("There %s %d item%s in the list.\n", n != 1 ? "are" : "is", n, n != 1 ? "s" : "");
//n=1时,输出:There is 1 item in the list.
//n=5时,输出:There are 5 items in the list.

6.关于%%

printf("%%\n");//输出:%

要想打印出来%字符,一个%字符显然不行,因为%字符会被编译系统视为格式项的标志,连续两个%字符才能代表%

7.printf(s)和printf("%s", s)的区别

7.1 printf(s)

printf(s)将字符串 s 中的任何一个%字符视为一个格式项的标志,其后字符会被视作格式码,如果除%%之外的任何格式码在字符串 s 中出现,而后面又没有对应的参数,将会带来麻烦。例如:

char s[10] = "%d";
printf(s);

会输出一些很奇怪的数字,而且每次都不一样,因为格式项%d没有传入参数(或者说,没有匹配到参数)。事实上,上述代码等价于:

printf("%d");

不同的是,编译软件对前者并不会报任何警告或者错误(至少在vs2022上是这样),后者在运行时会有明显的警告或者错误,这就说明,编译器不会对前者字符串里面的内容进行检查,这类错误难以发现,提醒大家注意。

7.2 printf("%s", s)

printf("%s", s)将字符串 s 视为普通的字符串进行打印,不对 s 做任何检查(包括是否含有格式项),例如:

char s[20] = "%d i love xmu";
printf("%s", s);//输出:%d i love xmu

8.关于%g

%g : 四舍五入保留6位有效数字,强调一下,是6位有效数字不是6位小数!同时(优先)去掉尾缀0,参数必须为浮点型,否则会带来难以想象的后果。例如:

printf("%g\n", 2.0 / 3.0);//输出:0.666667
printf("%g\n", 0.5000);//输出:0.5
printf("%g\n", 1234.56789);//输出:1234.57

如果一个浮点数绝对值大于999999,采用科学计数法,例如:

printf("%g\n", 123456789.0);//输出:1.23457e+08

如果一个浮点数绝对值很小,适时采用科学计数法,例如:

printf("%g %g %g\n", 3.14259e-3, 3.14259e-4, 3.14259e-5);
//输出:0.00314259 0.000314259 3.14259e-05

9.关于%e

%e :打印浮点数时一律显式使用指数形式,记住,参数一定要是浮点型,否则同样的会带来难以想象的后果。例如:

printf("%e\n", 3.14);//输出:3.140000e+00

10.宽度修饰符

直接看例子就行:

printf("%5d\n", 2);//右对齐
printf("%-5d\n", 2);//左对齐
//书上说对%%也有效,实际并未发现有效
printf("%8%\n");//只打印出一个%

11.精度修饰符

对于整数格式项%d、%o、%x和%u,精度修饰符指定了打印数字的最少位数。如果待打印的数值并不需要这么多位数的数字来表示,就会在它的前面补0。例如:

printf("%.2d/%.2d/%.4d\n", 26, 12, 1893);//输出:26/12/1893
printf("%.3d/%.3d/%.5d\n", 26, 12, 1893);//输出:026/012/01893

对于%e、%E和%f格式项,精度修饰符指定了小数点后应该出现的数字位数。仅当精度大于0时打印的数值中才会实际出现小数点。例如:

double pi = 4 * atan(1.0);
printf("%.0f  %.1f  %.2f  %.3f  %.4f  %.5f  %.6f  %.10f\n", pi, pi, pi, pi, pi, pi, pi, pi);
//输出:3  3.1  3.14  3.142  3.1416  3.14159  3.141593  3.1415926536
printf("%.0e  %.1e  %.2e  %.10e\n", pi, pi, pi, pi);
//输出:3e+00  3.1e+00  3.14e+00  3.1415926536e+00

对于%g和%G格式项,精度修饰符指定了打印数值中的有效数字位数。除非标志另有说明,非有效数字的0将被去掉,如果小数点后不跟数字则小数点将被删除。例如:

printf("%.1g  %.2g  %.4g  %.8g\n", 10 / 3.0, 10 / 3.0, 10 / 3.0, 10 / 3.0);
//输出:3  3.3  3.333  3.3333333

 以上就是全部内容了,感谢观看,如果喜欢,记得收藏关注哟!

注:部分内容来源于Andrew Koenig著作《C陷阱与缺陷》

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值