c 复合数据类型 结构 枚举等

结构

​ 由于多种内奸数据类型组成的一个整体,用于描述一个事物的各项数据。

设计结构体:

 struct TypeName
 {
      类型 成员名;
      ...
 };
 注意:此时只是完成了数据类型的设计
 相同的结构变量是可以赋值的  stu1 = stu;

定义结构变量:

​ 一般结构体设计都是放在头文件或者函数外,方便其他文件和函数调用。

struct TyppeName  结构变量名;

初始化成员:

方法一:
struct 	TypeName  结构变量名 = {数据1,数据2 ...}
注意:一定要按照成员的的顺序初始化
方法二:
struct TypeName 结构变量名 = {
   .成员名 = 数据1,
   .成员名 = 数据2,
   ...
}
注意:可以不按照顺序初始化,但是一定要加成员名

访问成员:

结构变量,成员名;

结构指针-> 成员名;

使用堆内存存储结构变量:
 struct Student* stup =malloc(sizeof(struct TypeName));
 赋值可以的 *stup = stu.con
 结构体指针输入 
     stup -> name(数组不用取地址),&stup->id..
 结构体指针输出
     类似于 stup->name,stup->id..

由于此时编译时,堆内存还没有分配(堆内存里有解释),所以编译器还无法初始化,只能批量赋值,或单个赋值。

给结构类型重定义:

​ 由于在c语言中struct关键词无法省略,导致使用时麻烦,可以使用typedef关键词结构重定义类型、

typedef struct typeName
{
    ..
}TypeName;

结构体的计算

系统为了快速的访问结构的成员,会对结构的成员在内存排列时进行对齐和补齐,因此结构成员顺序会影响结构的总字节数,一般结构的总字节数会 >= 所有成员的字节数之和。

对齐:

假定第一个成员使用0地址,所有成员所使用的内存地址,必须被他的字节数整除。

补齐

​ 结构的总字节数,必须是他最大成员的整数倍,如果不是则补充空字节。

注意:在linux系统中计算对齐、补齐时,成员的字节超过4时,则按照4自己计算,Windows正常。

struct Data
{
   char ch1:4;
   char ch1:4;
}

联合:

也是由程序设计的一种数据类型,使用语法与结构一样,只是成员的排列方式不同,所有成员共用一块内存,一个成员的值发生变化,其他成员的值也会跟着变化。

特点:一块内存对应多个标识符,达到节约内存的目的,现在已经基本不用。

union TypeName
{
    类型 成员名;
    ...
};

​ 考点:

  1. 他的成员天然是对齐的,但是有内存补齐

  2. 使用联合可以判断大小端系统

系统的大小端:

小端系统:低位数据存储在低位地址。

大段系统:地位数据存储在高位地址。

​ 一般个人计算机使用的是小端系统,大端服务器、网络设备使用的是大端,大端字节序也叫网络字节序。

枚举:

​ 枚举是一种特殊的整型数据,他是把一个整型数据可能出现的值全部罗列出来并取一个有意义的名字(枚举值),除此之外不应该 再使用其他的值,以此保证数据的安全性,提高程序的可读性。

enum Direction {Up,                Down,                Left,                Right};

​ 设计好的枚举类型,可以下定义枚举变量,而枚举变量只能赋值为枚举值,以此保证数据的安全性。

注意:编译器为了速度,并不会检查检查枚举变量的赋值

​ 枚举值是常量,可以使用在case语句的后面,不用再写字面值常量,从而提高程序的可读性

​ 枚举值在不指定的情况下,第一个默认为0,之后的逐渐加1,可以单个指定。可以定义匿名枚举,只使用枚举值。

预处理指令

​ 程序员所编写的c代码并不能直接被编译,而是需要一段程序预先翻译成标准的c代码,负责处理的程序就叫预处理器,翻译的过程就叫做预处理,被翻译的代码就叫做预处理指令,所有预处理指令都是以#开头

include 把头文件插入当前文件中

​ #include<> 从系统指定的路径下查找头文件,并插入当前文件中。

​ #include"" 先从当前路径下查找头文件,如果没有再从系统指定路径下查找头文件,然后再插入当前文件中。

define 宏定义指令

​ 定义号宏常量

​ #define 宏名 (字面值)

​ 注意:末尾不要加分号

​ 作用:用标识符来替代字面值数据,从而提高代码的可读性,可扩展性。

​ 代码在预处理时所使用的宏名会被替换成字面值。

​ #define 宏名(a,b,c) (a+b+c)

​ 不是真正的函数,而是在使用了带参数的宏,预处理时使用了带参宏的位置替换成宏名后面的语句,所提供的的参数也会被替换到语句中的位置。

优点:

  1. 不是真正的函数调用,没有参数传递过程,也没有返回值
  2. 不限制参数类型,任何类型都可以使用,代码的通用性强

缺点:

  1. 没有类型检查,安全性低
  2. 过多使用会造成代码冗SW余,导致代码段增大,浪费内存。
  3. 可能会有二义性(多加点括号)

定义宏函数要注意的问题:

  1. 给每个参数加一个小括号,避免产生二义性
  2. 使用小括号、大括号包含整个宏函数代码,进行保护
  3. 宏函数不能换行可以使用续行符 \ 来分行一下,但是还是一行
  4. 宏函数参数不要使用自变运算符。

练习

​ 定义一个交换两个变量的宏函数,要任何类型的变量都可以使用

the first way

#define swap(a,b,type) {            \	            type t=a;			\	            a=b;				\	            b=t;				\            }

the second way

#define swap(a,b) {typeof(a) t=a; a=b; b=t;}
posted @ 2021-07-12 20:54  de06  阅读(69)  评论(0编辑  收藏  举报