分享
 
 
 

内核printf源代码分析.

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

内核printf源代码分析.

打开Source Insight来阅读EduOS的源代码,我们在stdio.c里找到了printf的实现代码.首先看看对printf的定义:

[code]

int printf (const char *cntrl_string, ...)

[/code]

第一个参数cntrl_string是控制字符串,也就是平常我们写入%d,%f的地方.紧接着后面是一个变长参数.

看看函数头部的定义:

[code]int pos = 0, cnt_printed_chars = 0, i;

unsigned char* chptr;

va_list ap;[/code]

马上晕!除了ap我们可以马上判断出来是用来读取变长参数的,i用于循环变量.其他变量都不知道是怎么回事.不要着急,我们边看代码边分析.代码的第一行必然是

[code]va_start (ap, cntrl_string);[/code]

用来初始化变长参数.

接下来是一个while循环

[code]while (cntrl_string[pos]) {

...

}[/code]

结束条件是cntrl_string[pos]为NULL,显然这个循环是用来遍历整个控制字符串的.自然pos就是当前遍历到的位置了.进入循环首先闯入视线的是

[code] if (cntrl_string[pos] == '%') {

pos++;

...

} [/code]

开门见山,上来就当前字符是否办断是否%.一猜就知道如果成立pos++马上取出下一个字符在d,f,l等等之间进行判断.往下一看,果真不出所料:

[code]switch (cntrl_string[pos]) {

case 'c':

...

case 's':

...

case 'i':

...

case 'd':

...

case 'u':

...[/code]

用上switch-case了. 快速浏览一下下面的代码.

首先看看case 'c'的部分

[code]case 'c':

putchar (va_arg (ap, unsigned char));

cnt_printed_chars++;

break;[/code]

%c表示仅仅输出一个字符.因此先通过va_arg进行参数的类型转换,之后用putchar[1]输出到屏幕上去.之后是

cnt_printed_chars++,通过这句我们就可以判断出cnt_printed_chars使用来表示,已经被printf输出的字符个数的.

再来看看 case 's':

[code] case 's':

chptr = va_arg (ap, unsigned char*);

i = 0;

while (chptr [i]) {

cnt_printed_chars++;

putchar (chptr [i++]);

}

break;[/code]和case 'c',同出一辙.cnt_printed_chars++放在了循环内,也证明了刚才提到的他的作用.另外我们也看到了cnptr是用来在处理字符串时的位置指针.到此为止,我们清楚的所有变量的用途,前途变得更加光明了.

接下来:

[code]// PartI

case 'i':

case 'd':

cnt_printed_chars += printInt (va_arg (ap, int));

break;

case 'u':

cnt_printed_chars += printUnsignedInt (va_arg (ap, unsigned int));

break;

case 'x':

cnt_printed_chars += printHexa (va_arg (ap, unsigned int), 'x');

break;

case 'X':

cnt_printed_chars += printHexa (va_arg (ap, unsigned int), 'X');

break;

case 'o':

cnt_printed_chars += printOctal (va_arg (ap, unsigned int));

break;

// Part II

case 'p':

putchar ('0');

putchar ('x');

cnt_printed_chars += 2; /* of '0x' */

cnt_printed_chars += printHexa (va_arg (ap, unsigned int), 'x');

break;

case '#':

pos++;

switch (cntrl_string[pos]) {

case 'x':

putchar ('0');

putchar ('x');

cnt_printed_chars += 2; /* of '0x' */

cnt_printed_chars += printHexa (va_arg (ap, unsigned int), 'x');

break;

case 'X':

putchar ('0');

putchar ('X');

cnt_printed_chars += 2; /* of '0X' */

cnt_printed_chars += printHexa (va_arg (ap, unsigned int), 'X');

break;

case 'o':

putchar ('0');

cnt_printed_chars++;

cnt_printed_chars += printOctal (va_arg (ap, unsigned int));

break;[/code]

注意观察一下,PartII的代码其实就是比PartI的代码多一个样式.在16进制数或八进制前加入0x或是o,等等.因此这里就只分析一下PartI咯.

其实仔细看看PartI的个条case,也就是把参数分发到了更具体的函数用于显示,然后以返回值的形式返回输出个数.对于这些函数就不具体分析了.我们先来看看一些善后处理:

先看case的default处理.

[code]default:

putchar ((unsigned char) cntrl_string[pos]);

cnt_printed_chars++;[/code]就是直接输出cntrl_string里%号后面的未知字符.应该是一种容错设计处理.

再看看if (cntrl_string[pos] == '%')的else部分

[code]else {

putchar ((unsigned char) cntrl_string[pos]);

cnt_printed_chars++;

pos++;

}[/code]

如果不是%开头的,那么直接输出这个字符.

最后函数返回前

[code]va_end (ap);

return cnt_printed_chars;[/code]va_end处理变长参数的善后工作.并返回输出的字符个数.

在最后我们有必要谈谈putChar函数以及基本输出的基础函数printChar,先来看看putChar

[code]int putchar (int c) {

switch ((unsigned char) c) {

case '\n' :

newLine ();

break;

case '\r' :

carriageReturn ();

break;

case '\f' :

clearScreen ();

break;

case '\t' :

printChar (32); printChar (32); /* 32 = space */

printChar (32); printChar (32);

printChar (32); printChar (32);

printChar (32); printChar (32);

break;

case '\b':

backspace ();

break;

case '\a':

beep ();

break;

default :

printChar ((unsigned char) c);

}

return c;

}[/code]

通览一下,也是switch-case为主体的.主要是用来应对一些特殊字符,如\n,\r,....这里需要提一下,关于\t的理解.有些人认为\t就是8个space,有些人则认为,屏幕分为10大列(每个大列8个小列总共80列).一个\t就跳到下一个大列输出.也就是说不管你现在实在屏幕的第1,2,3,4,5,6,7位置输出字符,只要一个\t都在第8个位置开始输出. VS.NET中就是用的这种理解.因此如果按照这个理解的话,\t的实现可以这样

[code]int currentX = ((currentX % 10) + 1) * 8;[/code]

然后在currentX位置输出.

接下来看printChar也就是输出部分最低层的操作咯

[code]void printChar (const byte ch) {

*(word *)(VIDEO + y * 160 + x * 2) = ch | (fill_color << 8);

x++;

if (x >= WIDTH)

newLine ();

setVideoCursor (y, x);

}[/code]这里VIDEO表示显存地址也就是0xB8000.通过 y * 160 + x 屏幕(x,y)坐标在显存中的位置.这里需要知道,一个字符显示需要两个字节,一个是ASCII码,第二个是字符属性代码也就是颜色代码.因此才必须 y * 80 * 2 + x = y * 160 + x.那么ch | (fill_color << 8)也自然就是写入字符及属性代码用的了.每写一个字符光标位置加1,如果大于屏幕宽度WIDTH就换行.最后通过setVideoCursor设置新的光标位置.完成了整个printChar过程.

到此,把printf从上到下说了一遍.不知道各位大家感觉如何,如果说得不清楚还大家多提意见.有说得不对的地方请大家多多指教.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有