CSAPP SHOW

    xiaoxiao2025-05-28  96

    关于SHOW_BYTES的学习经验

    以下内容主要是我对这篇代码的学习经验与总结,对各个地方都会有详细的批注解释。文章可能存在一些纰漏,欢迎大家指正!

    阅读顺序提示: 1.按照main函数的顺序对代码依次进行解释,并附有相应运行结果 2.完整的代码

    一:关于输入新数据代码解释

    typedef unsigned char *byte_pointer; ① typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。 ②在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。int main(int argc, char *argv[]) ①我们通常说main函数不能带参数,实际上,main函数可以带参数,这个参数可以认为是 main函数的形式参数。C语言规定main函数的参数只能有两个, 习惯上这两个参数写为argc和argv。 ②argc参数表示了命令行中参数的个数(注意:文件名本身也算一个参数)argc的值是在输入命令行时由系统按实际参数的个数自动赋予的。 ③argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。 指针数组的长度即为参数个数。数组元素初值由系统自动赋予。 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

    if (argc > 1) “2”中提到过“argc”表示命令行中参数的个数,而文件名本身也算一个参数,不带参数时argc=1,所以判断是否输入新参数的条件为(argc>1) 此处的if/else语句用来判断程序是否输入新的数据

    val = strtol(argv[1], NULL, 0); printf("calling test_show_bytes\n"); 如果输入新数据,新数据的值存储在argv[1]中 strtol函数会将参数nptr字符串根据参数base来转换成长整型数,参数base范围从2至36或0 strtol(const char *nptr,char **endptr,int base);

    show_bytes 这个函数用于传递无符号字符型指针及所指对象的字节数 并依次输出每个字节的存储地址和值 %p:输出地址 %.2x:宽度为二,右对齐方式,位数不够左边补零。X表示输出十六进制数。 star[i]表示从star[0]开始第i个位置的字节

    show_int/float/pointer 这三个函数将不同类型对象的指针都强制转换成unsigned char*类型,并用sizeof表示该对象所占字节数

    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 *)); }

    输入数据65535后的运行结果

    int/float:32位表示 pointer:64位表示

    二.关于程序自动运行部分解释

    1.show_twocomp

    此函数用于输出一个short型数据及其补码的二进制表示

    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 */ }

    simple_show_a/b

    此函数用于向读者展示机器在小端模式下如何存储数据 低位数据存放在低地址处,高位数据存放在高地址处

    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 */ }

    float_eg 此函数用于输出整型及其转换成浮点数之后的二进制数据 将整型转换成浮点型 float f = (float) x; 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); }

    string_ueg/leg 此函数将字符串对应的ASCII转换成十六进制输出 A对应65(0X41) a对应97(0X61) 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 */ }

    至此,解释部分结束

    以下是三种不同的类型定义,我们选用第一种!

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

    接下来我们解释为什么选择第一种类型定义:

    unsigned char、(signed) char、(signed)int

    在C语言中,char型等价于整型数据,占一个字节。 char占一个字节,int占4个字节。表示的数据大小不一样 在C中,默认的基础数据类型均为signed,首先在内存中,char与unsigned char没有什么不同,都是一个字节,唯一的区别是,char的最高位为符号位,因此char能表示-128-127, unsigned char没有符号位,因此能表示0-255。 因为符号位的区别,在普通的操作中,主要看你怎么理解最高位(符号位)。

    但是我们却发现在表示byte时,都用unsigned char,这是为什么呢?

    对于机器来说,byte没有什么符号位之说,但是要把一个char类型的变量赋值给int、long等数据类型或进行类似的强制类型转换时时,系统会进行类型扩展,这时区别就大了。 对于char类型的变量,系统会认为最高位为符号位,然后对最高位进行扩展,即符号扩展。若最高位为1,则扩展到int时高位都以1填充。 对于unsigned char类型的变量,系统会直接进行无符号扩展,即0扩展。扩展的高位都以0填充。 所以在进行类似的操作时,如果char和unsigned char最高位都是0,则结果是一样的,若char最高位为1,则结果会大相径庭。 (int同理,会进行符号扩展,可能会改变数据原有的值) 同样的 unsigned char*、(signed) char*、(signed)int* char*或int*是有符号的,如果大于127即0x7F的数就是负数了,使用%x格式化输出,系统自动进行了符号扩展,就会产生变化。

    因此我们选用unsigned char*

    unsigned char*的部分运行结果 char*的部分运行结果

    int*的部分运行结果 这些运行结果其实我也还有一些不懂的地方,但是我会通过学习进步的!

    SHOW_BYTES完整代码

    #include <stdio.h> #include <stdlib.h> #include <string.h> 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; }

    感谢大家阅读呀!

    最新回复(0)