嵌入式C语言学习进阶系列文章
GUN C编译器拓展语法学习笔记(一)GNU C特殊语法部分详解
GUN C编译器拓展语法学习笔记(二)属性声明
GUN C编译器拓展语法学习笔记(三)内联函数、内建函数与可变参数宏
数组存储与指针学习笔记(一)数据类型与存储、数据对齐、数据移植、typedef
文章目录
- 嵌入式C语言学习进阶系列文章
- 一、枚举类型
- 1.2 枚举的三种方法与本质
- 1.3 枚举的三种方法与本质![在这里插入图片描述](https://img-blog.csdnimg.cn/c43c9101723a4b91a048690afb507df8.png#pic_center)
- 二、常量和变量
- 1.1 变量本质
- 1.2 常量存储
- 1.2 常量折叠
一、枚举类型
枚举(enum)是C语言的一种特殊类型。当我们在程序中想定义一组固定长度或范围的数值时,可以考虑使用枚举类型。使用枚举可以让程序可读性更强,看起来更加直观。
使用enum定义的枚举常量值列表中,默认从0开始,然后依次递增:SUN=0,MON=1,TUE=2,……当然我们也可以显式指定枚举值。
1.2 枚举的三种方法与本质
使用枚举类型定义变量,使用方法与结构体、共用体类似,经常使用的三种方法如下。
枚举是一种类型,属于整型类型。使用enum定义的枚举值列表,其实就是从0开始的一组整数序列。我们使用枚举类型定义的变量,同样可以作为函数参数和函数返回值,可以用来定义数组,甚至和结构体混用等。枚举有点类似typedef,为一个数值添加一个别名,让程序更加直观,可读性更高。
枚举变量和整型变量相互赋值,都是可以正常编译和运行的。我们在代码中使用的枚举类型的变量,在最终编译生成的可执行文件中都会被整型数值代替。
枚举与预处理指令#define的作用差不多,都是为了增加代码的可读性。宏在预处理阶段,通过简单的字符串替换就全部被替换掉了,编译器根本不知道有宏,而枚举类型则在编译阶段全部被替换为整型。枚举的优势是:枚举可以自动赋值,而宏则需要一个一个单独定义。
1.3 枚举的三种方法与本质
Linux内核中使用enum定义的枚举类型大部分是没有枚举名的,而且通常会在一串枚举值之后带上一个前缀为NR_的元素来表示枚举值的数量。当我们不需要使用枚举类型去定义一个枚举变量时,枚举并不需要一个名字,这些无名的枚举类型其实就相当于宏定义。而最后一个元素NR或MAX,一般用来记载枚举列表中元素的个数,或者作为循环判断的边界值。
二、常量和变量
本章节研究的内容是常量和变量在内存中是如何存储的?如何访问它们?
1.1 变量本质
变量名的本质,其实就是一段内存空间的别名。编译器在编译程序时会将变量名看成一个符号,符号值即变量的地址,各种不同的符号保存在符号表中。我们可以通过变量名对和它绑定的内存单元进行读写,而不是直接使用内存地址。通过变量名访问内存,既方便了程序的编写,也大大增强了程序的可读性。
赋值修改:在C语言中,一块可以存储数据的内存区域,一般被称为对象,而操作这片内存的表达式,即引用对象的表达式,我们称之为左值。常见的左值有变量、e[n]、e.name、p->name、*e等这些常见的表达式。但是如数组名、函数、枚举常量、函数调用等都不能作为左值,也不能通过它们去修改对象。
一个变量作为左值时,通常表示对象的地址,我们对变量名的引用其实就是对该地址区域进行各种操作。一个变量名作为右值时,通常表示对象的内容。
不同类型的变量有不同的存储方式、作用域和生命周期。在定义一个变量时,我们可以使用char、int、float、double等关键字来指定变量的类型,再加上short和long这两个整型限定符,基本上就确定了这个变量在内存中的存储空间的大小。有时候我们还可以使用一些变量修饰限定符来改变变量的存储方式,常用的修饰符有auto、register、static、extern、const、volatile、restrict、typedef等。这些修饰限定符往往会决定变量的存储位置、作用域或生命周期,所以一般也被称为存储类关键字。一个变量如果使用register修饰,意在告诉编译器这个变量将会被频繁地使用,如果有可能,可以将这个变量存储在CPU的寄存器中,以提高其读写效率。
我们在程序中,定义变量的目的,就是方便对存储在内存中的数据进行读写,不是直接通过地址,而是通过变量名来访问内存。编译器根据我们定义的变量类型,会在内存中分配合适大小的存储空间和地址对齐。变量名的本质,其实就是一段内存存储空间的别名,通过变量名可以直接对这段内存进行读写。
1.2 常量存储
常量和常量表达式,编译器在编译程序时会把它们单独放在一个叫作.rodata
的只读数据段中,我们可以使用objdump命令查看它们。
在C语言中,我们常常使用const关键字来修饰一个变量,表示该变量是只读的,不能被修改。如果我们使用const关键字修饰一个数组,则表示该数组中所有元素的值都不能被修改。变量i和j使用const修饰后,存储到了.rodata只读数据段中。
1.2 常量折叠
当一个C语言程序中存在常量表达式时,编译器在编译时会把常量表达式优化成一个固定的常量值,以节省存储空间。我们把这种编译优化称为常量折叠。
程序的编译过程中,编译器会直接把表达式23+54优化成一个常量26,作为全局变量val的初值存储在数据段中。在内存地址0x21024处,我们可以看到定义的全局变量val的值为0x1a。常量表达式23+54经过编译优化后,表达值的值就变成了26,直接存储在地址为0x21024的内存单元中。