1、为什么要使用结构体
思考:输入输出10个学生的年龄
数据模型:数组
操作:循环
思考:年龄只是学生的一个属性,如果想描述学生所有的信息
姓名 char name[20];
学号 int num;
性别 char sex;
年龄 int age;
成绩 float score;
住址 char addr[100];
为什么使用结构体类型:当程序中出现大量不同(相同)数据类型变量时,这些变量合在一起可以描述某些信息,通过结构体类型对这些不同变量进行整体的声明
2、结构体类型的声明
格式:
struct 结构体名
{
成员列表;
};
struct:声明结构体类型的关键字,不能够省略
struct 结构体名:结构体类型
成员列表:多个数据类型,变量名
例子:
姓名 char name[20];
学号 int num;
性别 char sex;
年龄 int age;
成绩 float score;
住址 char addr[100];
struct student
{
char name[20];
int num;
char sex;
int age;
float score;
char addr[100];
};
名字
{
代码;
}
注意:
结构体类型的声明只是相当于系统下创建一个新的数据类型,但是并没有定义出该类型下的变量,所以不会开辟任何的存储空间
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
};//结构体类型的声明
struct teacher
{
char name[NAME_SIZE];
int age;
char sex;
};
struct birthday
{
int year;
int month;
int day;
};
int main(int argc, const char *argv[])
{
return 0;
}
练习:
1、定义出描述老师相关属性的结构体
struct teacher
{
char name[20];
char sex;
int age;
};
2、定义出描述生日的结构体(年、月、日)
struct birthday
{
int year;
int month;
int day;
};
补充:
1:结构体类型成员名可以和系统中的变量名相同
2:结构体成员也可以是其他类型的结构体变量
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct birthday
{
int year;
int month;
int day;
};
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
struct birthday bd;//生日
};//结构体类型的声明
int main(int argc, const char *argv[])
{
return 0;
}
3、结构体类型变量的定义
1:声明结构体类型的同时定义结构体变量
2:先声明结构体类型再定义结构体变量(常用)
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
}std1;//声明的同时定义变量
int main(int argc, const char *argv[])
{
struct student std2;//先声明后定义
return 0;
}
注意:
结构体类型的声明只是声明了一个数据类型,不开辟存储空间,如果定义出该类型下的变量,就需要开辟存储空间。
声明一个结构体类型——>int a;中int
定义一个结构体类型变量——>int a中的a
扩充:
相同类型下的结构体变量之间可以直接赋值
struct student stu1={chen chao ….};
struct student stu2=stu1;
4、结构体类型变量的初始化
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
}std1 = {"songhuiqiao", 2, 'W', 35, 99, "hui long guan" };//声明的同时定义变量
int main(int argc, const char *argv[])
{
struct student std2 = {"chenchao", 1, 'M', 25, 100, "hui long guan"};//先声明后定义
return 0;
}
5、指向结构体变量的指针
1:定义
格式:struct 结构体名 *变量;
2:赋值
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
};//声明的同时定义变量
int main(int argc, const char *argv[])
{
struct student std1 = { "chenchao", 1, 'M', 25, 100, "hui long guan"};//先声明后定义
struct student *p1 = &std1;//初始化
struct student *p2 = NULL;
p2 = &std1;
return 0;
}
3:大小 8个字节
6、结构体变量成员的应用
1:通过结构体变量引用结构体变量的成员
格式:结构体变量名 . 成员名
2:通过指针来引用结构体成员
格式:指针变量->成员名
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
};//声明的同时定义变量
int main(int argc, const char *argv[])
{
struct student std1 = { "chenchao", 1, 'M', 25, 100, "hui long guan"};//先声明后定义
struct student *p = &std1;//初始化
printf("std1.name = %s\nstd1.num = %d\nstd1.sex = %c\nstd1.age = %d\nstd1.score = %f\nstd1.addr = %s\n", std1.name, std1.num, std1.sex, std1.age, std1.score, std1.addr);//结构体变量名
printf("(*p).name = %s\n(*p).num = %d\n(*p).sex = %c\n(*p).age = %d\n(*p).score = %f\n(*p).addr = %s\n", (*p).name, (*p).num, (*p).sex, (*p).age, (*p).score, (*p).addr);//指向结构体变量的指针
printf("p->name = %s\np->num = %d\np->sex = %c\np->age = %d\np->score = %f\np->addr = %s\n", p->name, p->num, p->sex, p->age, p->score, p->addr);//指向结构体变量的指针
printf("(&std1)->name = %s\n(&std1)->num = %d\n(&std1)->sex = %c\n(&std1)->age = %d\n(&std1)->score = %f\n(&std1)->addr = %s\n", (&std1)->name, (&std1)->num, (&std1)->sex, (&std1)->age, (&std1)->score, (&std1)->addr);//结构体变量名
return 0;
}
课堂练习:
1:输入两个学生的学号、姓名、和成绩,输出成绩较高的学生的学号、姓名和成绩。
#include <stdio.h>
#define NAME_SIZE 20
struct student//结构体类型的声明
{
int num;
char name[NAME_SIZE];
int score;
};
int main(int argc, const char *argv[])
{
struct student std1 = {};
struct student std2 = {};
scanf("%d", &(std1.num));//输入学生信息
getchar();
gets(std1.name);
scanf("%d", &(std1.score));
scanf("%d", &(std2.num));
getchar();
gets(std2.name);
scanf("%d", &(std2.score));
if (std1.score > std2.score){//比较两个学生的成绩
printf("std1.num = %d\nstd1.name = %s\nstd1.score = %d\n", std1.num, std1.name, std1.score);
} else {
printf("std2.num = %d\nstd2.name = %s\nstd2.score = %d\n", std2.num, std2.name, std2.score);
}
return 0;
}
2:输入三个学生的学号、姓名和成绩,按成绩高低排序,并输出排序后结果
#include <stdio.h>
#define NAME_SIZE 20
struct student//结构体类型的声明
{
int num;
char name[NAME_SIZE];
int score;
};
int main(int argc, const char *argv[])
{
int i,j;
struct student std[3] = {};
struct student st = {};
for (i=0; i<3; i++) {
scanf("%d %s %d",&std[i].num,std[i].name,&std[i].score);
}
for (i=0; i<3; i++) {
for (j=0; j<3-i; j++) {
if (std[j].score>std[j+1].score) {
st=std[j];
std[j]=std[j+1];
std[j+1]=st;
}
}
}
for (i=0; i<3; i++) {
printf(" %d %s %d\n", std[i].num, std[i].name, std[i].score);
}
return 0;
}
7、结构体数组和指向结构体数组首元素的指针及函数
思考:
大量整型变量——>整型数组
大量字符变脸——>字符数组
大量指针变量——>指针数组
大量结构体变量——>结构体数组
格式:
struct 结构体名 数组名[常量];
数组名:数组首元素的地址,地址常量
struct 结构体名:数组元素的类型
常量:数组成员的个数
赋值:
初始化
赋值语句
#include <stdio.h>
#define NAME_SIZE 20
#define ADDR_SIZE 50
#define NUM 10
struct student
{
char name[NAME_SIZE];//姓名
int num;//学号
char sex;//性别
int age;//年龄
float score;//成绩
char addr[ADDR_SIZE];//地址
};//结构体类型的声明
int main(int argc, const char *argv[])
{
struct student arr[NUM] = {};//结构体数组,数组成员10个
struct student (*pArr1) = arr;//初始化
struct student (*pArr2) = NULL;
pArr2 = arr;//赋值语句
return 0;
}
练习:
输入输出5个学生的信息
#include <stdio.h>
#define NUM 5
#define NAME_SIZE 20
struct student//声明结构体类型
{
int num;
char name[NAME_SIZE];
int age;
};
int main(int argc, const char *argv[])
{
struct student arr[NUM] = {};//结构体数组
int i = 0;
//输入
for (i=0; i<NUM; i++){//arr[i]结构体变量
scanf("%d", &(arr[i].num));
getchar();
gets(arr[i].name);
scanf("%d", &(arr[i].age));
}
//输出
for (i=0; i<NUM; i++){
printf("arr[%d].num = %d\narr[%d].name = %s\narr[%d].age = %d\n", i, arr[i].num, i, arr[i].name, i, arr[i].age);
}
return 0;
}
#include <stdio.h>
#define NUM 5
#define NAME_SIZE 20
struct student//声明结构体类型
{
int num;
char name[NAME_SIZE];
int age;
};
int main(int argc, const char *argv[])
{
struct student arr[NUM] = {};//结构体数组
struct student *pArr = arr;//指向结构体数组首元素的指针
int i = 0;
//输入
for (i=0; i<NUM; i++){//arr[i]结构体变量
scanf("%d", &((pArr+i)->num));
getchar();
gets((pArr+i)->name);
scanf("%d", &((pArr+i)->age));
}
//输出
for (i=0; i<NUM; i++){
printf("(pArr+%d)->num = %d\n(pArr+%d)->name = %s\n(pArr+%d)->age = %d\n", i, (pArr+i)->num, i, (pArr+i)->name, i, (pArr+i)->age);
}
return 0;
}
#include <stdio.h>
#define NUM 5
#define NAME_SIZE 20
struct student//声明结构体类型
{
int num;
char name[NAME_SIZE];
int age;
};
void input(struct student arr[], int n);//声明
void output(struct student arr[], int n);//声明
int main(int argc, const char *argv[])
{
struct student arr[NUM] = {};//结构体数组
input(arr, NUM);//调用
output(arr, NUM);
return 0;
}
void input(struct student arr[], int n)//定义
{
int i = 0;
//输入
for (i=0; i<n; i++){//arr[i]结构体变量
scanf("%d", &(arr[i].num));
getchar();
gets(arr[i].name);
scanf("%d", &(arr[i].age));
}
}
void output(struct student arr[], int n)
{
int i = 0;
//输出
for (i=0; i<n; i++){
printf("arr[%d].num = %d\narr[%d].name = %s\narr[%d].age = %d\n", i, arr[i].num, i, arr[i].name, i, arr[i].age);
}
}
#include <stdio.h>
#define NUM 5
#define NAME_SIZE 20
struct student//声明结构体类型
{
int num;
char name[NAME_SIZE];
int age;
};
void input(struct student *pArr, int n);//声明
void output(struct student *pArr, int n);
int main(int argc, const char *argv[])
{
struct student arr[NUM] = {};//结构体数组
struct student *pArr = arr;//指向结构体数组首元素的指针
input(arr, NUM);
output(arr, NUM);
return 0;
}
void input(struct student *pArr, int n)//定义
{
int i = 0;
//输入
for (i=0; i<n; i++){//arr[i]结构体变量
scanf("%d", &((pArr+i)->num));
getchar();
gets((pArr+i)->name);
scanf("%d", &((pArr+i)->age));
}
}
void output(struct student *pArr, int n)
{
int i = 0;
//输出
for (i=0; i<n; i++){
printf("(pArr+%d)->num = %d\n(pArr+%d)->name = %s\n(pArr+%d)->age = %d\n", i, (pArr+i)->num, i, (pArr+i)->name, i, (pArr+i)->age);
}
}
练习:
num
name
score
1:输入5个学生的全部信息,输出成绩最高的学生的信息
2:输入5个学生的全部信息,按成绩(姓名)高低进行排序,输出排序后学生的信息
#include <stdio.h>
#include <string.h>
#define SIZE 100
#define NUM 5
struct student{
int num;
char name[SIZE];
int score;
};
void input(struct student stu[],int n);
void output(struct student stu[],int n);
void paixu(struct student stu[],int n);
void xmpaixu(struct student stu[],int n);
int main(int argc, const char *argv[])
{
struct student stu[NUM]={};
input(stu,NUM);
paixu(stu,NUM);
output(stu, NUM);
printf("\n");
xmpaixu(stu,NUM);
output(stu, NUM);
printf("\n");
return 0;
}
void input(struct student stu[],int n){
int i;
for (i=0; i<n; i++) {
scanf("%d %s %d",&stu[i].num,stu[i].name,&stu[i].score);
}
}
void output(struct student stu[],int n){
int i;
for (i=0; i<n; i++) {
printf("%d %s %d\n",stu[i].num,stu[i].name,stu[i].score);
}
}
void paixu(struct student stu[],int n){
int i,j;
struct student s;
for (i=0; i<n; i++) {
for (j=0; j<n-1; j++) {
if (stu[j].score<stu[j+1].score) {
s=stu[j];
stu[j]=stu[j+1];
stu[j+1]=s;
}
}
}
}
void xmpaixu(struct student stu[],int n){
int i,j;
struct student s;
for (i=0; i<n; i++) {
for (j=0; j<n-1; j++) {
if (strcmp(stu[j].name, stu[j+1].name)>0) {
s=stu[j];
stu[j]=stu[j+1];
stu[j+1]=s;
}
}
}
}
补充:
1、预处理
1:什么是预处理
预处理发生在编译程序的第一个阶段,将预处理命令装换成纯粹的c语句
2:常见预处理的种类和特点
种类:
1、宏定义
2、文件包含
3、条件编译
特点:
1、#开头
2、独占一行
3、末尾没有分号
3:宏定义
1、不带参数的宏定义
格式:#define 宏名 字符串
作用:将程序中出现宏名的地方都用字符串进行简单的替换
作用范围:定义开始,文件末尾结束
定义的位置:程序的任何位置
例子:
#define NUM 10
#define SIZE 100
例子:求圆的面积
#define PI 3.14
#define R 3
printf(“%f”, R*PI);
2、带参数的宏定义
格式:#define 宏名(参数列表) 字符串
作用:不仅要对字符串进行替换,还要对参数进行替换
例子:
#define S(a, b) a+b
int area = S(3, 5);——>3+5
方法:实参替换行参,其他字符串原样保留
#define PI 3
#define S(r) ((r)*(r)*(PI))
printf(“%f”, S(3));——>3*3*3.14
思考:
s(3+2);——> ((3+2) *(3+2)* (3))
a*=b;
3 *= 2+3;——>a = 3*(2+3);
4:文件包含
文件包含:就是把头文件内容加载到当前程序中
1、#include <head.h>:到存放系统标准库函数头文件的路径去查找head.h
2、#include“head.h”:首先在当前路径下查找head.h文件,如果找不到,按照标准方式查找
5:条件编译
条件编译:正常情况下程序中的每一行代码都要参与编译,如果想让程序中的某些代码在满足一定条件下才参与编译,这就是条件编译
条件编译的几种形式:
1:
#ifdef 标识符
程序段1;
#else //可有可无
程序段2;
#endif
执行过程:如果标识符已经被#define过了则编译程序段1,否 则编译程序段2
2:
#ifndef 标识符
程序段1;
#else //可有可无
程序段2;
#endif
执行过程:如果标识符已经被#define过了则编译程序段2,否 则编译程序段1
工程常用条件编译:
1:#if 0 #endif之间的代码就不参与编译
#if 0
#endif
2:防止头文件重复包含
.h文件开始处加上:
#ifndef _HEAD_H
#define _HEAD_H
#endif
2、枚举
1:枚举的作用
思考:如何描述一个季节
季节:春、夏、秋、冬
枚举:当一个变量的值,只有几种可能的时候,这时候可以通过枚举元素去一一列举变量的值
例子:
1:季节
2:星期
3:月份
2:枚举类型的声明
格式:enum 枚举名 {枚举元素列表};
enum:声明枚举类型的关键字,不能省略
enum 枚举名:枚举类型
例子:
enum season{spring,summer,autumn,winter};
enum week{……};
enum month{…………};
3:枚举变量的定义
1、声明的同时定义枚举变量
2、先声明枚举类型,再定义枚举变量
#include <stdio.h>
enum Season{spring, summer, autumn, winter}season1;//声明枚举类型的同时定义变量
int main(int argc, const char *argv[])
{
enum Season season2;//先声明枚举类型,在定义枚举变量
return 0;
}
4:枚举变量的赋值
#include <stdio.h>
enum Season{spring, summer, autumn, winter}season1;//声明枚举类型的同时定义变量
int main(int argc, const char *argv[])
{
enum Season season2;//先声明枚举类型,在定义枚举变量
return 0;
}
chenchaos-MacBook-Pro:day10 mac$ vim 1.c
chenchaos-MacBook-Pro:day10 mac$ cat 1.c
#include <stdio.h>
enum Season{spring, summer, autumn, winter};//枚举类型的声明
int main(int argc, const char *argv[])
{
enum Season season1 = spring;//先声明枚举类型,在定义枚举变量
enum Season season2;//先声明枚举类型,在定义枚举变量
season2 = winter;
return 0;
}
5:关于枚举的几点说明
1、编译器是把枚举元素的值当作常量来处理的,程序运行过程中不能够修改枚举元素的值
2、枚举元素中,每个元素的值对应一个整数值,枚举元素值的大小是由枚举元素在枚举元素列表中的位置决定的
spring(0)summer(1),autumn(2),winter(3)
枚举元素可以在声明的同时进行初始化
#include <stdio.h>
enum Season{spring, summer=5, autumn, winter};//枚举类型的声明
int main(int argc, const char *argv[])
{
enum Season season = spring;//先声明枚举类型,在定义枚举变量
printf("season = %d\n", season);
return 0;
}
3、枚举元素之间,可以进行关系运算
#include <stdio.h>
enum Season{spring, summer, autumn, winter};//枚举类型的声明
int main(int argc, const char *argv[])
{
enum Season season;//先声明枚举类型,在定义枚举变量
for (season=spring; season<=winter; season++){
printf("season = %d\n", season);
}
return 0;
}
3、typedef
1:typedef的作用
typedef可以对系统中已经存在的数据类型起一个别名,typedef并没有创新的数据类型,只是在原有类型的基础上起了一个新的名字
2:typedef给类型起别名的步骤
1、定义出原有类型下的变量
2、将变量名改成新的类型名
3、在最开始处加上typedef
4、用新类型名定义出变量
3:typedef和其他数据类型
1: typedef和基本数据类型
例子:
int
typedef int Interger;
Interger a;//等价于int a;//Interger是数据类型,等价于int
2: typedef和数组
例子:
int [10];
typedef int Arr10[10]; Arr10 arr;//等价于int arr[10];//Arr10是数据类型,等价于int [10]
3: typedef和指针
1: typedef和一级指针
例子:typedef int *Pointer;
Pointer p;//等价于 int *p//Pointer是数据类型,等价于int *
2:typedef和二级指针
例子:typedef int **PPointer;
PPointer pp;//等价于 int **pp//PPointer是数据类型,等价于int **
3:typedef和函数指针
例子:
int add(int a, int b);
typedef int (*Pfun)(int a, int b);
Pfun p = add;//等价于int (*p)(int a, int b)//Pfun是数据类型,int (*)(int a, int b)
4: typedef和结构体
1: typedef和结构体变量
2: typedef和指向结构体变量的指针
typedef struct student//结构体名可以省略
{
int num;
char name[20];
}Student, *Pstudent;
Student std;//等价于struct student std;//Student是数据类型,struct student
Pstudent p;//等价于struct student *p;//Pstudent数据类型,struct student *
5: typedef和枚举
例子:
typedef enum Season{spring, summer, autumn, winter} EnumSeason;
EnumSeason season;//等价于enum Season season//EnumSeason是数据类型,enum Season
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
typedef int interger;//基本数据类型
typedef int Arr10[10];//数组
typedef int *Pointer;//一级指针
typedef int **PPointer;//二级指针
typedef int (*Pfun)(int a, int b);//函数指针
typedef struct student
{
int num;
char name[20];
}Studnet, *Pstudent;//结构体
typedef enum Season {spring, summer, autumn, winter}EnumSeason;//枚举
int main(int argc, const char *argv[])
{
interger a;//整型变量
Arr10 arr;//数组,数组成员10个
Pointer p;//一级指针
PPointer pp;//二级指针
Pfun pfun = add;//函数指针
Studnet std;//结构体变量
Pstudent pstd;//结构体指针
EnumSeason season;//枚举变量
return 0;
}