嵌入式C学习笔记二
Typedef:
1)typedef 的一个重要用途是定义机器无关的类型。例如,定义一个叫“REAL”的浮点类型,该浮点类型在目标机器上可以获得最高的精度:
typedef long double REAL;
如果在不支持 long double 的机器上运行相关代码,只需要修改对应的 typedef 语句,例如: typedef double REAL;
或者: typedef float REAL;
2)使用 typedef 为现有类型创建别名,给变量定义一个易于记忆且意义明确的新名字。例如:
typedef unsigned int UINT
3)使用 typedef 简化一些比较复杂的类型声明,例如:
typedef void (PFunCallBack)(char pMsg, unsigned int nMsgLen);
上述声明引入了 PFunCallBack 类型作为函数指针的同义字,该函数有两个类型分别为 char* 和 unsigned int 参数,以及一个类型为 void 的返回值。通常,当某个函数的参数是一个回调函数时,可能会用到 typedef 简化声明。
结构体:
结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。
1、数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragmapack指定的数值和这个数据成员自身长度中,比较小的那个进行。
2、结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragmapack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
宏定义:
宏定义作用域:
#define指令出现在程序中的函数的外面,宏名的有效范围为该指令行起到本源文件结束。通常,#define指令写在文件开头,函数之前,作为文件一部分,在整个文件范围内有效。
可以用==#undef指令==终止宏定义的作用域。这样可以灵活控制宏定义的作用范围。
在进行宏定义时,可以引用已定义的宏名,即可以层层置换。
对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。
宏定义与定义变量的含义不同,不分配存储空间。不带参数的宏定义只作简单的字符替换,千万不要把宏名当作变量名使用。
条件编译:
条件编译是根据实际定义宏(某类条件)进行代码静态编译的手段。可根据表达式的值或某个特定宏是否被定义来确定编译条件。
1.条件编译中使用的预编译指令
#define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用,判断某个宏是否被定义
内存操作:
代码区:存放函数体的二进制代码。
全局变量与静态变量区:通常定义变量,编译器在编译时都可以根据该变量的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。程序结束后由系统释放。
文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放
局部变量区即栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
动态存储区即堆区:一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
指针:
指针就是地址。
结构体指针:
1.所谓结构体指针,就是指向结构体变量的指针。
2.一个结构体变量的起始地址就是这个结构体变量的指针。
结构体的参数传递:
1.结构体变量作为函数参数[实参与形参]时,形参结构体变量成员值的改变不影响对应的实参构体变量成员值的改变。
2.结构体数组或结构体指针变量作为函数参数[实参与形参]时,形参结构体数组元素(或形参结构体指针变量指向的变量)成员值的改变将影响对应的实参构体数组[或实参结构体指针变量指向的变量]成员值的改变。
3.结构体变量可作为函数的参数,函数可返回一结构体类数据
文件的包含:
文件包含处理是指在一个源文件中,通过文件包含命令将另一个源文件的内容全部包含在此文件中。在源文件编译时,连同被包含进来的文件一同编译,生成目标目标文件。
(1) 处理时间:文件包含也是以"#"开头来写的(#include ), 那么它就是写给预处理器来看了, 也就是说文件包含是会在编译预处理阶段进行处理的。
(2) 处理方法:在预处理阶段,系统自动对#include命令进行处理,具体做法是:降包含文件的内容复制到包含语句(#include )处,得到新的文件,然后再对这个新的文件进行编译。
大小端和字节序:
大端和小端
在计算机中是以字节为单位,每一个地址对应一个字节,一个字节8bit。在C中,除了8bit的char以外,还有16bit的short,32位的int,64位long,具体要由编译器决定,可以通过sizeof来获取不同类型在内存中占用的字节数。在计算机系统中,当物理单位的长度大于1个字节时,就要区分字节顺序。常见的字节顺序有两种:Big Endian(High-byte first) 和 Litter Endian(Low-byte first),还有其他字节顺序,但不常见,例如Middle Endian。
小端Little Endian:低字节存放在低地址,低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
大端Big Endian:高字节存放在低地址,即高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
以二节中的例子int类型整数123456789位例:
小端在内存中排列: 0x15 0xCD 0x5B 0x07(低位在前)
大端在内存中排列: 0x07 0x5B 0xCD 0x15(高位在前)
网络字节序(Network Order):TCP/IP各层协议将字节序定义为Big Endian,因此TCP/IP协议中的字节序同称之为网络字节序。
主机字节序(Host Order):整数在内存中保存的顺序,它遵循Little Endian规则(不一定,要看主机的CPU架构)。所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序列(Little Endian)和网络序(Big Endian)的转换。
如果是做跨平台开发时,双方需要协商好字节序,然后根据程序运行的环境,确定是否需要字节序转换。
例如约定的通讯字节序位是Big Endian,默认的window采用的Little Endian,那收到数据后就需要做转换操作。
位域:
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct 位域结构名
{ 位域列表 };
其中位域列表的形式为:类型说明符 位域名:位域长度
函数指针:
函数指针基础:
-
获取函数的地址
-
声明一个函数指针
-
使用函数指针来调用函数
获取函数指针:
函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名。
声明函数指针
声明指针时,必须指定指针指向的数据类型,同样,声明指向函数的指针时,必须指定指针指向的函数类型,这意味着声明应当指定函数的返回类型以及函数的参数列表。