本章我们主要来学习之前所学的复杂数据类型中的同样也很重要的结构体和枚举的知识,以及C语言中的预处理,typedef的使用,简单介绍一下static和extern等一些比较杂的知识。在开始本章的新知识学习之前首先我们来学习一下根据变量的作用域的不同C语言一般把变量分为两种,局部变量和全局变量。下面我们来分别比较和体会一下这两种不同的变量类型:
根据变量的作用域,可以分为:
1. 局部变量:
a. 定义:在函数(代码块)内部定义的变量(包括函数的形参)
b. 作用域:从定义变量的那一行开始,一直到代码块结束
c. 生命周期:从定义变量的那一行开始分配存储空间,代码块结束后,就会被回收
d.局部变量没有固定的初始值
2. 全局变量
a. 定义:在函数外面定义的变量
b. 作用域:从定义变量的那一行开始,一直到文件结尾(能被后面的所有函数共享)
c. 生命周期:程序一启动就会分配存储空间,程序退出时才会被销毁
d. 全局变量默认的初始值就是0
第一讲 结构体
在上一章我们学习过数组,数组是用来存储数据的,但是数组只能存放一种类型的数据,在实际的应用中往往需要存储一组不同数据类型的数据,这让数组在使用中有一定的局限性,结构体可以解决这类的问题。在定义结构体时,结构体可以由多种不同类型的数据组成。
1. 结构体的定义
定义结构体变量的三种方式:
(1)先定义类型,在定义变量(分开定义)
strcut Student
{
int age;
};
struct Student stu;
(2) 定义类型的同时定义变量
strcut Student
{
int age;
}stu;
struct Student stu2;
(3)定义类型的同时定义变量(省略了类型名称)
strcut
{
int age;
}stu;
下面通过代码来引出结构体,同时要注意结构体定义时的一些规范问题:
#include<stdio.h>
int main()
{
// 1.定义结构体的类型
struct Person
{ // 里面的三个变量可以称为是结构体的成员或者属性
int age; // 年龄
double height; // 身高
char *name; // 姓名
};
// 2.根据结构体的类型,定义结构体变量,并在定义时可以进行赋值
struct Person p = {20 , 1.55, "jack"};
p.age = 30;
p.name = "rose"; // 在调用结构体体属性时用"变量名.属性"调用
/*下面为错误写法,如果要定义直接写struct Person p2 = {30, 1.23, "rose"};
struct Person p2;
p2 = {30, 1.23, "rose"}; 这么定义是不对的
*/
// 在赋值时可以不按定义属性的顺序赋值,调用属性赋值
struct Person p2 ={.height=1.78,.age = 20, .name="tom"};
printf("age=%d,height=%f,name=%s\n", p.age,p.height,p.name);
return 0;
}
2. 结构体的内存分析
(1)结构体的作用域
定义在函数(代码块)外面:从这行开始,一直到文件结尾,都是有效(跟全局变量一样)
在外面定义了结构体类型后,函数内部也可以再次重新定义相同的结构体类型,只是作用域只作用域函数的代码块
定义在函数(代码块)内部:结构体类型也是有作用域的,从定义类型的那一行开始,一直到代码块结束
如果想定义整个程序都可以使用的结构体类型,需要在整个程序外定义,不可在函数内部定义
(2)结构体内存分配
结构体在定义类型时不会分配存储空间,在定义结构体变量时才会真正分配存储空间
struct Date
{
int year;
int month;
int day;
}; // 这是定义结构体类型,并不会分配存储空间
struct Date d1={2011,9,10};
struct Date d2={2015,12,3}; // 定义结构体变量(定义了变量,真正分配存储空间)
d2 = d1; // 会将d1所有成员的值对应的赋值给d2的所有成员
(3)结构体所占存储空间
结构体所占存储空间是其内部定义的数据类型所占之和
补齐算法(对齐算法):分配结构体内存时,结构体所占用的存储空间,必须是最大成员字节数的倍数
3. 结构体数组
结构体数组是有顺序,多类型的
int main()
{
struct RankRecord // 定义结构体
{
int no; // 序号
char *name; // 名称
int score; // 积分
};
/* struct RankRecord r1={1,"jack",5000};
struct RankRecord r2={2,"jim",500};
struct RankRecord r3={3,"rose",300};*/
//结构体数组:有顺序,多类型
// 定义结构体数组
struct RankRecord records[3]=
{
{1,"jack",5000},
{2,"jim",500},
{3,"rose",300}
};
// 改结构体的值
records[0].no=4;
//records[0] = {2,"tom",200};这么写是不对的,只能在定义结构体类型的时候可以这么写
return 0;
}
4. 指向结构体的指针
(1)指向结构体的指针的定义
struct Student *p; 这么定义了之后表示指针变量p将来指向struct Student类型的数据
(2)利用指针访问结构体的成员
(*p).成员名称
p->成员名称
结构体变量名.成员名称
struct Student // 定义一个结构体
{
int no; // 学号
int age; // 年龄
};
//结构体变量
struct Student stu = {1,20};
//指针变量p将来指向struct Student类型的数据
struct Student *p;
//指针变量p指向了stu变量
p=&stu;
p->age=30; // 利用指针访问结构体成员并改值
5. 结构体和函数
结构体作为函数参数,只是将实参结构体所有成员的值一一赋值给了形参结构体所有成员,修改函数内部结构体成员不会影响外面实参结构体成员。而如果指向结构体的指针作为函数参数,则会影响外面实参结构体的成员。
第二讲 枚举、预处理
1. 枚举
枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数,枚举的一般使用场合是:当一个变量只允许有固定几个取值时用枚举
(1)定义枚举类型
enum Season
{
spring, // 0
summer, // 1
autumn, // 2
winter // 3
};
(2)定义枚举变量
enum Season s = summer;
枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。
2. 预处理(所有的预处理指令都是#开头)
C语言在对源程序进行编译之前,会先对一些特殊的预处理指令作解释(比如之前使用的#include文件包含指令),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译。
(1)宏定义
#define宏名 字符串 宏定义开始
#undef 宏名 字符串 宏定义失效
简单地带参数的宏定义比函数效率高
定义宏定义时,参数和结果值都用加上小括号
(2)条件编译
#ifdef
#elif
#endif//只要写了#if,在后面必须加上#endif
(3)文件包含
#include
<>表示系统自带的文件,""表示自定义的文件
不允许循环包含,比如a.h包含b.h,b.h又包含a.h
(4)预处理几项注意总结
所有的预处理指令都是以#开头
预处理指令一共三种:宏定义,条件编译,文件包含
预处理指令在代码翻译成0跟1之前执行
预处理的位置是随便写的
预处理指令的作用域:从编写指令的那一行开始,一直到文件的结尾
宏名一般用大写或者以k开头,变量名一般用小写
3. typedef关键字
使用typedef为已经存在的各种数据类型定义一个新的类型名。
typedef 类型名 新的类型名
使用场合:基本数据类型、指针、结构体、枚举、指向函数的指针、指向结构体的指针
下面通过代码来体会一下typedef的使用:
struct Student // 定义一个结构体
{
int age;
};
typedef struct Student MyStu; // 这是第一种做法
typedef struct Student
{
int age;
} Mystu; // 这是第二种做法
typedef struct
{
int age;
} Mystu; // 这是第三种做法
typedef的使用可以让一些复杂的类型定义为自己设定的简易的新类型,特别在枚举类型,指向函数的指针,指向结构体的指针等较为复杂的类型
// 指向结构体的指针
struct Person
{
int age;
};
typedef struct Person * PersonPoint; // 利用typedef定义新的类型
int main()
{
struct Person p = {20};
struct Person *p2 = &p;
PersonPoint p2 = &p; // 这一句跟上一句是相同的效果
return 0;
}
4. static和extern
static:声明、定义内部函数(允许不同文件中有同名的内部函数);定义一个内部变量
extern:完整地声明、定义外部函数(不允许有同名的外部函数),默认情况下,声明和定义的函数都是外部函数,可省略extern;声明一个外部变量
本章学习总结:
本章学习的知识可能有点零散,但在C语言的学习中也都是需要注意和掌握的。其中本章的第一讲主要讲述了结构体的相关知识,结构体在C语言的学习包括今后的OC学习中都会经常用到,因此也是非常重要的一部分,第一讲主要学习了结构体的定义,其中定义结构体变量有三种不同的方式,还学习了结构体的内存分析,包括结构体的作用域,内存分配和所占存储空间,结构体跟之前所学复杂数据类型数组,指针等有密切的联系和应用,结构体也重点讲述了结构体和数组,指向结构体的指针,同时了解了结构体和函数的关系,当结构体作为函数参数的一些注意点。第二讲主要学习了我们枚举数据类型,C语言的预处理和C语言中比较好用的typedef关键字,同时了解了一下static和extern。枚举enum和typedef在使用上有一定类似的地方,可以比较着学习,一个是基本数据类型,用来定义只允许有几个固定取值的变量,另一个是将一些已经存在的数据类型定义新的类型名。而C语言的预处理是在C语言代码翻译成0跟1之前执行的,主要包括宏定义,条件翻译和文件包含,注意预处理都是以#开头的,需要了解预处理的执行过程以及其作用域。