这段时间专注于对C语言的学习,所以在读书和学习的过程中,对于C语言有了很多认识。 总结如下 1, 对于数组下标的看法:数组下标从0开始而不是1开始。编译器的设计者选择下标从0开始,是因为偏移量的概念在他们心中是根深蒂固的。但是这种设计让一般人感觉很别扭。从编译器的角度来看,在访问数组中的某个元素的时候,我们先找到数组的首地址,也就是数组名所代表的地址,然后根据数组的下标(也就是地址偏移量)来找到我们想访问的数组元素的地址,进而访问它。利用偏移量的概念,下标以0开始就不足为奇了。 2, C语言声明的优先级规则: 1 声明从它的名字开始读取,然后按照优先级顺序依次读取 2 优先级从高到低依次是: 2.1 声明中被括号包起来的那部分 2.2 后缀操作符,如()[ ] 2.3 前缀操作符:星号* 3 如果const volatile 关键字的后面紧跟类型说明符 那么它作用于类型说明符。 在其他情况下,const volatile 关键字作用于它左边紧邻的指针星号 char * const (*p)();
这个声明表示: p是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为char的常量指针。记住:const表示“只读”,并不能因
为它的意思是常量就认为它所表示的就是一个常量。 3, 关于a.out: a.out 这个名字怎么来的?它是“assembler output”的缩写! 你是否质疑过a.out这个名字是怎么确定的?所有输入文件都缺省的使用同一个名字a.out可能会带来不便,对于任何一个文件进行下一次编译的时候,
都有可能覆盖它。大多数人有个模糊的印象,觉得这个名字秉承而来unix传统的简洁性,而且a是字母表的第一个字母,所以会首先想到用它来命名新文件。
其实,取这个名字和这些毫无关系。 当然,这个程序并不是真正的汇编程序输出,而是连接器输入! 段(segments):表示二进制文件中简单的区域。部分(section):section 是ELF文件中的最小组织单位,一个段中包含几个section。 size a.out 会告诉你这个文件的三个段(文本段,数据段,BSS段) BSS(Block Started by Symbol由符号开始的块)段通常是指用来存放程序中未初始化的全局变量和静态变量的一块区域,是可读可写的。在程序执行之
前BSS段会自动清零。所以,为初始化的全局变量在程序执行之前已经是0了。注意和数据段的区别,BSS存放的是未初始化的全局变量和静态变量,数据
段存放的是初始化后的全局变量和静态变量。 数据段:存放初始化后的全局变量的一块内存区域。数据段属于静态内存分配 代码段:通常指用来存放程序执行代码的一块内存区域。这部份区域的大
小在程序运行之前就已经确定,并且在内存中通常属于只读, a.out中的神奇的数字 在linux中,可执行文件是以一种特殊的方式加上标签,这样系统就能确认他们的特殊属性(可执行文件)。标签就是一组“神奇”的数字,系统根据他们
识别可执行文件。这个神奇的数字就是7F454C46
(7F E L F)(od -x a.out od -c a.out readelf a.out)
实验: - 编译“helloworld”程序,在可执行文件中执行ll,得到文件的总体大小。运行size 得到文件的各个段的大小。
- 增加一个全局变量int a[1000]数组声明,重新编译,再用上面的命令得到总体以及各个段的大小
- 现在,在数组的声明中,增加初始值。这将使数组从BSS段转换到数据段,重复测量
- 现在,在函数内声明一个巨大的数组,测量。然后再声明一个巨大数组,加上初始值,测量。
结论: - 数据段保存在目标文件中
- BSS段不保存在目标文件中(只是记录BSS段在运行时所需的大小)
- 文本段是最容易受到优化措施影响的段
- a.out文件大小受到调试状态下编译的影响,但是段不受影响。
让我们看看为什么a.out要以段的形式组织。段可以方便的映射到链接器在运行时可直接载入的对象中!载入器只是取文件中的每个段的映像,并直接将他们放入内存中。从本质上说,段在正在执行的程序中是一块内存区域,每个区域都有特定的目的。 文本段包含程序的指令。链接器把指令直接从文件拷贝到内存中,以后就再也不用管它。程序的文本无论是内容还是大小,一般都不会再改变。有的操作系统会对文本段设置只读属性。 数据段包含经过初始化的全局和静态变量以及它们的值。BSS段的大小从可执行文件中得到(a.out),然后链接器得到这个大小的内存快,紧跟在数据段之后。当这个内存区进入程序的地址空间后全部清零。包括数据段和BSS段的整个区段此时通常统称为数据区。一般情况下,数据段是最大的段。
|