CSAPP:学习日志1---show

    xiaoxiao2025-02-08  58

    show_bytes

    代码展示

    /* show-bytes - prints byte representation of data */ /* $begin show-bytes */ #include <stdio.h> /* $end show-bytes */ #include <stdlib.h> #include <string.h> /* $begin show-bytes */ typedef unsigned char *byte_pointer; //typedef char *byte_pointer; //typedef int *byte_pointer; void show_bytes(byte_pointer start, size_t len) { size_t i; for (i = 0; i < len; i++) printf("%p\t0x%.2x\n", &start[i], start[i]); printf("\n"); } void show_int(int x) { show_bytes((byte_pointer) &x, sizeof(int)); } void show_float(float x) { show_bytes((byte_pointer) &x, sizeof(float)); } void show_pointer(void *x) { show_bytes((byte_pointer) &x, sizeof(void *)); } /* $end show-bytes */ /* $begin test-show-bytes */ void test_show_bytes(int val) { int ival = val; //float fval = (float) ival; double fval = (double) ival; int *pval = &ival; printf("Stack variable ival = %d\n", ival); printf("(int)ival:\n"); show_int(ival); printf("(float)ival:\n"); show_float(fval); printf("&ival:\n"); show_pointer(pval); } /* $end test-show-bytes */ void simple_show_a() { /* $begin simple-show-a */ int val = 0x87654321; byte_pointer valp = (byte_pointer) &val; show_bytes(valp, 1); /* A. */ show_bytes(valp, 2); /* B. */ show_bytes(valp, 3); /* C. */ /* $end simple-show-a */ } void simple_show_b() { /* $begin simple-show-b */ int val = 0x12345678; byte_pointer valp = (byte_pointer) &val; show_bytes(valp, 1); /* A. */ show_bytes(valp, 2); /* B. */ show_bytes(valp, 3); /* C. */ /* $end simple-show-b */ } void float_eg() { int x = 3490593; float f = (float) x; printf("For x = %d\n", x); show_int(x); show_float(f); x = 3510593; f = (float) x; printf("For x = %d\n", x); show_int(x); show_float(f); } void string_ueg() { /* $begin show-ustring */ const char *s = "ABCDEF"; show_bytes((byte_pointer) s, strlen(s)); /* $end show-ustring */ } void string_leg() { /* $begin show-lstring */ const char *s = "abcdef"; show_bytes((byte_pointer) s, strlen(s)); /* $end show-lstring */ } void show_twocomp() { /* $begin show-twocomp */ short x = 12345; short mx = -x; show_bytes((byte_pointer) &x, sizeof(short)); show_bytes((byte_pointer) &mx, sizeof(short)); /* $end show-twocomp */ } int main(int argc, char *argv[]) { int val = 12345; if (argc > 1) { val = strtol(argv[1], NULL, 0); printf("calling test_show_bytes\n"); test_show_bytes(val); } else { printf("calling show_twocomp\n"); show_twocomp(); printf("Calling simple_show_a\n"); simple_show_a(); printf("Calling simple_show_b\n"); simple_show_b(); printf("Calling float_eg\n"); float_eg(); printf("Calling string_ueg\n"); string_ueg(); printf("Calling string_leg\n"); string_leg(); } return 0; }

    代码说明及心得体会

    #include <stdio.h>

    调用库函数stdio.h用于输入和输出函数如 printf和scanf等或者可用于一些指针的宏如NULL(空指针常量)

    #include <stdlib.h>

    调用库函数stdlib.h用于与系统调用相关的函数,如内存申请malloc和释放free等

    #include <string.h>

    调用库函数string.h用于做字符串处理的函数,如:

    strlen()   求字符串长度 strcmp()  比较2个字符串是否一样 strcat()  字符串连接操作 strcpy()  字符串拷贝操作 strncat()  字符串连接操作 strncpy()  字符串拷贝操作

    typedef unsigned char *byte_pointer; //typedef char *byte_pointer; //typedef int *byte_pointer;

     typedef将数据类型byte_pointer定义为一个指向类型为“unsigned char”  “char”  “int”的对象的指针,三种不同的类型也会使结果不同

    typedef是c语言的关键字,作用是为一种数据类型定义一个新名字, 比如 typedef int status(数据结构里常用的一句话)是给int一个名字status ,此后可以用status给其他变量名赋 一个整型变量

    void show_bytes(byte_pointer start, size_t len) { size_t i; for (i = 0; i < len; i++) printf("%p\t0x%.2x\n", &start[i], start[i]); printf("\n"); }

    show-bytes 这个函数将传入对象的指针,和该对象的字节数,然后通过在for循环中,打印十六进制的地址以及数据

    size_t : 实为unsigned int 类型,是作为sizeof()的返回值,它是一种“整型”类型,里面保存的是一个整数,就像int, long那样。这种整数用来记录一个大小(size)。size_t的全称应该是size type,就是说“一种用来记录大小的数据类型”。

    C格式化指令“%.2x”表明整数必须用至少两个数字的十六进制输出。 在格式串里,每个以“%”开始的字符序列都表示如何格式化下一个参数     “%p”表示指针输出    “%d”表示输出一个十进制整数    “%f”是输出一个浮点数    “%c”是输出一个字符,其编码由参数给出     Tab是制表符,就是"\t",作用是预留8个字符的显示宽度,用于对齐

    在c语言中,换行(\n)  就是光标下移一行却不会移到这一行的开头,回车(\r)  就是回到当前行的开头却不向下移一行. Enter键按下后会执行  \n\r  这样就是我们看到的一般意义的回车了

    start代表对象的首地址,start[i] 代表从start[0] 开始第i个位置处的字节

    void show_int(int x) { show_bytes((byte_pointer) &x, sizeof(int)); } void show_float(float x) { show_bytes((byte_pointer) &x, sizeof(float)); } void show_pointer(void *x) { show_bytes((byte_pointer) &x, sizeof(void *)); } /* $end show-bytes */

    三个函数传递给show_bytes一个指向它们参数x的指针&x和字节大小,且这个指针被强制转化为“unsigned char *”  "char *”  “int *”  ,这个指针会被看成是对象使用的最低字节地址,打印整型的数据、浮点数的数据、任意类型及打印x本身

    /* $begin test-show-bytes */ void test_show_bytes(int val) { int ival = val; //float fval = (float) ival; double fval = (double) ival; int *pval = &ival; printf("Stack variable ival = %d\n", ival); printf("(int)ival:\n"); show_int(ival); printf("(float)ival:\n"); show_float(fval); printf("&ival:\n"); show_pointer(pval); } /* $end test-show-bytes */

    text_show_bytes为测试函数,通过将对象val强制类型转换为整型、浮点型、整型指针,调用show_int函数、show_float函数、show_pointer函数,测试显示字节

    同时,需注意:

    1:从int 转换为float,数字不会溢出,但可能舍入。 2:从int或float转换为double时,因为double有更高的精度,更大的范围,能够保留精确的数值,不会溢出。 3:从float或double转换为int时,值将会向零舍入。

    void simple_show_a() { /* $begin simple-show-a */ int val = 0x87654321; byte_pointer valp = (byte_pointer) &val; show_bytes(valp, 1); /* A. */ show_bytes(valp, 2); /* B. */ show_bytes(valp, 3); /* C. */ /* $end simple-show-a */ } void simple_show_b() { /* $begin simple-show-b */ int val = 0x12345678; byte_pointer valp = (byte_pointer) &val; show_bytes(valp, 1); /* A. */ show_bytes(valp, 2); /* B. */ show_bytes(valp, 3); /* C. */ /* $end simple-show-b */ } void float_eg() { int x = 3490593; float f = (float) x; printf("For x = %d\n", x); show_int(x); show_float(f); x = 3510593; f = (float) x; printf("For x = %d\n", x); show_int(x); show_float(f); } void string_ueg() { /* $begin show-ustring */ const char *s = "ABCDEF"; show_bytes((byte_pointer) s, strlen(s)); /* $end show-ustring */ } void string_leg() { /* $begin show-lstring */ const char *s = "abcdef"; show_bytes((byte_pointer) s, strlen(s)); /* $end show-lstring */ } void show_twocomp() { /* $begin show-twocomp */ short x = 12345; short mx = -x; show_bytes((byte_pointer) &x, sizeof(short)); show_bytes((byte_pointer) &mx, sizeof(short)); /* $end show-twocomp */ }

     在ubantu上运行该程序时,如未加参数,则调用以上5个函数,其自带的参数可以供我们研究

     在小端法机器上,它将按照从最低有效字节到最高有效字节的顺序列出字节(低对低,高对高);在大端法机器上,他将按照从最高有效字节到最低有效字节的顺序列出字节。

    int main(int argc, char *argv[]) { int val = 12345; if (argc > 1) { val = strtol(argv[1], NULL, 0); printf("calling test_show_bytes\n"); test_show_bytes(val); } else { printf("calling show_twocomp\n"); show_twocomp(); printf("Calling simple_show_a\n"); simple_show_a(); printf("Calling simple_show_b\n"); simple_show_b(); printf("Calling float_eg\n"); float_eg(); printf("Calling string_ueg\n"); string_ueg(); printf("Calling string_leg\n"); string_leg(); } return 0; } int main(int argc, char *argv[])

    main函数都是不带参数的。因此main 后的括号都是空括号。实际上,main函数可以带参数,这个参数可以认为是 main函数的形式参数。C语言规定main函数的参数只能有两个,习惯上这两个参数写为argc和argv。因此,main函数的函数头可以写为: main (argc,argv)C语言还规定argc(第一个形参)必须是整型变量,argv( 第二个形参)必须是指向字符串的指针数组。加上形参说明后,main函数的函数头应写为:  main (argc,argv) int argv; char *argv[];

    或写成: main (int argc,char *argv[])

    由于main函数不能被其它函数调用, 因此不可能在程序内部取得实际值,但当我们要运行一个可执行文件时,在DOS提示符下键入文件名,再输入实际参数即可把这些实参传送到main的形参中去。

    val = strtol(argv[1], NULL, 0);

     strtol()函数原型为:

    long int strtol(const char *nptr,char **endptr,int base);

     nptr就是我们的字符串,endptr是结束符(NULL就是\0),base是进制数,默认的0(10进制),

    作用就是将字符串转为长整型

    而在ubantu中运行该代码时:

    不加参数,则只有文件名,argc=1,val=12345,直接执行else语句

    如加一个参数,argc=2>1   执行   val = strtol(argv[1], NULL, 0);   其中argv[1]为参数的字符串型,即将其转为10进制长整型,此时val=参数值

    代码在ubantu上的运行结果

    typedef unsigned char *byte_pointer;

    不带参数:

     

     带参数1

     带参数3.14

     typedef char *byte_pointer;

    不带参数

     

     带参数1

     带参数3.14

     typedef int *byte_pointer;

    带参数3.14

    带参数65535

    分析运行结果 

    由以上结果可得,我的电脑的储存模式是小端模式,它将按照从最低有效字节到最高有效字节的顺序列出字节(低对低,高对高),这段代码其实也是对小端模式的验证

     对三种类型的byte_pointer的运行结果存在差异的思考:

    当typedef unsigned char *byte_pointer;改成用有符号的char型时,会出现补符号位置的形式;字符型才是地址是+1存储; 通过仔细对比上述两种结果可得出结论,此外若改成int型,则是另一种结果,出现地址+4的情况,这与unsigned char型、char型、int型在内存中的存储方式有关

    小结

    show_bytes是我在学习《深入理解计算机系统》(CSAPP)第一部分第2章后开始接触了解的程序,第一次从PPT上看到这个程序时,我是很茫然的,因为其中有很多比较陌生的用法和结构,哪怕之后在ubantu上运行时也只是大致了解它的作用。

    我查阅书籍、浏览各类编程网站、询问老师同学,现在,经过完成这次学习日志,我对show_bytes的原理、其中一些函数的应用和ubantu的使用有了很大的进步。

    CSAPP实验1:Data Lab

    第一次学习老师推荐的不周山笔记『读薄/读厚』系列,我真的是欢喜至极,如获至宝。《深入理解计算机系统》这本书是本大部头,量大且杂,如果只看课本的话,学习效率很慢,老师推荐给我们MOOC袁春风教授的计算机系统基础网课和不周山笔记帮我们解决了这个难题,通过网课和PPT课件学习,很容易知道知识重点难点,再结合不周山笔记读薄篇,进行知识点的回顾和概括总结,学习变得有目的有方向,知道该怎么学习,该往哪方面学习,而教材是我们查漏补缺,拓展学习的工具。

    不周山笔记读薄系列主要是点出书本中的重点概念,读厚部分则是专注于七个实验的思考,我主要看了一下第一个实验Data Lab,其中大多数题目都有详细的解答,我挑选了给我启发最大的整数运算题目进行记录,方便以后查阅复习,浮点数就算了,我真的害怕浮点数的运算,总觉得自己是错的,以后有就会再慢慢看。

    isLessOrEqual

     

    代码:

    int sigx = (x >> 31) & 1; int sigy = (y >> 31) & 1; int sig = sigx ^ sigy; int v = y + (~x + 1); return (sig & sigx) | (!sig & (~(v >> 31) & 1));  

    分析:

    当我们习惯性的用y-x来判断时,结果可能不正确,因为y-x在异号时可能会导致溢出,所以我们把y-x的结果与符号、x和y的符号来一起判断

    当x和y同号且相减结果的符号位为0或者x和y异号且x的符号位为1时该结果成立

    我们记sig为判断x和y的符号是否相同,如果不同且x的符号位为1,说明x是负数,y是正数,此时x <= y一定成立

    如果x和y的符号相同,此时不会导致溢出,因此直接取结果的符号位来进行判断

    logicalNeg

    计算 !x 而不用 ! 运算符

    Examples: logicalNeg(3) = 0, logicalNeg(0) =1

    代码:

     

    要求: isLessOrEqual - if x <= y  then return 1, else return 0     如果x小于等于y则返回1,否则返回0Legal ops: ! ~ & ^ | + << >> Max ops: 24 Example: isLessOrEqual(4,5) = 1

    int logicalNeg(int x)  {

      return((~(~x+1)&~x)>>31)&1;

    }

    分析:

    令y=~x+1,考虑x与y的符号位:

    当x=0x0时,x,y两者符号位都为0;当x=0x8000 0000时,x,y两者符号位都为1;

    否则,两者符号位为01或10;由(~x)&(~y),则当且仅当x=0x0时,其符号位为1。

    bang

    要求:输出!x

    int bang(int x)

    {

            return ((~((~x+1)^x))>>31) & 0x01;

    }

    若x=0, ~x+1和x的符号位都为0,而其他情况则至少有一个数符号位为1

    也可能两个符号位都为1的情况,如x=0x80 00 00 00,所以不能用^

    或运算之后取反,再取符号位

    小结

    通过不周山笔记系列的学习,收获良多,但我感觉只是现在颇有感悟,以后不知什么时候用得着,可能很快就忘了,就像完成某项任务。纸上得来终觉浅,绝知此事要躬行,我完成这个日志,也算是对自己的勉励,不断提醒自己经常查阅复习。

    不周山笔记:

    https://wdxtub.com/csapp/thick-csapp-lab-1/2016/04/16/

    最新回复(0)