Lab1-Exercise8
本篇文章未经同意引用或参考了以下连接的内容,需要删除请私信我
https://github.com/fatsheep9146/6.828mit/blob/master/lab/lib/printfmt.c
https://www.cnblogs.com/oasisyang/p/15365482.html
https://blog.csdn.net/amgtgsh3150267/article/details/101834729
https://www.cnblogs.com/gatsby123/p/9759153.html
Exercise8
替换 printfmt.c “case 'o:” 下的代码,让程序能够打印出八进制形式数。
case 'o':
// Replace this with your code.
putch('X', putdat);
putch('X', putdat);
putch('X', putdat);
break;
已替换,见 printmt.c
输入命令./grade-lab1可以检验正确性:

回答以下问题:
- Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?
- Explain the following from console.c:
if (crt_pos >= CRT_SIZE) {
int i;
memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
crt_buf[i] = 0x0700 | ' ';
crt_pos -= CRT_COLS;
}
-
For the following questions you might wish to consult(参阅) the notes for Lecture 2. These notes cover(介绍) GCC's calling convention(约定) on the x86.
Trace the execution of the following code step-by-step:int x = 1, y = 3, z = 4;
cprintf("x %d, y %x, z %d\n", x, y, z);
- In the call to cprintf(), to what does fmt point? To what does ap point?
- List (in order of execution) each call to cons_putc, va_arg, and vcprintf. For cons_putc, list its argument as well. For va_arg, list what ap points to before and after the call. For vcprintf list the values of its two arguments.
-
Run the following code.
unsigned int i = 0x00646c72;
cprintf("H%x Wo%s", 57616, &i);What is the output? Explain how this output is arrived at in the step-by-step manner of the previous exercise. Here's an ASCII table that maps bytes to characters.
The output depends on that fact that the x86 is little-endian. If the x86 were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?
Here's a description of little- and big-endian and a more whimsical description. -
在下面的代码中,'y='后面会打印什么?(注意:答案不是一个特定的值。)为什么会发生这种情况?
cprintf(“x = y = % d % d”,3); -
让我们假设GCC改变了它的调用约定,现在它按照声明顺序将参数推送到堆栈上,因此最后一个参数是最后推送的。你如何改变cprintf或它的接口,使它仍然有可能传递变量数量的参数?
解答
-
接口是 console.c 中的 cputchar() 函数,cputchar() 调用了同文件的 cons_putc() 函数,然后printf.c 中的 putch() 调用cputchar()。作用是输出一个 character 到 console。
-
这段代码来自 cga_putc()函数,cga_putc()函数被cons_putc()函数所调用。这段代码的作用我的猜测是:如果输出语句后要显示的内容超出屏幕范围,则将滚动屏幕并将光标放在最后,滚动屏幕意思是将之前显示的内容往上移动。代码和更详细解释见:console.c 和 这位大神的博客:https://blog.csdn.net/younothings/article/details/118768874
-
-
源代码见 printf.c
函数cprintf(const char *fmt, ...)
调用例子:cprintf("x %d, y %x, z %d\n", x, y, z);
fmt对应"x %d, y %x, z %d\n"这个字符串,...可变参数对应x,y,z。
cprintf函数中使用了va_list,va_start,va_end来获取...可变参数(VA_LIST 是在C语言中解决变参问题的一组宏,所在头文件: #include<stdarg.h>,用于获取不确定个数的参数。)并存储在ap中,所以ap指向可变参数的第一个参数。 -
列出(按执行顺序)对cons_putc(题目写少了一个s)、va_arg和vcprintf的每次调用。对于cons_putc,也列出它的参数。对于va_arg,列出ap在调 用之前和之后指向的内容。对于vcprintf,列出它的两个参数值。
首先看调用关系:
![image]()
图不是我画的,红色箭头是我加的,cons_putc被cputchar调用,va_arg被
vprintfmt调用,所以我们要关注的是vprintfmt中putch和va_arg的调用
va_arg:在vprintfmt中不是每个switch分支都调用了它,用vscode查看引用操作就能看到,太多了。在switch分支中有一个借口getint,也调用了va_arg。它的作用是将指针指向变参的下一个参数。则调用前,ap指向x,调用后,ap指向y。va_start宏识别并指向第一个变参,va_arg一个一个依次指向接下来的变参。
cons_putc:调用顺序为cprintf->vcprintf->vprintfmt->putch->cputchar->cons_putc。cons_putc的作用是输出一个字符到控制台。参数是一个整型表示的ASCII码。cons_putc可以想象成一个游标,遍历"x %d, y %x, z %d\n"字符串并输出。对于这个字符串,每次调用cons_putc时候,它的参数依序是x,空格,逗号,y,空格,逗号,z,空格的ASCII码。详情参考:https://blog.csdn.net/amgtgsh3150267/article/details/101834729
-
-
首先运行代码并查看输出结果,将指定代码复制到kern/monitor.c的monitor函数中,再make qemu 就可以看到运行结果。
![image]()
可以看到
unsigned int i = 0x00646c72;
cprintf("H%x Wo%s", 57616, &i); 输出的是He110 World
%x的意思是以十六进制数形式输出整数,57616十进制转十六进制是:e110
%s是输出字符串,查ASCII对照表得,r->0x72, l->0x6c, d->0x64,
如果是大端字节序,i的值要修改为0x726c6400,而57616这个值不用修改。因为根据printnum函数实现,打印整数时,总是按照从高位到低位来打印的。 -
系统调用cprintf,并将“x = y = % d % d”作为第一个参数,可变参数...作为第二个参数,3就放在可变参数...中。
然后逐个字符开始输出“x = y = % d % d”,当遇到第一个%d的时候,系统读取可变参数首字节首字节+4字节地址指向的值,也就是3,并将3输出。当当遇到第二个%d的时候,读取可变参数首字节+4字节首字节+8字节地址指向的值,但是由于我们并没有设置这个值(正确调用姿势是cprintf(“x = y = % d % d”,3,3)),所以这个值是未知的,我们也就不能确定'y='后面会打印什么。 -
从题目可知,在GCC调用约定中,参数从右往左压栈,如果GCC现在是从左往右压栈,我们应该如何改变cprintf或它的接口,使它仍然有可能传递变量数量的参数。
例如cprintf(“x = y = % d % d”,1,2),压栈顺序是2,1,读取顺序是1,2。现在反过来了压栈顺序是1,2,读取顺序是2,1。
我觉得可以新建一个堆栈a,逐个pop放参数的堆栈中的参数并push进栈a,然后以a作为新的参数栈。。。



浙公网安备 33010602011771号