嵌入式C学习第二次任务

一.Typedef

typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。

1.typedef用法

使用typedef定义新类型的方法:在传统的变量声明表达式里用(新的)类型名替换变量名,然后把关键字typedef加在该语句的开头就行了。

下面以两个示例,描述typedef的用法步骤。

示例1:

int a; ———— 传统变量声明表达式
int myint_t; ———— 使用新的类型名myint_t替换变量名a
typedef int myint_t; ———— 在语句开头加上typedef关键字,myint_t就是我们定义的新类型

示例2:

void (*pfunA)(int a); ———— 传统变量(函数)声明表达式
void (*PFUNA)(int a); ———— 使用新的类型名PFUNA替换变量名pfunA
typedef void (*PFUNA)(int a); ———— 在语句开头加上typedef关键字,PFUNA就是我们定义的新类型

2.typedef作用

(1)减少错误:定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:

char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,

// 和一个字符变量;
以下则可行:
typedef char* PCHAR;

PCHAR pa, pb;

这种用法很有用,特别是char* pa, pb的定义,我们往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。

(2)增强可读性

用在旧的C代码中,帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名对象名,如:


struct tagPOINT1
 {
    int x;
    int y; 
};
struct tagPOINT1 p1;
而在C++中,则可以直接写:结构名对象名,即:tagPOINT1 p1;

typedef struct tagPOINT
{
    int x;
    int y;
}POINT;

我们可以看出, 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候。

(3)平台无关性

用typedef来定义与平台无关的类型。

 

typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以获得最高的精度: 

  typedef long double REAL; 

在不支持 long double 的机器上,该 typedef 看起来会是下面这样: 

  typedef double REAL; 

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样:

  typedef float REAL; 

 

也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。

(4)掩饰复合类型

 

typedef 还可以掩饰复合类型,如指针和数组。 

例如,你不用像下面这样重复定义有 81 个字符元素的数组: 

char line[81];

char text[81];

定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样: 

  typedef char Line[81]; 

此时Line类型即代表了具有81个元素的字符数组,使用方法如下: 

Line text, secondline;

 getline(text);

同样,可以像下面这样隐藏指针语法: 

typedef char * pstr;

int mystrcmp(pstr, pstr);

二.结构体

结构体(struct)是由一系列具有相同类型或不同类型的数据项构成的数据集合,这些数据项称为结构体的成员。如上面的student结构体包含三个成员,分别是name、num、age。

结构体是C语言中的一种构造类型。

结构体变量的定义方法

方法一:此处,student是结构体名,该名字是由任意定义的。其相当于一个模板,可以使用这个模板去定义变量stu1,stu2,stu3。定义的时候不要忘了struct。

// 创建结构体模板struct student
struct student
{
char *name;  // 学生名字
int num;     // 学生学号
int age;     // 学生年龄
};

// 使用该结构体模板创建三个变量stu1, stu2, stu3
struct student stu1, stu2, stu3;

方法二:相对于方法一,此处省略了结构体名。虽然更简洁了,但是因为没有了名字,后面就不能用该结构定义新的变量。

// 定义三个结构体变量stu1, stu2, stu3
struct
{
char *name;  // 学生名字
int num;     // 学生学号
int age;     // 学生年龄
}stu1, stu2, stu3;

方法三:此处使用typedef为结构体模板struct student定义一个别名student。使用typedef给结构体创建一个别名,这在实际编程用使用非常广泛。

// 给结构体模板struct student重新命名为student
typedef struct student
{
char *name;  // 学生名字
int num;     // 学生学号
int age;     // 学生年龄
}student;

// 使用student创建三个结构体变量stu1, stu2, stu3
student stu1, stu2, stu3;

结构体的申明

//申明一个结构体 
struct book 
{
    char title[MAXTITL];//一个字符串表示的titile 题目 ; 
    char author[MAXAUTL];//一个字符串表示的author作者 ; 
    float value;//一个浮点型表示的value价格; 
};//注意分号不能少,这也相当于一条语句;

这个声明描述了一个由两个字符数组和一个float变量组成的结构体,但是注意,他并没有创建一个实际的数据对象,而是描述了一个组成这类对象的元素,【因此,我们也有时候将结构体声明叫做模板,因为它勾勒出数据该如何存储,并没有实例化数据对象】。
下面介绍一下上面的结构体声明;
1,首先使用关键字struct,他表示接下来是一个结构体。
2,后面是一个可选的标志(book),它是用来引用该结构体的快速标记。因此我们以后就会可以这样创建数据对象

3,接下来就是一个花括号,括起了结构体成员列表,及就是每个成员变量,使用的都是其自己的声明方式来描述,用分号来结束描述;
列如;char title[MAXTITL];字符数组就是这样声明的,用分号结束;
注意;其中每个成员可以使用任何一种c数据结构甚至是其他的结构体,也是可以的;

4,在结束花括号后的分号表示结构体设计定义 的结束。

三.宏定义

每个#define行(即逻辑行)由三部分组成:第一部分是指令 #define 自身,“#”表示这是一条预处理命令,“define”为宏命令。第二部分为宏(macro),一般为缩略语,其名称(宏名)一般大写,而且不能有空格,遵循C变量命令规则。“替换文本”可以是任意常数、表达式、字符串等。在预处理工作过程中,代码中所有出现的“宏名”,都会被“替换文本”替换。这个替换的过程被称为“宏代换”或“宏展开”(macro expansion)。

宏定义在 C 语言源程序中允许用一个标识符来表示一个==字符串==,称为“==宏/宏体==” ,被定义为“宏”的==标识符==称为“==宏名==”。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为“==宏替换==”或“==宏展开==”。 宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。在 C 语言中,宏分为 有参数和无参数两种。

具体为:

// 不带参数的宏定义
#define MAX 10

/*带参宏定义*/
#define M(y) y*y+3*y

/*宏调用*/
k=M(5);

优点:

方便程序的修改
使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更方便一些。
相对于==全局变量==两者的区别如下:
1. 宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以。
2. 宏定义的只是一段字符,在编译的时候被替换到引用的位置。在运行中是没有宏定义的概念的。而变量在运行时要为其分配内存。
3. 宏定义不可以被赋值,即其值一旦定义不可修改,而变量在运行过程中可以被修改。
4. 宏定义只有在定义所在文件,或引用所在文件的其它文件中使用。 而全局变量可以在工程所有文件中使用,只要再使用前加一个声明就可以了。换句话说,宏定义不需要extern。

提高程序的运行效率

使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生==函数调用 #800023==时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问 题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。

有参宏:

这里写图片描述

宏调用: 
宏名(实参表); 
printf(“MEAN = %d\n”, MEAN(7, 9)); // 输出结果: MEAN = 8

和函数类似,在宏定义中的参数成为形式参数,在宏调用中的参数成为实际参数。 
而且和无参宏不同的一点是,有参宏在调用中,不仅要进行宏展开,而且还要用实参去替换形参。如:

四.条件编译

条件编译是根据实际定义宏(某类条件)进行代码静态编译的手段。可根据表达式的值或某个特定宏是否被定义来确定编译条件。

最常见的条件编译是防止重复包含头文件的宏,形式跟下面代码类似:

条件编译中使用的预编译指令

#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配合使用,判断某个宏是否被定义

预编译指令应用举例

1. #define、#undef

#define命令定义一个宏:
#define MACRO_NAME[(args)] [tokens[(opt)]]
之后出现的MACRO_NAME将被替代为所定义的标记(tokens)。宏可带参数,而后面的标记也是可选的。

宏定义,按照是否带参数通常分为对象宏、函数宏两种。
对象宏: 不带参数的宏被称为"对象宏(objectlike macro)"。对象宏多用于定义常量、通用标识。例如:

2. defined

defined用来测试某个宏是否被定义。defined(name): 若宏被定义,则返回1,否则返回0。
它与#if、#elif、#else结合使用来判断宏是否被定义,乍一看好像它显得多余, 因为已经有了#ifdef和#ifndef。defined可用于在一条判断语句中声明多个判别条件;#ifdef和#ifndef则仅支持判断一个宏是否定义。

#if defined(VAX) && defined(UNIX) && !defined(DEBUG) 

和#if、#elif、#else不同,#ifdef、#ifndef、defined测试的宏可以是对象宏,也可以是函数宏。

3. #ifdef、#ifndef、#else、#endif

条件编译中相对常用的预编译指令。模式如下:

#ifdef用于判断某个宏是否定义,和#ifndef功能正好相反,二者仅支持判断单个宏是否已经定义,上面例子中二者可以互换。如果不需要多条件预编译的话,上面例子中的#elif和#else均可以不写。

4. #if、#elif、#else、#endif

#if可支持同时判断多个宏的存在,与常量表达式配合使用。常用格式如下:

常量表达式可以是包含宏、算术运算、逻辑运算等等的合法C常量表达式,如果常量表达式为一个未定义的宏, 那么它的值被视为0。

在判断某个宏是否被定义时,应当避免使用#if,因为该宏的值可能就是被定义为0。而应当使用#ifdef或#ifndef。
注意: #if、#elif之后的宏只能是对象宏。如果宏未定义,或者该宏是函数宏,则编译器可能会有对应宏未定义的警告。

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值