目录
1.结构体
结构体变量:
成员的获取和赋值
2.c结构体数组
3.c语言结构体类型的指针
通过结构体指针获取结构体成员
结构体数组 = 指针
结构体指针作为函数参数
4.c枚举类型 enum
5.C语言共用体(Union)
6.C语言位域
7.c语言位运算
8.C语言typedef:给类型起一个别名
按照类似的写法,还可以为函数指针类型定义别名,其实我不太清楚?:
typedef 和 #define 的区别
9.C语言const:禁止修改变量的值
const 和指针
在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。结构体的定义形式为:
struct 结构体名{
结构体所包含的变量或数组
};
注意大括号后面的分号
;
不能少,这是一条完整的语句。
像 int、float、char 等是由C语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为复杂数据类型或构造数据类型。
既然结构体是一种数据类型,那么就可以用它来定义变量。例如:
struct stu stu1, stu2;
stu 就像一个“模板”,定义出来的变量都具有相同的性质。也可以将结构体比作“图纸”,将结构体变量比作“零件”,根据同一张图纸生产出来的零件的特性都是一样的。
//你也可以在定义结构体的同时定义结构体变量:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
//无结构体名称,这样此后无法再定义结构体变量
struct{ //没有写 stu
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
结构体和数组类似,也是一组数据的集合,整体使用没有太大的意义。数组使用下标[ ]
获取单个元素,结构体使用点号.
获取单个成员。获取结构体成员的一般格式为:
结构体变量名.成员名;
除了可以对成员进行逐一赋值,也可以在定义时整体赋值,不过整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。整体赋值是默认值。
struct stu stus[10];会初始化stus数组,把模板stu放在stus为首的空间;而int arr[10];不会初始化,是因为,arr里直接是int字节数;
数组如果初始化,内部的值就是默认值。int arr[10] = {};相当于初始化
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.5 };
需要注意的是,结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。
定义结构体数组和定义结构体变量的方式类似:
#include
2 int main(){
3 struct teacher{
4 char *name;
5 int age;
6 }class[]={
7 {"yz",20},
8 {"zzq",12}
9 };
10
11 printf("%d\n",class[0].age);
12
13 struct teacher school[2];
14 struct teacher ly,yyk;
15 ly.age = 100;
16 yyk.age = 200;
17 ly.name = "ly";
18 yyk.name = "yyk";
19 school[0] = ly;
20 school[1] = yyk;
21 printf("%d\n",school[0].age);
22
23 }
~
指针也可以指向一个结构体,定义的形式一般为:
struct 结构体名 *变量名;
可以先定义结构体,再定义结构体的指针;也可以定义结构体的同时定义结构体指针;eg:
1 #include
2 int main(){
3 struct stu{
4 char *name;
5 int age;
6 }stu2,stu3 = {"yz",12},*pstu = &stu3;
7
8 struct stu *stu1;
9 struct stu s;
10 s.name = "yz";
11 s.age = 100;
12
13 stu1 = &s;
14
15 printf("%d\n",(*stu1).age);
16 printf("%s\n",stu1->name);
17 printf("pstu3.name=%s\n",pstu->name);
18 }
注意,结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&.
还应该注意,结构体和结构体变量是两个不同的概念:结构体是一种数据类型,是一种创建变量的模板,编译器不会为它分配内存空间,就像 int、float、char 这些关键字本身不占用内存一样;结构体变量才包含实实在在的数据,才需要内存来存储。
(*ptr).name 或者 ptr->name
结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针。如果结构体成员较多,尤其是成员为数组时,传送的时间和空间开销会很大,影响程序的运行效率。所以最好的办法就是使用结构体指针,这时由实参传向形参的只是一个地址,非常快速。
些数据的取值往往是有限的,只能是非常少量的整数.
定义:enum 类型名{值名字1=1,值名字2};enum 类型名{值名字1,值名字2}a,b;enum 类型名 a,b;
通过前面的讲解,我们知道结构体(Struct)是一种构造类型或复杂类型,它可以包含多个类型不同的成员。在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体(Union),它的定义格式为:
union 共用体名{
成员列表
};
共用体有时也被称为联合或者联合体,这也是 Union 这个单词的本意。
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
共用体也是一种自定义类型,可以通过它来创建变量
在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),这就是位域。请看下面的例子:
C语言标准还规定,只有有限的几种数据类型可以用于位域。在 ANSI C 中,这几种数据类型是 int、signed int 和 unsigned int(int 默认就是 signed int);到了 C99,_Bool 也被支持了。
struct bs{
unsigned m;
unsigned n: 4;
unsigned char ch: 6;
}
C语言标准规定,位域的宽度不能超过它所依附的数据类型的长度。通俗地讲,成员变量都是有类型的,这个类型限制了成员变量的最大长度,:
后面的数字不能超过这个长度。
C语言提供了六种位运算符:
运算符 | & | | | ^ | ~ | << | >> |
---|---|---|---|---|---|---|
说明 | 按位与 | 按位或 | 按位异或 | 取反 | 左移 | 右移 |
C语言中不能直接使用二进制,&
两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都是以二进制形式存储,&
就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理。
C语言允许为一个数据类型起一个新的别名,就像给人起“绰号”一样。
起别名的目的不是为了提高程序运行效率,而是为了编码方便。例如有一个结构体的名字是 stu,要想定义一个结构体变量就得这样写:
struct stu stu1;
struct 看起来就是多余的,但不写又会报错。如果为 struct stu 起了一个别名 STU,书写起来就简单了:
STU stu1;
这种写法更加简练,意义也非常明确,不管是在标准头文件中还是以后的编程实践中,都会大量使用这种别名。
使用关键字 typedef 可以为类型起一个新的别名,语法格式为:
typedef oldName newName;
数组也是有类型的。例如char a1[20];
定义了一个数组 a1,它的类型就是 char [20],
由上句可见,我认为:类型就是对这种类型数据占据大小的约束;
需要强调的是,typedef 是赋予现有类型一个新的名字,而不是创建新的类型。为了“见名知意”,请尽量使用含义明确的标识符,并且尽量大写。
再如,为指针类型定义别名:
typedef int (*PTR_TO_ARR)[4];//int *ptr;
表示 PTR_TO_ARR 是类型
int * [4]
的别名,它是一个二维数组指针类型。
typedef int (*PTR_TO_FUNC)(int, int);
PTR_TO_FUNC pfunc;
typedef 在表现上有时候类似于 #define,但它和宏替换之间存在一个关键性的区别。正确思考这个问题的方法就是把 typedef 看成一种彻底的“封装”类型,声明之后不能再往里面增加别的东西。
有时候我们希望定义这样一种变量,它的值不能被改变,在整个作用域中都保持固定。例如,用一个变量来表示班级的最大人数,或者表示缓冲区的大小。为了满足这一要求,可以使用const
关键字对变量加以限定:
const int MaxNum = 100; //班级的最大人数
创建常量的格式通常为:
const type name = value;
const 也可以和指针变量一起使用,这样可以限制指针变量本身,也可以限制指针指向的数据。const 和指针一起使用会有几种不同的顺序,如下所示:
在最后一种情况下,指针是只读的,也就是 p3 本身的值不能被修改;在前面两种情况下,指针所指向的数据是只读的,也就是 p1、p2 本身的值可以修改(指向不同的数据),但它们指向的数据不能被修改。