目录
一、结构体、联合体和枚举
1.1、创建和使用结构体
1.2、结构体数组和指针
1.3、联合体
1.4、枚举
1.5、typedef关键字
二、预处理
2.1、系统库介绍
2.2、宏定义
2.3 条件编译
我们之前认识过很多种数据类型,包括整数、小数、字符、数组等,通过使用对应的数据类型,我们就可以很轻松地将我们的数据进行保存了,但是有些时候,这种简单类型很难去表示一些复杂结构。
比如我们现在要保存100个学生的信息(学生信息包括学号、姓名、年龄)我们发现似乎找不到一种数据类型能够同时保存这三种数据(数据虽然能保存一些列的元素,但是只能保存同种类型的)。但是如果把它们拆开单独存在,就可以使用对应的类型存放了,不过这样很不方便,这些数据应该是捆绑在一起的,而不是单独地去存放。所以,为了解决这种问题,C语言提供了结构体类型,它能够将多种类型的数据集结到一起,让他们形成一个整体。
#include
//使用(struct关键字 + 结构体类型名称)来声明结构体类型,这种类型是我们自己创建的(同样也可以作为函数的参数,返回值之类)
struct Student {
int id; // 结构体中可以包含多个不同类型的数据,这些数据共同组成了整个结构体类型(当然结构体内部也能包含结构体类型的变量)
int age;
char * name; //用户名可以用指针指向一个字符串,也可以用char数组来存,如果是指针的话,那么数据不会存在结构体中,只会存放字符串的地址,但是如果是数组的话,数据会存放在结构体中
};
int main(){
struct Student student = {1, 18, "小明"};
}
那么现在我们拿到结构体变量后,怎么去访问结构体内部存储的各种数据呢
int main(){
struct Student student = {1, 18, "小明"};
int a = student.age;
printf("%d", a);
}
那么结构体在内存中占据的大小是如何计算的呢?比如下面的这个结构体
struct Object {
int a;
short b;
char c;
}
这里我们可以借助sizeof关键字来帮助我们进行计算:
int main() {
printf("int类型的大小是:%lu", sizeof(int));
}
前面我们介绍了结构体,现在我们可以将各种类型的数据全部安排到结构体中一起存放了。
不过仅仅只是使用结构体还不够,我们可能需要保存很多个学生的信息,所以我们需要使用结构体类型的数组来进行保存:
#include
//使用(struct关键字 + 结构体类型名称)来声明结构体类型,这种类型是我们自己创建的(同样也可以作为函数的参数,返回值之类)
struct Student {
int id; // 结构体中可以包含多个不同类型的数据,这些数据共同组成了整个结构体类型(当然结构体内部也能包含结构体类型的变量)
int age;
char * name; //用户名可以用指针指向一个字符串,也可以用char数组来存,如果是指针的话,那么数据不会存在结构体中,只会存放字符串的地址,但是如果是数组的话,数据会存放在结构体中
};
int main(){
struct Student arr[3] = {{1, 18, "小明"},
{2, 17, "小红"},
{3, 18, "小刚"}};
printf("%s", arr[1].name);
}
#include
//使用(struct关键字 + 结构体类型名称)来声明结构体类型,这种类型是我们自己创建的(同样也可以作为函数的参数,返回值之类)
struct Student {
int id; // 结构体中可以包含多个不同类型的数据,这些数据共同组成了整个结构体类型(当然结构体内部也能包含结构体类型的变量)
int age;
char * name; //用户名可以用指针指向一个字符串,也可以用char数组来存,如果是指针的话,那么数据不会存在结构体中,只会存放字符串的地址,但是如果是数组的话,数据会存放在结构体中
};
int main(){
struct Student student = {1, 18, "小明"};
struct Student * p = &student; //同样的,类型后面加上*就是一个结构体类型的指针了
printf("%s", (*p).name); //由于.运算符优先级更高,所以需要先使用*p得到地址上的值,然后再去访问对应数据
}
同样,指针还有简便写法
int main(){
struct Student student = {1, 18, "小明"};
struct Student * p = &student; //同样的,类型后面加上*就是一个结构体类型的指针了
printf("%s", (*p).name); //由于.运算符优先级更高,所以需要先使用*p得到地址上的值,然后再去访问对应数据
printf("%s", p -> name); //使用 -> 运算符来快速将指针所指结构体的对应数据取出
}
联合体也可以在内部定义很多种类型的变量,但是它与结构体不同的是,所有的变量共用同一个空间。
#include
union Object{
int a;
char b;
float c;
};
int main() {
union Object object;
object.a = 66;
printf("%d", object.b);
}
枚举类型一般用于表示一些预设好的整数常量,比如我们风扇有低、中
高三个档位,我们总是希望别人使用我们预设好的这三个档位,而不希望使用其他的档位,因为我们风扇就只设计了这三个档位。
#include
//比如我们现在设计:1 = 低档位 2 = 中档位 3 = 高档位
//enum 枚举类型名称 {枚举 = 初始值, 枚举....}
enum status {low = 1, middle = 2, high = 3};
int main() {
enum status a = high;
if(a == low){ printf("低档位");}
else if (a == high) {
printf("高档位");
} else {
printf("中档位");
}
}
这个关键字用于给指定的类型起别名
#include
typedef int lbwnb;
int main() {
lbwnb i = 666;
printf("%d", i);
}
前面我们介绍了如何使用#include 引入其他文件,我们接着来了解一下系统为我们提供的一些常用库。实际上我们已经使用过不少官方库提供的内容了
#include
int main() {
int a;
scanf("%d", &a);
printf("%d", a);
getchar();
putchar('A');
}
包括我们在实战中用到了一次string.h中提供的计算字符串长度的函数
#include
#include
int main() {
char *c = "hello world";
printf("%lu", strlen(c));
}
#include
#include
int main() {
//现在有两个字符串,我们希望把他们拼接到一起
char a[20] = "hello", *b = "world";
//注意不能写 char *a = "world", *b = "world";
//如果直接用指针指向字符串常量,是无法进行拼接的,因为大小已经固定
//这里需要两个参数,第一个是目标字符串,将会将第二个参数的字符串拼接到第一个字符串中,注意要装得下
strcat(a, b);
printf("%s", a);
}
还有关于处理数学问题的相关库
#include
#include
int main() {
int a = 2;
double d = sqrt(a);
printf("%lf", d);
}
#include
#include
int main() {
double x = 3.14;
printf("不小于x的最小整数:%f\n", ceil(x));
printf("不大于x的最大整数:%f\n", floor(x));
}
前面我们认识了#include指令,我们接着来看#define指令,它可以实现宏定义
#include
#define PI 3.1415926
int main() {
printf("Π的值为:%f", PI);
}
我们来看看条件编译,我们还可以根据条件,选择性地对某些内容进行忽略
#include
#ifdef PI //ifdef用于判断是否定义了符号PI,如果没有的话则处理以下指令
#define M 666
#else //如果没有定义了符号PI,那么就处理这个分支的语句
#define M 777
#endif //最后以endif结束整个判断
int main() {
printf("%d", M);
}