printf 返回值 (你想知道的C语言 1.1)

 

  printf的返回值大部分情况下是没有必要关注的, 因为关注了也不会带来好处, 每次都关注的高成本也许会让你放弃.

然而,它的返回值却值得研究.

 

Q: printf("猫")返回值是多少?

A: 

     printf_ret.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef PD
#define PD(str)		printf(#str ":%d\n", (str))
#endif

int main(int argc, char *argv[])
{
	int ret;

	ret = printf("123");
	PD(ret);
	ret = printf("猫");
	PD(ret);

    return 0;
}

  运行结果: 

123ret:3
猫ret:3

   源代码默认编码是UTF-8, "猫"的UTF-8格式是3字节, 所以返回3.

 

Q: 为什么printf返回的是字节数, 不是字符数?

A:  从c语言的角度, 它不应该和所谓的"字符"打交道, 因为"字符"可能有N种编码格式,对应的字节数都可能不一样, libc也不应该

   为不同编码格式决定返回值, 否则会让libc变成一个大杂烩.  如果真的要这个返回值, 也应该为此单独写一个API.

 

Q: "猫"是如何被存储的?

A:  lldb调试读到的数据如下:

    

   可以看到, "猫"被存储为e7 8c ab (LSB --> MSB), 查询UTF-8编码表可以对上.

 

Q: 我们知道了printf返回值的原理, 我们是不是可以动态修改数据?

A:

   是的, 修改数据为"ab", 返回值变成了2, 结果如下: 

     

 

  Q: 在编译的静态二进制文件中如何找到"猫"的存储位置?

  A:

     

   MachOView:

          

 

 Q:  如何在libc看到printf返回值是如何计数的?

 A: 

     printf最终会call到vfprintf, return计数默认按字节计算(如下的ch和fmt分别是char和char *类型, 数据默认为1字节). 

     

 

Q: 如何构造出printf的返回值和预期不一致的情形?

A: 

  利用printf不支持的格式串让内部不能正常计数输出的字节数.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef PD
#define PD(str)		printf(#str ":%d\n", (str))
#endif

int main(int argc, char *argv[])
{
	int ret;

	ret = printf("%j123");	// %j is a wrong format!
	PD(ret);

    return 0;
}

 结果: 

ret:0

 所以, 这个时候可以看到, 返回值有的时候还是有作用的, 为了检测输出的数据而做防范, 通过它确保输出是否有异常.

 

Q: 除了上面为了检测异常输出的情况, 返回值真的没有用处了吗?

A:  用户空间透过sysfs等机制得到kernel信息时,不可避免地需要用"printf系列/scanf系列"的返回值,而这个时候,正是利用printf系列返回值最佳的时机: 因为无法保证内部组装的字符串都是常量, 一旦涉及到格式, 依赖程序员"直接"统计将是无法保证的任务.

    如下是linux cpufreq driver显示cpus信息利用scnprintf/sprintf计数统计值.

        

 

Q: C语言有没有内部计数printf的信息?

A:  %n 可以记录当前已经输出的字节数, 且不额外占用统计值.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef PD
#define PD(str)		printf(#str ":%d\n", (str))
#endif

#ifndef PD2
#define PD2(str1, str2) \
	printf(#str1 ":%d, " #str2 ":%d\n", (str1), (str2))
#endif

int main(int argc, char *argv[])
{
	int ret;
	int n;

	ret = printf("123%n4", &n);
	PD2(ret, n);

    return 0;
}

 结果:

  

 

作者:     陈曦
环境:     MacOS 10.14.5
         Apple LLVM version 10.0.1 (clang-1001.0.46.4)
         Target: x86_64-apple-darwin18.6.0

转载请注明出处
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值