三个一工程比较有意思的题汇总

Near far 近远指针。*(char far *)

*(int  *)0x2000=0xf; 
*(char *)0x2000=’a’; 

((char *)*(int far *)0x02000000)
malloc20)是开辟20个字节的内存空间。

以下地址,本意是指内存地址。不然实在要说的话,那什么东西肯定都有地址喽,只要它依赖了硬件,我们看得见摸得着检测得到,没有地址才有鬼喽,现在我是只比较一下最常用法的不同。
1>.变量有名字吗?变量有地址吗?
变量有变量名;变量有地址。
2>.寄存器有名字吗?寄存器有地址吗?
寄存器有寄存器名;寄存器没有地址(此处切记与arm单片机等区分开,现在谈论一下单纯的C语言)。
3>.内存有名字吗?内存有地址吗?
内存没有名字;内存有内存地址。
4>.端口有名字吗?端口有地址吗?
就目前看到的,好像常用的就是地址;名字?有吗?存疑。

1>.用C语言编程可以不用变量吗?
可以,综合研究二得出其中一种额外选择:可以使用寄存器替换变量的使用。
2>.函数名、变量,这些区别于汇编的新生名词,在C语言反汇编为汇编语言的时候又是具体以怎样的熟悉的形式展开的呢?
相对于函数,很容易就想到了子程序,包括子程序的标号、调用、返回等都与函数感觉有某种近亲关系一样。变量的话,存在的形式,还不确定就是唯一的形式。
3>. 用debug怎么查找函数的偏移地址?
参考语句“printf(“%x\n”,main);”,最好能举一反三。

1>.c0s.obj文件的作用
c0s.obj文件的作用:在程序开始运行,进行相关初始化,再调用main函数,返回后进行相关的资源释放,环境恢复等工作,再将程序返回。
2>.可以不用main函数编程吗?
可以。只不过需要修改相应的原来在程序开始运行时,负责进行相关初始化操作、调用main函数、返回后进行相关的资源释放、环境恢复等工作的文件。在本次研究中用到的是c0s.obj文件,同样的道理,以后的话要学会举一反三。

1>.头文件的作用和生成的四个阶段?
1.预处理(include这个库寻找定义头文件的内容插入源程序中生成.i文件)
2.编译(将.i文件翻译成生成.s后缀的汇编语言程序)
3.汇编 (将.s文件翻译成机器语言,生成可重定位的目标程序,打包生成.o文件)
4.连接(将各种代码和数据整合成一个单一可执行文件)

3>.有关于程序的的编译过程?

虽然最终都会编译成机器码,但存在有的程序不必编译成汇编语言,c语言必须转变成汇编语言,因为C语言是汇编编译。之后会讲到“汇编器”相关知识。

4>.Main与Main()的区别?
在用printf()打印main时,打印出的是main()的偏移地址;而在打印main()时,得到的数是main()的返回值。但是这个返回值的选取又涉及到之前函数将返回值保存在哪里的问题,由于原本编写的main()返回值保存在ax寄存器中当我们再编写一个函数,并且该函数并没有在作用过程中修改ax寄存器的值。我们得到的该函数的返回值仍然是上一次保存在ax寄存器中的值。

5>.Sp、bp问题?
作用一:与SP联合使用,SP不能轻易更改,所以赋值给BP,用BP来实现我们的要求;
作用二:栈帧,相当于一个链表,bp,sp代表一个栈帧的序列,为了方便调用其他函数,返回之后恢复调用前状态。

而在这里,再回头看一下在这一过程中指点迷津、至关重要的那条语句“printf(“%x\n”,main);”格式如此,是否可以理解为这里的函数名简直是在当作变量用,但是区别呢?联系呢?函数名、变量,这些区别于汇编的新生名词,在C语言反汇编为汇编语言的时候又是具体以怎样的熟悉的形式展开的呢?
相对于函数,很容易就想到了子程序,包括子程序的标号、调用、返回等都与函数感觉有某种近亲关系一样。

1>.既然大家都是变量都是用来存储的,为啥放的位置还不一样呢?或者全局变量为啥不用栈?局部变量为啥不选数据段?

这个肯定的,首先全局变量,就是要让它从定义起就可以在程序中任意位置使用,如果选择栈了,栈用完就pop了,其他地方还咋用呢?这就是全局变量不选栈;
局部变量呢?如果所有的数据都直接放在数据段中,局部变量也变成了直到整个程序运行完毕才会得到释放的话,那么如果我们定义的变量越来越多。。。。。。一方面增大了内存的开销,另一方面,这个“局部变量”哪里都可以修改,函数的独立性不好保证。
2>.程序1中,全局变量n,是由“unsigned int n”这条语句定义,还是由main函数中的“n=0”这条语句定义?
应该是由前者定义的,函数外定义的变量,不管有没有加static,没有初始化的话,系统默认初始化为0。如果在n=0语句之前打印n,是能够打印出它的值的。

搜到的网上资料:
当启动连接程序时,它会寻找“未定义的外部函数”,也就是说,它将在每一个库文件中查找源代码文件中未定义的函数。当它找到一个未定义的外部函数后,它会引入包含该函数定义的目标代码(obj)。不幸的是,如果这个函数是在一个包含其它函数定义的源文件中被编译的话,那么这些函数也会被包含进来,你的可执行代码中将包含一些不需要的代码。因此,将库函数放到各自的源文件中是很重要的——否则会浪费宝贵的程序空间。有些编译程序包含特殊的“精明的”连接程序,这些连接程序能查出不需要的函数并去掉它们,从而使这些函数不再进入你的程序。

一、内存对齐:节省时间,以空间换时间。看资料自己推演。vc下可以写代码规定内存对齐。(希望大家的研究要有深度)

同样的观察汇编代码可以发现(a+n)等价于(&a[0]+n),即a与&a[0]是等价的。这是因为a[0]是数组的第一项,所以它的地址就是数组a的地址。
所以数组名就是一个指针,[]运算是在指针的值上加上一个偏移量,使其指向下一个存储单元的数据。

结合之前学习的汇编,计算机中最多的就是数字了。这些数字,在c语言中从不同的角度看,具有不同的使命。数据、指令、地址。但是三者之间又是相互联系的。啊呀,一拍脑门,这不又与计算机的组成中三大总线数据总线、控制总线、地址总线一一对应起来了吗?可以,这很指针。

是否可以自己编写一个功能与sizeof关键字相似的函数呢?如果打算编写,自己会从什么角度出发?
Sizeof是求字节数,字节也就是内存中一种绝对的单元长度,如果打个比方,我觉得他就像尺子中的厘米单位,当然比他更小的还有毫米,对应的,位可以填充。同样sizeof好像就是一把丈量的尺子一样,他是一把短小精悍的工具,厘米——字节、毫米——位等都存在于内存中,但并不意味着内存更贴近尺子,当然是与sizeof在此处相较而言。内存是个容器,是字节们、位们的容器,是他们的世界,而我们的世界,又是我们的容器。我如果想测量一段字节多长,我会怎么测呢?我想了想我在现实生活中的使用尺子的时候一般是这样做的:
第一种:我会将尺子的0刻度对齐欲测量字节段的首或尾端,然后查看对应的尾或首端读出的“尺子刻度”是多少厘米,也就是多少字节,是多少毫米,也就是多少位。。。。。。
第二种:有时候尺子的0刻度可能断掉了,被墨迹玷污了。。。。。。这时候我会随便找个刻度,当然现实生活中一般选取整厘米处,内存选取整字节处。然后记下这个刻度值,将尺子的这个刻度对齐欲测量字节段的首或尾端,然后查看对应的尾或首端读出的“尺子刻度”,然后两值相减取绝对值。也就是多少厘米,也就是多少字节,是多少毫米,也就是多少位。。。。。。
编写程序的话,会选上面两种。但是还有两个问题。
1>.sizeof是关键字,不是函数;
2>.sizeof能实现对结构体中出现内存对齐情况的正确计算。

一般的大型工程都会在生成可执行文件的同时让链接器生成一个map文件,从而大致查看一下可执行文件中符号的内存布局以及从哪里引入可执行文件。这个通常对于小型工程是作用不大,因为代码就那么多,随便grep一下就知道符号定义位置了。但是对于一些大型工程或者涉及了比较多的第三方库、或者涉及了比较多的功能模块的时候,就需要知道这些符号是在哪里定义,或者说如果一个符号引用了但是没有知道函数定义,此时也需要找到这个符号是哪个模块引入的,为什么需要,所以需要一些通用的(形式化)的方法来搜索这些符号,而map文件就是一个比较好的切入点。

1.针对静态库、动态库占用内存资源问题。对比两者的实现。
静态动态的关键区别(学长的):加载的时间不同。
还有什么其他异同:针对静态库和动态库中存放的文件。静态库是多个*.obj文件的集合,有的同学查出资料说动态库是可执行程序文件的集合。
动态库、静态库都是模块。

动态库是调用的时候加载,因为如果一运行就加载,那动态库和静态库还有什么分别呢?
库: 指由标准常用函数编译而成的文件,旨在提高常用函数的可重用性,减轻开发人员负担。常用的sdtio.h,math.h等。库便是C函数库的冰山一角。
1.静态库:指编译链接阶段将整个库复制到可执行文件
1.1优点:静态链接的程序不依赖外界库支持,具有良好的可移植性。
1.2缺点: 每次库更新都需要重新编译程序,即使更新很小或只是局部。
1.3缺点:每个静态链接的程序都有一份库文件,存储时增加了硬盘空间消耗,运行时则增加了内存消耗。
2.动态库:指直到运行时才将库链接到可执行程序
1.1优点: 动态链接方式的程序不需要包含库(编辑链接时节省时间),占用的空间小很多。
1.2优点: 运行时系统内存只需提供一个共享库给所有程序动态链接,内存消耗减少。
1.3缺点: 需要系统中动态库支持才可运行,可能有动态库不兼容问题

1.(学长的)指令“ c = *c + 1;”c为int类型,这条指令读写了几次内存?
1>.读c的内容;
2>.读*c的内容;
*3>.读c的内容;
4>.写*c。
这里注意一下:*c在算过一次地址后要再算一遍。

1.(学长的)指令“c = &ch;”c为int*类型,ch为int类型,这条指令读写了几次内存?
ch的地址并没有存放在内存中,所以访问&ch并不需要访问内存)写一次。
相关汇编代码:
Mov ax,offset ch
Mov [c],ax

3.(学长的)Push [bx]
运行这一句,访问了读指令、算地址、取一次数据、入栈4次指令。

对于强类型语言c语言中,当然不止是c语言,这些语言中都有很多的描述性语言,但是有时候当一件拥有大量陌生名词的学习事物充斥在初学者身边时,很多时候,其实是排斥的。而且有很多概念如果拿以前已经学习吸收的知识组装成新知识的理解框架之后,相比于直接抛出这个陌生的概念,学习效果可能也会很不错。“=”右边的表达式中,放在变量、数字之前的,目前来看大部分看作“强制类型转换”来理解,特例如果有可以举出来,这样对于大家理解指针函数、函数指针、指向指针的指针等较复杂概念与一般化的(int)、(char)等大家熟悉的强制类型转化性质进行统一和类比,更容易消化吸收。“b =(int) a”、“ b =(int ) a”、“ b =(int *) a”等等,甚至将“b =& a”,也就是“b =(&)a”,也归入“强制转换类型”,好像也说的通。还有“b =* a”,也就是“b =()a”,从这个角度,我自己觉得也是。然后如果p是一个指向字符的指针,“b = p[3]”,这个呢?好像有点不像了,可是之前说的等价式:“b = (p + 3)”,也就是“b = ()(p + 3)”,这样好像和前面我认为的同样属于强制类型转换应用的“b =()a”有点像呀,只不过这里(p+3),抽象化,两者都是指针,属性不变呀,所以,这种也算吗?嗯,有点不确定。学长,您说呢?还有其他的什么情况可以归到这里呢?还需要再想想,从长计议,好好总结。

1.打印char类型,要以无符号形式打印16进制。因为char类型是1个字节。打印%x是压入2个字节,这时要进行扩展,如果是以有符号形式打印的话,则扩展后为ff80,(假设打印80h),这样就不合适,因为前面的bit位都变成了1,而用无符号的话,扩展出来就成了0x0080,这样就很合适。

这样,也就涉及到快速数数的方面。我们对计算机存取数值的灵敏感知,其实没有必要非得看什么原码、反码、补码。想出这些只顾着理论性强的繁琐词汇,并想要学生们非得记住的,一定一定不是一个好主意,而很多学校老师们就很是这样,不仅喜欢教,还喜欢这样考,真的受不了。其实数一直是连续的,至少,是线性排列的。就好像最经典的一维坐标轴:


——————————————————————————————————>


对于无符号数,比如无符号整型,数值范围是(0~255)同样模仿一维坐标轴的形式列出来:


————————————————————————————————–> 增大
0 1 2 …… 127 128 129 …… 253 254 255
00h 01h 02h …… 7fh 80h 81h …… Fdh feh ffh 补码


其中从始至终,箭头方向,就是数值单调递增的方向。

对于有符号数,比如有符号整型,数值范围是(-128~127)同样模仿一维坐标轴的形式列出来:


——————————————>|———————————————————>增大
0 1 2 …… 127 | -128 -127 …… -3 -2 -1
00h 01h 02h …… 7fh | 80h 81h …… Fdh feh ffh 补码


其中从始至终,箭头方向,就是数值递增的方向,但是并不是单调递增,分为了两大部分:“0~127”和“-128~-1”。接着中间这个划分两部分,也就是划分“正负”的界限,就更熟悉了,此处的“127”和“-128”之间,也就是正好二分处。

接着考虑进来之前所说的数是线性排列的概念,思考学长提出的扩展成有符号四字节之后,“-128”又是怎样表示的,数值范围是(-32768~32767)同样模仿一维坐标轴:


——————————————>|——————————————————>增大
0 1 …… 32767 | -32768 -32767 …… -2 -1
0000h 0001h …… 7fffh | 8000h 80011h …… fffeh ffffh 补码


其中,同样-32768 到 -1也是线性增大的,而在之前的有符号整型,-128距离 -1是80h 到ffh 的距离,这样推算下来,-128扩展之后的表示,就是ff80h。

而在这个过程中,补码是一直线性连续的,这样就比较直观,好记忆理解了。

在不同的系统中,
NULL并非总是和0等同,NULL仅仅代表空值,也就是指向一个不被使用的地址,在大多数系统中,都将0作为不被使用的地址,所以就有了类似这样的定义

#define NULL 0

但并非总是如此,也有些系统不将0地址作为NULL,而是用其他的地址,所以说,千万别将NULL和0等价起来,特别是在一些跨平台的代码中,这更是将给你带来灾难。

结果表明用函数指针p和用变量b强转,都实现了调用函数的效果。

这里最令我感兴趣的就是“a = ( (int ()(char,char) )b )(3,4);”这一句,因为他又让我想起了自己之前那段逻辑不清的表述,这里也是可以归类于其中的另一种“强制类型转换”,尽管书上没有教过我们这个道理,但它的确是存在的,这个程序恰恰验证了。“(int ()(char,char) )b”这里就是将整型变量b强制类型转换为“返回值为int,参数为char、char的函数的函数指针变量类型”。而之所以可以实现f()函数的功能,是因为在这之前b的值已经通过“b =(int)f”赋值为f()函数的首地址了。

看到这里,感觉之前自己认为的“(&)a”,这里a是一个普通变量,假设是整型吧,可以有另一种自己觉得更好的解释,本来a的地址与变量a以及变量a的值就是一体的,但是我们平时默认的就是正面的角色-》a的值,而(&)a自然就是反面角色了。但是正反从来都是一体的。看过不少武侠书籍之中,总会提到一种常见的机关门,按下机关,门就会翻转,主人公就能抵达暗室了。在这里,a的值,以及(&)a的值,就像是这扇会翻转的门的正反两面,而他们从来都不是分割的,因为他们都在a这扇门上。嘿嘿,感觉好像是在“翻牌子”;那么“*p”呢?其实我更喜欢“->”的标记来描述。这里p是一个普通的指针变量,假设是整型指针吧,它又有什么自己的解释呢?它更像个穿越门,穿越空间,不穿越时间。到了链表中就更好说了,通过p可以穿越到哪里,制定目的地的主人,就是我们了。多个穿越门可以到达同一个地方,一个穿越门可以修改到达的目的地,穿越了之后可以再次穿越。。。。。。我去 ,怎么有点哈利波特的感觉了?哈哈。希望以后现实中也可以有这种超级黑科技。应该,可以实现吧?

1.sizeof和strlen的区别

Sizeof是预处理时候得出的,strlen是库函数,运行的时候根据“\0”进行计算

粗浅的理解:
Continue

#include <stdio.h>

void printWelcome(int len)
{
    printf("welcome -- %d\n", len);
}


void printGoodbye(int len)
{
    printf("goodbye -- %d\n", len);
}

void callback(int times, void (* print)(int))
{
    int i;
    for (i = 0; i < times; ++i)
    {
        print(i);
    }
    printf("\nwhat am i doing!\n\n");
}
void main(void)
{
    callback(3, printWelcome);
    callback(3, printGoodbye);
    printWelcome(3);
    printGoodbye(3);
}

这里,callback()是可以调用两个回调函数( printWelcome()、printGoodbye() )的函数,这两个回调函数“类型”是一样的。Callback()这里就像普通的整型变量一样用“这两个回调函数的类型”定义了一个形参变量给自己,另外还有一个变量,是在main()函数中可以提供的。因为打印时候的i本质上是受传入的参数times影响和控制的。
所以这个有什么用呢?网上对这个讲的真的是五花八门,看着都能睡着了。我就觉得他让我最感兴趣的就俩点儿:
1.把函数像变量来使用,通过使用函数的地址、函数指针等;
2.在最外面的函数,这里就是main(),通过在main()函数里面进行操作,也能很轻松按照我们想要的方式控制和影响最里面的函数的变化,从而实现最终结果变量化的功能。

表面上看,中间函数callback()可以通过函数指针形参来选择回调函数,最外层函数又可以通过times传参影响callback函数,但是这个times参数还会对回调函数产生影响,而且其实最终目的就是为了影响最内层的回调函数。
我该不会越讲越乱了吧。

变量:

变量是一种存放数据方式,与常量不同,它的内容是可以改变的。它可以分为全局变量和局部变量,它们的本质区别是存储的位置不同,全局变量是在内存中存储的,而局部变量是在栈段中存储的,这个差别导致了它们的一系列区别:全局变量存储的内存空间是没有内存对齐的情况的,而局部变量有;全局变量作为参数传递是直接用地址调用,而局部变量是入栈的方式;全局变量的生命周期是整个程序,而局部变量的生命周期是当前函数;全局变量的段地址在ds寄存器里,局部变量的段地址在ss寄存器里;全局变量定义是自动清零的,而局部变量定义时在栈中的空间还是原来的数据。比较特别的静态局部变量的存储位置和生命周期都和全局变量一样,只是静态局部变量只能在定义的函数中使用。

比较重要的变量类型有char、int、long、double和结构体,它们分别占的大小为1字节、2字节、4字节、8字节,结构体的大小是结构体中数据项之和。结构体也存在内存对齐的情况,结构体中各数据项存储位置是相邻的。结构体作为参数传递和返回比一般变量要复杂,一般变量都是直接入栈,而结构体必须创建一个临时变量,用块搬移函数将结构体的各数据项复制到临时变量里,在子函数里再将临时变量的值搬移到栈段里面,返回的原理也是相同的。

数组:

数组是利用一段连续的内存空间存放一系列相同类型的数据。一维数组是存储的数据按照线性的顺序来排列,二维数组是存储的数据按照类似围棋棋盘的顺序来排列,多维数组是存储的数据以多维的形式存储,我们可以通过当前一组不断向下查找到某一个元素。数组也可以根据存放的元素类型不同来分类:整型数组的元素是int型数据、指针型数组的元素是指针型数据、结构体数组的元素是结构体数据。数组中的元素是连续存放的。数组名相当于数组的首地址,也是数组第一个元素的地址,它的使用和指针有相似之处,如果p是一个指针,那么p[n]等同于*(p+n),即跳到下一个元素就相当于在当前地址上加上数组元素的类型大小。数组还有函数指针数组,存放的元素是函数指针,指向函数的指针,函数指针数组可以将要运行的程序以数据的形式写入并对函数进行调用。

函数:

函数是一段指令的集合。函数名相当于一个函数指针,存储函数的入口地址,程序由这个入口地址跳转到当前函数。函数的参数是局部变量,在调用该函数的函数中将参数压入栈中,在子函数里用bp寄存器找到参数的地址进行调用。函数可以有返回值,void函数没有返回值,函数的返回值一般是存储在寄存器中,如果返回值为结构体,则将结构体的内容传递到一个临时变量里。函数是一段数据,它同样存储在内存空间里,这样我们可以以数据的形式将一个函数写到内存中执行。

指针:

指针存储的数据是一个地址,我们可以通过“*”来取得指针存储的这个地址处的内容,通过“&”来取得一个内存空间的地址赋给指针。指针加减一个数并不是以它的值加减一个数字,而是加减它所指向的存储空间的数据类型的大小,即如果它指向的是int型数据,那么加1就是在当前地址上加上2个字节。我们可以将一个地址赋给一个整形变量,但我们不能对一个整形变量使用“*”取得它所存储的地址处的值,因为它不是一个指针,同时如果一个指针是一级指针,即定义成*p,那么只能用“*”对它取一次值,如果一个指针是二级指针,可以用“*”对它取两次值,总之,一个指针是几级指针,就可以对它取几次值。对于指针的使用我们一定要注意它和其他的变量的类型匹配问题,近指针占2个字节,存储偏移地址,远指针占4个字节,存储段地址加偏移地址。虽然指针的值的大小是固定的,但是指针指向的值的大小和指针的定义有关。指针在地址和内容之间建立了一条联系,这种联系是c语言最重要的基础,我们可以用它来实现多种数据结构。指针可以指向任意的数据类型,结构体指针指向的是一个结构体,它可以以->符号调用结构体的数据项。函数指针是指向一个函数入口的指针,当定义一个函数指针时要指明函数的类型和参数类型和个数,通过函数指针可以调用指定位置的函数。

1、重新总结针对12的关于共性和个性的问题

研究12中b.c程序的共性和个性的思考可以分成几个部分。
第一部分:通过函数指针数组func[n]中选择n的值来实现调用不同的函数,其中func[]是共性,通过变量n来调用不同的add(),sub()等函数,参数不同,这是个性;
第二部分:几个个性函数的接收参数都是两个,这是共性,但是对于两个参数的操作是不同的,这是个性;
第三部分:字符串code中的“+-*/”等都是可以增减变化的,而且各自的意义不同,这是个性,但是函数中对于code[n]数组元素的遍历及判断选择操作并不会因为code中元素内容的改变而更改,这是共性。

1.对数组和指针一定要有关键的区分点。

计算机为数组分配存储空间,但没有为数组变量分配空间;
计算机为指针是分配了空间的。

由于数组变量没有分配空间,而且它其实在编译的时候就被替换为具体的地址,所以它也无法像一个正经的指针那样指向任何地方。

但是应用方面我觉得最经典的就是,对于字符串常量了:
如果“char* f = ”hello”;”之后像“f[1] = f[0];”这样想要改变值的情况时,就会出现错误;
如果 “char f1[] = ”hello”;”,之后再改变,就安然无恙。
因为这里位于栈区的f想要改变位于常量区字符串常量,而位于栈区f1想要改变的是位于栈区的字符串常量的副本。

map文件的作用是,我们可以根据程序产生错误地址在map文件中找到相应的函数、变量地址。

比如程序有“除0错误”,在 Debug 方式下编译的话,运行时肯定会产生“非法操作”。好,让我们运行它,果然,“非法操作”对话框出现了,这时我们点击“详细信息”按钮,记录下产生崩溃的地址。比如:0x0040104a

再看看它的 MAP 文件:

仔细浏览 Rva+Base 这列,会发现第一个比崩溃地址 0x0040104a 大的函数地址是 0x00401070 ,所以说在 0x00401070 这个地址之前的那个入口就是产生崩溃的函数,也就是这行:

0001:00000020 ?Crash@@YAXXZ 00401020 f CrashDemo.obj

发生崩溃的函数就是 ?Crash@@YAXXZ ,也就是 Crash() 这个子函数。

不仅如此,根据*.map文件还可以找到具体的行号。不过得先生成代码行信息,

2.为什么要使用一个头结点?

这里我觉得就是一个统一的问题,比较一下按位计数法,最常见的10进制计数法,0的作用是什么呢?举个数字“2503”,0在这里的作用是什么?对的,脱口而出,就是“没有”,0在这里就是表示没有,但是,可不可以没有“没有”呢?那不就是“253”了吗?明显不对了。所以这里的0表示“没有”但是不可或缺,也就是起了“占位”的作用。同样的,乐谱中的休止符也是可以由此联想的。头结点就像0一样,它有具体的空间,它占了一个位。而在数学中10的幂次从0…5…100…依次增长,这里为什么 有0呢?因为还有…-100…-5…0
的次幂,如果我们把10的0次只作为1来特殊化处理,就好像人为地建立了一个隔断,所以0还有统一标准的作用。头结点同样如此,统一标准,就像是程序员的一枚小细作,程序员控制头结点,头结点像领头羊一样带领着屁股后面的一串结点。

3.void *memcpy( void *dest, const void *src, size_t count);

Memcpy函数的原型如上,即从指针src指向的空间拷贝count个字节到指针dest指向的空间里。

4.对void* 指针强制类型转换时的体会

就像是用基本类型(系统地默认模板)或自定义数据类型(自定义模板)对一块已经密密麻麻摆满的数据的文本进行了相应的数据类型的格式化排版,这也就解决了相应的打印输出问题。