CSAPP基本版实验学习日志:关于locate代码的阅读以及在ubantu上的运行

locate代码内容:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

static void show_pointer(void *p, char *descr) {
    //    printf("Pointer for %s at %p\n", descr, p);
    printf("%s\t%p\t%lu\n", descr, p, (unsigned long) p);
}

char big_array[1L<<24];    /*  16 MB */
//char huge_array[1L<<31];   /*   2 GB */
char huge_array[1L<<30];/*   1 GB */
int global = 0;

int useless() { return 0; }

int main ()
{
    void *p1, *p2, *p3, *p4;
    int local = 0;
    p1 = malloc(1L << 28);
    p2 = malloc(1L << 8);
    //p3 = malloc(1L << 32);
	p3 = malloc(1L << 16);
    p4 = malloc(1L << 8);

    show_pointer((void *) big_array, "big array");
    show_pointer((void *) huge_array, "huge array");
    show_pointer((void *) &local, "local");
    show_pointer((void *) &global, "global");
    show_pointer((void *) p1, "p1");
    show_pointer((void *) p2, "p2");
    show_pointer((void *) p3, "p3");
    show_pointer((void *) p4, "p4");
    show_pointer((void *) useless, "useless");
    show_pointer((void *) exit, "exit");
    show_pointer((void *) malloc, "malloc");
    return 0;
}

下面我将分别解释:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include<stdio.h>是编译预处理命令,即在程序编译之前要处理的内容,<>中以以“.h ”作为结尾的文件称为头文件,如stdio.h,stdlib.h,string.h均为头文件。
stdio 即 “standard input & output"(标准输入输出)在开头加上#include <stdio.h>即可实现c语言中的 输入函数printf、输出函数scanf等函数。
同理,在开头加上#include <stdlib.h>即可实现c语言中的exitmallocfree等函数。同样的,在开头加上#include <string.h>即可实现关于字符数组的函数,例如连接字符串函数strcat,复制字符串函数strcpy,比较字符串(区分大小写)函数strcmp等。
注意这里的#include<unistd.h>不是C语言的东西,是linux/unix的系统调用。它包含了许多UNIX系统服务的函数原型,例如read、write、close 等等。unistd.h在unix中类似于window中的windows.h。,因此它是是依赖于编译器,依赖于操作系统的。更多关于#include<unistd.h>可参考:#include<unistd.h>头文件的理解

static void show_pointer(void *p, char *descr) {

定义一个无返回值的函数show_pointer。而函数前加static 使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。更多关于static可参考C语言static关键字
该函数有两个形参,分别是没有数据类型的指针p和字符型指针descr。void 指针可以指向任意类型的数据,就是说可以用任意类型的指针对 void 指针对 void 指针赋值。void 指针其余相关知识可参考:C 语言中 void * 详解及应用。主函数输入的实参必须与形参类型匹配,注意实参与形参有各自的存储空间,如不使用指针则形参值的改变不会影响实参。char 其余相关知识可参考:关于指针区分char *与char[]

//    printf("Pointer for %s at %p\n", descr, p);

//符号:里面内容为C语言的一行注释,不会被运行,注意//只注释一行,而/ * 符号和 * / 符号可以注释多行。注释里面内容为打印“Pointer for descr的值 at p的值”并换行。%s用来输出一个字符串,%p是打印地址的,以主机的格式显示指针,即变量的地址。会按十六进制输出数据。
若运行这行程序,则为显示指针descr和它所在的地址。

	printf("%s\t%p\t%lu\n", descr, p, (unsigned long) p);
}

同上面解释,这行语句表示打印descr字符串,p指针的数据,和将p强制转换为无符号长整型(unsigned long)并打出它的无符号十进制整数。
%lu即用来打印无符号长整型十进制数。unsigned long,无符号长整型,long在32位机器为4个字节,在64位机器为8个字节,无符号即不会出现负数。
这里出现了两个转义字符,’\n’是一个转义字符,它是换行符,即实现回车换行的功能。另一个转义字符是’\t’,它的功能是横向跳到下一制表位置。
关于转义字符:C语言字符型数据(字符)

char big_array[1L<<24];    /*  16 MB */
//char huge_array[1L<<31];   /*   2 GB */
char huge_array[1L<<30];/*   1 GB */

定义两个字符型全局变量数组 big_array和huge_array。在所有函数外部定义的变量称为全局变量,它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。该数组定义得非常大。<<是左移运算符,在程序运行时可用作乘法。在二进制的计算中,左移n位即乘2的n次方。1L<<24即2的24次方,其值为16777216,换算为16MB。1L<<30即2的30次方,其值约为1.073742e+9,换算为1024MB即1GB。因此在此处big_array数组具有大约16MB个字符型元素,huge_array数组具有大约1GB个字符型元素
补充:算数运算符

int global = 0;

定义一个整型变量(int)。整型变量,在内存中占4个字节,最长能到32位二进制位,其取值为基本整常数,在该定义中,变量名为global,并赋初值0

int useless() { return 0; }

定义一个返回值类型为整型(int)的函数,函数名为useless,没有参数内容只有返回值为0的指令。

int main ()
{

有main()函数的程序才能运行,即函数必须被main()直接或间接调用才能发挥作用,主函数的返回值类型为int。科普:int main(int argc,char** argv) 详解

	void *p1, *p2, *p3, *p4;

定义四个没有数据类型的指针p1,p2,p3,p4。

	 int local = 0;

定义一个整型变量(int),其变量名为local,并赋初值0。

	p1 = malloc(1L << 28);
	p2 = malloc(1L << 8);
	//p3 = malloc(1L << 32);
	p3 = malloc(1L << 16);
	p4 = malloc(1L << 8);

malloc函数用来动态地分配内存空间,它的头文件为#include <stdlib.h>。
这里是将p1,p2,p3,p4分别分配很大的空间。
1L<<28即2的28次方,其值约为2.684355e+8,换算为256MB。
1L<<8即2的8次方,其值为256,换算为0.25KB。
1L<<16即2的8次方,其值为65536,换算为64KB。
1L<<32即2的32次方,其值约为4.294967e+9,换算为4GB。
更多可参考:C语言malloc()函数

show_pointer((void *) big_array, "big array");
show_pointer((void *) huge_array, "huge array");
show_pointer((void *) &local, "local");
show_pointer((void *) &global, "global");
show_pointer((void *) p1, "p1");
show_pointer((void *) p2, "p2");
show_pointer((void *) p3, "p3");
show_pointer((void *) p4, "p4");
show_pointer((void *) useless, "useless");
show_pointer((void *) exit, "exit");
show_pointer((void *) malloc, "malloc");

多次调用 show_pointer函数,并将所有第一个参数强制转换为没有数据类型的指针。第二个参数在函数中功能为以字符串形式输出它们各自的变量名。

return 0;
}

主函数返回0表程序结束。
以下为该代码在ubantu上运行结果:

gec@ubuntu:/mnt/hgfs/share/csapp_code$ gcc locate.c
gec@ubuntu:/mnt/hgfs/share/csapp_code$ ./a.out
big array	0x4804a060	1208262752
huge array	0x804a060	134520928
local	0xbfcc9fdc	3217858524
global	0x804a044	134520900
p1	0xa7545008	2807320584
p2	0x49a67008	1235644424
p3	0x49a67110	1235644688
p4	0x49a77118	1235710232
useless	0x80484b6	134513846
exit	0x8048370	134513520
malloc	0x8048350	134513488

综上,该程序的运行结果为先输出它们的变量名,再输出它们的存储地址,再输出它们的地址的十进制转换数。可以看到big array和huge array两个全局变量由于占空间过大,各自首地址之间相差0x40000000。而huge array和global存储地址相近,三个函数useless,exit,malloc的地址也相近,这是因为它们存储位置相近。
该程序分别展示了全局变量、函数、局部变量等的存储位置。全局变量global和静态变量static存放在数据区Data。变量p1,p2,p3,p4分配得是动态存储空间,因此存放在堆Heap里,堆位于栈和数据区之间,而栈是从高地址向低地址增长,但是堆是从两边向中间增长,所以p1可能是从高地址往低地址增长,所以p1的地址特别大,然后p2,p3,p4是从低地址往高地址增长,所以它们的地址比较小。而无论是系统的函数还是自己定义的函数,都放在代码区Text。这就是该程序所要展现的。
附图:该程序各个变量所存位置

关于计算机
该程序各个变量所存位置如图
程序运行过程
博客参考:《深入理解计算机系统》第三章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值