linux0.12-8-11-vsprintf.c
[383页]
1、 这一小节可以不看代码如何实现,因为标准的C库函数;
2、 等自己看完的这本书,有兴趣过来研究研究也是可以的。
8-11 vsprintf.c程序
8-11-1 功能描述
该程序主要包括vsprintf(),用于对参数产生格式化的输出。由于该函数是C函数库中
的标志函数,基本没有设计内核工作原理方面的内容,因此可以跳过。直接阅读代码后对该函数
的使用说明。vsprintf()函数的使用方法参照C库函数手册。
8-11-2 代码注释
代码在后面!!
8-11-3 vsprintf()的格式字符串
int vsprintf(char *buf, const char *fmt, va_list args)
vsprintf()函数是printf()系列函数之一。这些函数都产生格式化的输出:接受确定输出格式的格式字符串fmt,用格式字符串对个数变化的参数进行格式化,产生格式化的输出。
printf直接把输出送到标准句柄stdout。
cprintf把输出送到控制台。
fprintf把输出送到文件句柄。
printf前带’v’字符的(例如vfprintf)表示参数是从va_arg数组的va_list args中接受。
printf前带’s’字符则表示把输出送到以null结尾的字符串Buf中(此时用户应确保buf有足够的空间存放字符串)。
下面详细说明格式字符串的使用方法。
1、 格式字符串
printf系列函数中的格式字符串用于控制函数转换方式、格式化和输出其参数。对于每个格式,
必须有对应的参数,参数过多将被忽略。格式字符串中含有两类成分,
一种是被直接复制到输出中的简单字符串;另一种是用于对对应参数进行格式化的转换指示字符串。
2、格式指示字符串
格式指示串的形式如下:
%[flags][width][.prec][|h|l|L][type]
每一个转换指示串均需要以百分号(%)开始。其中:
[flags]是可选择的标志字符序列。
[width]是可选择的宽度指示符。
[.prec]是可选择的精度(precision)指示符。
[|h|l|L]是可选择的输入长度修饰符。
[type]是转换类型字符(或称为转换指示符)。




8-11-4 与当前版本的区别
由于该文件也属于库函数,所以从1.2版本内核开始就直接使用库中的函数了。即删除了该文件。
/** linux/kernel/vsprintf.c** (C) 1991 Linus Torvalds*//* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/** Wirzenius wrote this portably, Torvalds fucked it up :-)*/
// Lars Wirzenius是Linus的好友,在Helsinki大学时曾同处一间办公室。在1991年夏季开发Linux
// 时,Linus当时对C语言还不是很熟悉,还不会使用可变参数列表函数功能。因此Lars Wirzenius
// 就为他编写了这段用于内核显示信息的代码。他后来(1998年)承认在这段代码中有一个bug,直到
// 1994年才有人发现,并予以纠正。这个bug是在使用*作为输出域宽度时,忘记递增指针跳过这个星
// 号了。在本代码中这个bug还仍然存在(130行)。 他的个人主页是http://liw.iki.fi/liw/
#include <stdarg.h> // 标准参数头文件。以宏的形式定义变量参数列表。主要说明了-个// 类型(va_list)和三个宏(va_start, va_arg和va_end),用于// vsprintf、vprintf、vfprintf函数。
#include <string.h>// 字符串头文件。主要定义了一些有关字符串操作的嵌入函数。/* 我们使用下面的定义,这样我们就可以不使用ctype库了 */
#define is_digit(c) ((c) >= '0' && (c) <= '9')// 判断字符c是否为数字字符。
// 该函数将字符数字串转换成整数。输入是数字串指针的指针,返回是结果数值。另外指针将前移。
static int skip_atoi(const char **s)
{int i=0;while (is_digit(**s))i = i*10 + *((*s)++) - '0';return i;
}
// 这里定义转换类型的各种符号常数。
#define ZEROPAD 1 /* 填充零 */
#define SIGN 2 /* 无符号/符号长整数 */
#define PLUS 4 /* 显示加 */
#define SPACE 8 /* 如是加,则置空格 */
#define LEFT 16 /* 左调整 */
#define SPECIAL 32 /* 0x */
#define SMALL 64 /* 使用小写字母 */// 除操作。输入:n为被除数,base为除数;结果:n为商,函数返回值为余数。
// 参见4.5.3节有关嵌入汇编的信息。
#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })// 将整数转换为指定进制的字符串。
// 输入:num-整数;base-进制;size-字符串长度;precision-数字长度(精度);type-类型选项。
// 输出:数字转换成字符串后指向该字符串末端后面的指针。
static char * number(char * str, int num, int base, int size, int precision,int type)
{char c,sign,tmp[36];const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";int i;// 如果类型type指出用小写字母,则定义小写字母集。// 如果类型指出要左调整(靠左边界),则屏蔽类型中的填零标志。// 如果进制基数小于2或大于36,则退出处理,也即本程序只能处理基数在2-32之间的数。if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";if (type&LEFT) type &= ~ZEROPAD;if (base<2 || base>36)return 0;// 如果类型指出要填零,则置字符变量c='0',否则c等于空格字符。// 如果类型指出是带符号数并且数值num小于0,则置符号变量sign=负号,并使num取绝对值。// 否则如果类型指出是加号,则置sign=加号,否则若类型带空格标志则sign=空格,否则置0。 c = (type & ZEROPAD) ? '0' : ' ' ;if (type&SIGN && num<0) {sign='-';num = -num;} elsesign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);// 若带符号,则宽度值减1。若类型指出是特殊转换,则对于十六进制宽度再减少2位(用于0x),// 对于八进制宽度减1(用于八进制转换结果前放一个零)。if (sign) size--;if (type&SPECIAL)if (base==16) size -= 2;else if (base==8) size--;// 如果数值num为0,则临时字符串='0';否则根据给定的基数将数值num转换成字符形式。i=0;if (num==0)tmp[i++]='0';else while (num!=0)tmp[i++]=digits[do_div(num,base)];// 若数值字符个数大于精度值,则精度值扩展为数字个数值。// 宽度值size减去用于存放数值字符的个数。if (i>precision) precision=i;size -= precision;// 从这里真正开始形成所需要的转换结果,并暂时放在字符串str中。// 若类型中没有填零(ZEROPAD)和左靠齐(左调整)标志,则在str中首先// 填放剩余宽度值指出的空格数。若需带符号位,则存入符号。if (!(type&(ZEROPAD+LEFT)))while(size-->0)*str++ = ' ';if (sign)*str++ = sign;// 若类型指出是特殊转换,则对于八进制转换结果头一位放置一个'0';而对于十六进制则存放'0x'。if (type&SPECIAL)if (base==8)*str++ = '0';else if (base==16) {*str++ = '0';*str++ = digits[33];}// 若类型中没有左调整(左靠齐)标志,则在剩余宽度中存放c字符('0'或空格),见51行。 if (!(type&LEFT))while(size-->0)*str++ = c;// 此时i存有数值num的数字个数。若数字个数小于精度值,则str中放入(精度值-i)个'0'。 while(i<precision--)*str++ = '0';// 将数值转换好的数字字符填入str中。共i个。 while(i-->0)*str++ = tmp[i];// 若宽度值仍大于零,则表示类型标志中有左靠齐标志。则在剩余宽度中放入空格。 while(size-->0)*str++ = ' ';return str;// 返回转换好的指向字符串末端后的指针。
}
// 下面函数是送格式化输出到字符串中。
// 为了能在内核中使用格式化的输出,Linus在内核实现了该C标准函数。
// 其中参数fmt是格式字符串;args是个数变化的值;buf是输出字符串缓冲区。
// 请参见本代码列表后的有关格式转换字符的介绍。
int vsprintf(char *buf, const char *fmt, va_list args)
{int len;int i;char * str;// 用于存放转换过程中的字符串。char *s;int *ip;int flags; /* number()函数使用的标志 */int field_width; /* 输出字段宽度*/int precision; /* min. 整数数字个数;max. 字符串中字符个数 */int qualifier; /* 'h', 'l',或'L'用于整数字段 */// 首先将字符指针指向buf,然后扫描格式字符串,对各个格式转换指示进行相应的处理。for (str=buf ; *fmt ; ++fmt) {// 格式转换指示字符串均以'%'开始,这里从fmt格式字符串中扫描'%',寻找格式转换字符串的开始。// 不是格式指示的一般字符均被依次存入str。if (*fmt != '%') {*str++ = *fmt;continue;}
// 下面取得格式指示字符串中的标志域,并将标志常量放入flags变量中。 /* process flags */flags = 0;repeat:++fmt; /* this also skips first '%' */switch (*fmt) {case '-': flags |= LEFT; goto repeat;// 左靠齐调整。case '+': flags |= PLUS; goto repeat;// 放加号。case ' ': flags |= SPACE; goto repeat;// 放空格。case '#': flags |= SPECIAL; goto repeat;// 是特殊转换。case '0': flags |= ZEROPAD; goto repeat;// 要填零(即'0')。}// 取当前参数字段宽度域值,放入field_width变量中。如果宽度域中是数值则直接取其为宽度值。// 如果宽度域中是字符'*',表示下一个参数指定宽度。因此调用va_arg取宽度值。若此时宽度值// 小于0,则该负数表示其带有标志域'-'标志(左靠齐),因此还需在标志变量中添入该标志,并// 将字段宽度值取为其绝对值。 /* get field width */field_width = -1;if (is_digit(*fmt))field_width = skip_atoi(&fmt);else if (*fmt == '*') {/* it's the next argument */ // 这里有个bug,应插入++fmt;field_width = va_arg(args, int);if (field_width < 0) {field_width = -field_width;flags |= LEFT;}}// 下面这段代码,取格式转换串的精度域,并放入precision变量中。精度域开始的标志是'.'。// 其处理过程与上面宽度域的类似。如果精度域中是数值则直接取其为精度值。如果精度域中是// 字符'*',表示下一个参数指定精度。因此调用va_arg取精度值。若此时宽度值小于0,则将// 字段精度值取为0。/* get the precision */precision = -1;if (*fmt == '.') {++fmt; if (is_digit(*fmt))precision = skip_atoi(&fmt);else if (*fmt == '*') {/* it's the next argument */ // 同上这里也应插入++fmt;precision = va_arg(args, int);}if (precision < 0)precision = 0;}
// 下面这段代码分析长度修饰符,并将其存入qualifer变量。(h,l,L的含义参见列表后的说明)。/* get the conversion qualifier */qualifier = -1;if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {qualifier = *fmt;++fmt;}
// 下面分析转换指示符。switch (*fmt) {
// 如果转换指示符是'c',则表示对应参数应是字符。此时如果标志域表明不是左靠齐,则该字段前面
// 放入'宽度域值-1'个空格字符,然后再放入参数字符。如果宽度域还大于0,则表示为左靠齐,则在
// 参数字符后面添加'宽度值-1'个空格字符。case 'c':if (!(flags & LEFT))while (--field_width > 0)*str++ = ' ';*str++ = (unsigned char) va_arg(args, int);while (--field_width > 0)*str++ = ' ';break;// 如果转换指示符是's',则表示对应参数是字符串。首先取参数字符串的长度,若其超过了精度域值,// 则扩展精度域=字符串长度。此时如果标志域表明不是左靠齐,则该字段前放入'宽度值-字符串长度'// 个空格字符。然后再放入参数字符串。如果宽度域还大于0,则表示为左靠齐,则在参数字符串后面// 添加'宽度值-字符串长度'个空格字符。case 's':s = va_arg(args, char *);len = strlen(s);if (precision < 0)precision = len;else if (len > precision)len = precision;if (!(flags & LEFT))while (len < field_width--)*str++ = ' ';for (i = 0; i < len; ++i)*str++ = *s++;while (len < field_width--)*str++ = ' ';break;
// 如果格式转换符是'o',表示需将对应的参数转换成八进制数的字符串。调用number()函数处理。case 'o':str = number(str, va_arg(args, unsigned long), 8,field_width, precision, flags);break;
// 如果格式转换符是'p',表示对应参数是一个指针类型。此时若该参数没有设置宽度域,则默认宽度
// 为8,并且需要添零。然后调用number()函数进行处理。case 'p':if (field_width == -1) {field_width = 8;flags |= ZEROPAD;}str = number(str,(unsigned long) va_arg(args, void *), 16,field_width, precision, flags);break;
// 若格式转换指示是'x'或'X',则表示对应参数需要打印成十六进制数输出。'x'表示用小写字母表示。case 'x':flags |= SMALL;case 'X':str = number(str, va_arg(args, unsigned long), 16,field_width, precision, flags);break;// 如果格式转换字符是'd','i'或'u',则表示对应参数是整数,'d', 'i'代表符号整数,因此需要加上// 带符号标志。'u'代表无符号整数。case 'd':case 'i':flags |= SIGN;case 'u':str = number(str, va_arg(args, unsigned long), 10,field_width, precision, flags);break;// 若格式转换指示符是'n',则表示要把到目前为止转换输出字符数保存到对应参数指针指定的位置中。// 首先利用va_arg()取得该参数指针,然后将已经转换好的字符数存入该指针所指的位置。case 'n':ip = va_arg(args, int *);*ip = (str - buf);break;// 若格式转换符不是'%',则表示格式字符串有错,直接将一个'%'写入输出串中。// 如果格式转换符的位置处还有字符,则也直接将该字符写入输出串中,并返回到107行继续处理// 格式字符串。否则表示已经处理到格式字符串的结尾处,则退出循环。default:if (*fmt != '%')*str++ = '%';if (*fmt)*str++ = *fmt;else--fmt;break;}}*str = '\0';// 最后在转换好的字符串结尾处添上null。return str-buf;// 返回转换好的字符串长度值。
}
相关文章:
linux0.12-8-11-vsprintf.c
[383页] 1、 这一小节可以不看代码如何实现,因为标准的C库函数; 2、 等自己看完的这本书,有兴趣过来研究研究也是可以的。 8-11 vsprintf.c程序 8-11-1 功能描述 该程序主要包括vsprintf(),用于对参数产生格式化的输出。由于该函数是C函数…...
Node.js 与 WebAssembly
目录 1、简介 2、关键概念 3、生成WebAssembly模块 4、如何使用它 5、与操作系统交互 1、简介 首先,让我们了解为什么WebAssembly是一个很棒的工具,并学会自己使用它。 WebAssembly是一种类似汇编的高性能语言,可以从各种语言编译&…...
OpenCL编程指南-4.4矢量操作符
矢量操作符 如下描述了可用于矢量数据类型或矢量和标量数据类型组合的各类操作符。 算术操作符 算术操作符(加()、减(–)、乘(*)和除(/)),可以作用于内置整数、浮点标量和矢量数…...
索洛模型(二)
索洛模型(二) 文章目录 索洛模型(二)[toc]1 事实2 假设2.1 对生产函数的假设2.2对投入要素的假设 3 索洛模型的动态学3.1 k k k的动态学3.2 平衡增长路径 4 储蓄率变化的影响4.1 对产出的影响4.2 对消费的影响 索罗经济增长模型(Solow growth model)&am…...
【多微电网】基于粒子群优化算法的面向配电网的多微电网协调运行与优化(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
使用Atmel Studio开发Arduino的ATmega328P单片机
摘要:我们知道Arduino开发板常用的芯片是Atmel公司生产的AVR微控制器系列。最常见的是ATmega328P,被广泛用于Arduino Uno开发板。其他常用的AVR芯片包括ATmega2560和ATmega32U4。使用Arduino平台开发AVR的单片机非常方便。Arduino IDE提供了一个非常简洁…...
docker基础命令
查找镜像 docker search nginx 拉取镜像 不指定镜像版本的话默认拉取最新的版本,可以在dockerhub上查看镜像的版本 dockerhub地址:Docker docker pull nginx docker pull nginx:1.23 查看镜像列表 docker images 打包镜像 打包的镜像可以迁移到其它的主机上部署 …...
数组名+1和数组名+1的区别
数组名和&数组名区别 #include <stdio.h>int main() {int a[5] {1,2,3,4,5};int *ptr (int *)(&a 1);printf("%d,%d,%d\n",*(a 4),*(ptr - 1),*ptr); // 2 5 为什么是5printf("数组a[4]的地址%p,(ptr-1)地址%p, ptr的地址%p",(a 4)…...
开放原子训练营(第三季)inBuilder低代码开发实验室初体验
一、活动介绍 开放原子训练营开启inBuilder低代码实验室活动。无论您是计算机行业相关从业者、低代码开发爱好者还是普通用户,都可以基于inBuilder低代码开发平台社区版(基于UBML开源项目的一个可以广泛使用的发行版),体验向导式、…...
sql数据定义语句(cascade,set,null,no action的区别)
(一)ADD 基本格式: ALTER TABLE <表名> ADD 新属性名 新属性类型 例:alter table s1 add tele char(12):增加一个电话号码(tele)属性 注: 新增的属…...
Java进程(基础)
基本概念 1、进程:程序的执行过程 2、线程:一个进程可以有单个线程也就是我们说的单线程,还可以有多个线程也就是我们说的多线程, 线程 1、当一个类继承了Thread类就可以当成一个线程用 2、我们会重写run方法写上我们自己的业务…...
Android之 Activity活动详解
一 四大组件 1.1 Activity组件,它一个单独的窗口,程序流程都必须在Activity中运行。 1.2 service组件,用于在后台完成用户指定的操作。 1.3 content provider组件,会为所有的应用准备一个内容窗口,并且保留数据库、…...
车载软件架构——闲聊几句AUTOSAR BSW(五)
我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 我们并不必要为了和谐,而时刻保持通情达理;我们需要具备的是,偶尔有肚量欣然承认在某些方面我们可能会有些不可理喻。该有主见的时候能掷地有声地镇得住场…...
APP图标尺寸规范一文了解清楚
在进行图标设计前,熟知手机 app 图标尺寸规范,能更好地去针对不同平台设计出更极致的图标。当前智能手机系统主要以 iOS 及 Android 为主,APP 图标是产品给用户的第一印象,图标视觉设计的美感与吸引力,与用户是否选择下…...
写给程序员Android Framework 开发,
前言 在 Android 开发者技能中,如果想进大厂,一般拥有较好的学历可能有优势一些。但是如果你靠硬实力也是有机会的,例如死磕Framework。Framework 知识广泛应用在Android各个领域中,重要性显而易见。 成为一名Android Framework…...
html实现一个一闪一闪的按钮,CSS实现一个一闪一闪的按钮,Css闪烁点标,css设置按钮层次感,css按钮美化,CSS按钮动画过渡,CSS按钮添加阴影
效果 动态 静态 实现 底部多加了几个过渡按钮 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style>#app {margin: 2% auto;text-align: center;}.lay-btn-box {position: relative;display: …...
品优购项目学习记录04--列表页
文章目录 1.品优购项目列表页制作准备工作2.列表页header和nav修改2.1 秒杀logo的制作2.2 导航栏nav修改 3.列表页主体sk_container 1.品优购项目列表页制作准备工作 1.列表页面是新的页面,我们需要新建页面文件list.html 2.因为列表页的头部和底部基本一致&#x…...
script标签type值application/json,importmap和module
type(默认text/javascript) 该属性定义 script 元素包含或src引用的脚本语言。属性的值为 MIME 类型(媒体类型); 如果没有定义这个属性,脚本会被视作 JavaScript。 如果 MIME 类型不是 JavaScript 类型&a…...
基于ArcGIS实现陕西省1:250000比例尺地形图分幅和编号
1地形图的分幅与编号原理 我国1:1000000地形图的分幅采用国际1:1000000地图分幅标准,而其他比例尺地形图分幅以1:1000000比例尺地形图为基准进行分幅。每幅1:1000000地形图范围是经差6、纬差4;纬度60~ 76之间经差12、纬差4;纬度76~ 88之间经…...
校园安全,一键报警主机助力保障
校园安全,一键报警主机助力保障 随着社会发展和科技进步,校园安全问题日益受到重视。如何保障师生们的安全成为了学校一项重要任务。而校园可视一键报警主机就是一种非常有效的安保设备。 这种报警主机集合了视频监控、安全防范、数据处理等多个功能&a…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
rknn toolkit2搭建和推理
安装Miniconda Miniconda - Anaconda Miniconda 选择一个 新的 版本 ,不用和RKNN的python版本保持一致 使用 ./xxx.sh进行安装 下面配置一下载源 # 清华大学源(最常用) conda config --add channels https://mirrors.tuna.tsinghua.edu.cn…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
