一、源码实现

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>void my_printf(const char *fmt, ...)
{va_list ap;va_start(ap, fmt); /* 用最后一个具有参数的类型的参数去初始化ap */for (; *fmt; ++fmt){/* 如果不是控制字符 */if (*fmt != '%'){putchar(*fmt); /* 直接输出 */continue;}/* 如果是控制字符,查看下一字符 */++fmt;if ('\0' == *fmt) /* 如果是结束符 */{assert(0); /* 这是一个错误 */break;}switch (*fmt){case '%': /* 连续2个'%'输出1个'%' */putchar('%');break;case 'd': /* 按照int输出 */{/* 下一个参数是int,取出 */int i = va_arg(ap, int);putchar(i);}break;case 'c': /* 按照字符输出 */{/** 但是,下一个参数是char吗*//*  可以这样取出吗? */char c = va_arg(ap, char);putchar(c);}break;case 's':{char *pc = va_arg(ap, char *);while(*pc)putchar(*pc++);}break;}}va_end(ap);return;
}int main()
{my_printf("%s %s %c%c%c%c%c!\n", "welcome", "to", 'C', 'h', 'i', 'n', 'a');return 0;
}

二、缺陷分析

代码编译时会提示警告:

test_printf.c:41:33: warning: ‘char’ is promoted to ‘int’ when passed through ‘...’char c = va_arg(ap, char);

不处理,直接执行程序,发现程序崩溃了。

问题就在于这行代码:

char c = va_arg(ap, char);

这里面会涉及“默认参数提升”的情况。

C语言中什么时候会牵扯到默认参数提升呢?

在C语言中,调用一个不带原型声明的函数时:调用者会对每个参数执行“默认实际参数提升(default argument promotions)。同时,对可变长参数列表超出最后一个有类型声明的形式参数之后的每一个实际参数,也将执行上述提升工作。

提升工作如下:

  • float 类型的实际参数将提升到 double 。

  • char、short 和相应的 signed、unsigned 类型的实际参数提升到 int 。

  • 如果 int 不能存储原值,则提升到 unsigned int 。

所以,调用 my_printf 函数时,传入的参数绝对不会是如下类型:

  • char、signed char、unsigned char

  • short、unsigned short

  • signed shortshort int、signed short int、unsigned short int

  • float

所以正确的方案是将代码

char c = va_arg(ap, char);

改为

int c = va_arg(ap, int);

即可。

参考:

https://blog.csdn.net/astrotycoon/article/details/8284501

https://blog.csdn.net/iynu17/article/details/51588199

(SAW:Game Over!)

c/c++ / printf 实现相关推荐

  1. 关于C语言中printf函数“输出歧视”的问题

    目录 关于C语言中printf函数"输出歧视"的问题 问题描述 探索问题原因 另一种研究方法 问题结论 关于C语言中printf函数"输出歧视"的问题 问题描述 ...

  2. Go 学习笔记(46)— Go 标准库之 fmt(输入/输出格式化参数、Printf/Fprintf/Sprintf区别、Println/Fprintln/Sprintln 区别)

    1. 概述 import "fmt" fmt 包实现了类似 C 语言 printf 和 scanf 的格式化 I/O .格式化动作( verb )源自 C 语言但更简单. 2. P ...

  3. c语言字符串 s,c – printf格式字符串中“% – *.* s”的含义是什么

    你可以在这里阅读printf的手册页: http://pubs.opengroup.org/onlinepubs/009695399/functions/fprintf.html.但是它更像是法律文本 ...

  4. printf格式化输出几点注记

    搞了很多年c/c++,有很多细小的东西,曾经不止一次遇到,可是一直都是放在零散的地方,要用的时候怎么也找不到,今天,我痛下决心,改掉不良习惯,把这些经验或是tips记录在这里,便于日后查找. 1.在统 ...

  5. PHP函数printf()、sprintf()的用法

    printf()函数优点在于可以格式化输出 格式: %['padding_character][-][width][.precision]type 所有的转换说明都是以%开始,如果想打印一个%符号,必 ...

  6. printf(%d, -10u); 这个输出什么呀, 0或1?

    printf("%d", -1<0u); 这个输出什么呀, 0或1? 周银辉 既然我这么问了, 那么答案自然不是1,而是0 看看下面的代码: 对于-1+0u输出为-1,似乎理 ...

  7. fprintf、printf、sprintf、fscanf、scanf、sscanf 格式化输入输出

    格式化输入输出 1.         fprintf(格式化输出数据至文件) 相关函数  printf,fscanf,vfprintf 表头文件  #include<stdio.h> 定义 ...

  8. 【C】printf warning: unknown conversion type character ‘l‘ in format [-Wformat=]

    1.问题描述 在使用printf.fprintf打印long long类型时报错 printf warning: unknown conversion type character 'l' in fo ...

  9. 【linux】printf在终端打印彩色hello world

    代码 #include <stdio.h>#define NONE "\033[m" #define RED "\033[0;32;31m" #de ...

  10. c语言自定义char*函数返回值是乱码_[每日C语言」printf()函数的修饰符和返回值...

    在上一个小demo<printf()函数(1)>中主要说了一下printf()函数的转换说明符,这些转移说明符是可以被修饰的.我们可以在%d和定义的转义字符之间通过插入修饰符对基本的转换说 ...

最新文章

  1. PyTorch全连接ReLU网络
  2. limit-进程句柄限制
  3. AWS — AWS 上的 5G 专网部署模式
  4. linux中怎么删除只读变量,【Linux】【问题集锦】如何删除shell只读变量
  5. linux系统 qt调试,Linux下Qt Creator远程调试(redhat5+mini6410)
  6. 使用OutputDebugString帮助调试
  7. 一个小栗子聊聊JAVA泛型基础
  8. 微软五月份安全补丁发布
  9. 4.2 算法之数论 185 反正切函数的应用 python
  10. Magento : 直接下载扩展插件到本地 Extension
  11. ajax 获取openid,纯前端获取当前用户的openid(微信小程序)
  12. 今天中午处理的一笔数据真的是好纠结 好心惊胆战
  13. 单行溢出隐藏没生效_至今没搞懂,为什么这个缸这么火?
  14. JAVA + LR实现apache流媒体的性能测试
  15. TwinCAT 3 file记录日志txt文件程序
  16. Android 汇率换算对比小工具
  17. python读取tiff图像,浅谈python下tiff图像的读取和保存方法
  18. win10修改用户名_win7如何设置局域网共享无需输入用户名和密码
  19. 参数维纳滤波(Parametric Wiener Filter)
  20. html文本通常由版本信息组成,第 2 章 网页版面设计.ppt

热门文章

  1. Mysql学习总结(25)——MySQL外连接查询
  2. 并发集合(二)使用非阻塞线程安全的列表
  3. VMware Workstation
  4. php SqlServer 中文汉字乱码
  5. 轻松学MVC4.0–2 创建用户列表页面
  6. nagios errors
  7. 一个机械系毕业生的感言
  8. [IDEA 配置MYSQL数据库连接]
  9. go支持对函数返回值命名,可以解决函数返回值的顺序书写问题
  10. Ansible WebUI工具之Semaphore