有参宏和函数的区别
1.展开时机:有参宏而言,在预处理阶段展开,而函数在调用时才展开
2.内存使用:有参宏而言,占用的是所在函数的空间,而函数在调用时会单独开辟空间
3.效率上:有参宏的效率比函数有的效率高
4.有参宏是宏定义,只替换不计算不做正确性检查,而函数调用要符合函数的调用标准,形参会进行计算
结构体
概念:结构体是由相同数据类型或不同数据类型构成的连续存储的变量的集合,结构体属于构造数据类型
定义格式
struct 结构体名
{
成员类型1 成员变量1;
成员类型2 成员变量2;
......
}
注意事项:
1.struct是结构体的关键字,在C语言中定义结构体时不能省略
2.struct后面跟的是结构体类型名,类似于int float double
3.所有的结构体成员属性使用一对花括号包裹,每个成员之间使用分号隔开
4.成员变量的类型,可以是任意类型(可以是基本数据类型,也可以是构造数据类型、指针类型、空类型)
5.花括号后的分号绝对不能省略
struct Student //若没有student(结构体名)后续便不能继续定义结构体变量
{
int num; //学号
char name[20]; //姓名
double score; //成绩
char sex; //性别
char TEL[11]; //手机号
}s1,s2;
//定义结构体类型时顺便定义了两个结构体变量
/*struct student s1,s2;*/
结构体可以定义在程序的任何地方,可以定义在函数体内也可以定义在函数体外,但是必须要定义在使用之前 一般定义在函数体外,分文件编译时,通常定义在头文件内
结构体变量的初始化,也有三种方式,初始化时所有成员是花括号包裹,每个成员之间使用逗号隔开
//定义一个结构体类型
struct Student
{
int num; //学号
char name[20]; //姓名
double score; //成绩
char sex; //性别
char TEL[11]; //手机号
};
//在程序中
struct Student s1 = {1001, "张三", 99.5, 'M', "13888888888"}; //定义了一个结构体变量并初始化
//定义一个结构体类型
struct Student
{
int num; //学号
char name[20]; //姓名
double score; //成绩
char sex; //性别
char TEL[11]; //手机号
}s1 = {1002, "李四", 99.8, 'W',13888888888};
定义结构体变量并给部分成员初始化
struct Student
{
int num; //学号
char name[20]; //姓名
double score; //成绩
char sex; //性别
char TEL[11]; //手机号
};
//在程序中
struct Student s1 = {.num="张三", .score=99.5}; //定义了一个结构体变量并初始化
2.4结构体类型访问成员
1.结构体变量访问成员,使用成员.运算符来完成
使用方法:变量名.成员名;
2.结构体指针访问成员,使用成员运算符->来完成
使用方法:指针变量->成员名;
3.虽然这两个运算符是双目运算符但是拥有较高的优先级。比单目运算符优先级高
4.上面两个运算执行后结果是 成员类型,跟结构体变量和结构体指针就没有关系了
5.不要试图通过结构体变量名,操作所有成员,只能通过成员运算符一个一个操作
6.结构体变量支持两种运算:取地址运算、赋值运算
7.结构体类型中,也可以有另一个结构体变量,访问最里层的成员时需要成员运算符一级一级找到最低一级
#include<myhead.h>
struct Student; //结构体类型的声明
//定义一个生日类型
struct Birthday
{
int year; //年份
int month; //月份
int day; //日
};
//定义结构体类型
struct Student
{
int num; //学号
char name[20]; //姓名
double score; //成绩
struct Birthday bir; //生日
} s1 = {1002, "张三", 99.5}; //分号不能省略
int main(int argc, const char *argv[])
{
//使用结构体类型定义变量并初始化
struct Student s2 = {1001, "zhangpp", 99, {2000,2,20}};
struct Student s3 = {.num=1003, .score=100}; //对部分成员初始化
s1.num = 9527; //使用成员运算符对成员进行操作
printf("s1.num = %d, s1.name=%s, s1.score=%.2lf\n", \
s1.num, s1.name, s1.score);
//printf("%s\n", s1); //不可以通过结构体变量名输出所有成员
printf("&s1 = %p\n", &s1); //输出的是s1的地址
struct Student *ptr = &s1; //结构体指针变量
ptr->num = 9999;
printf("ptr->num = %d, ptr->name=%s, ptr->score=%.2lf\n", \
ptr->num, ptr->name, ptr->score);
//结构体变量支持赋值运算
s1 = s2;
printf("ptr->num = %d, ptr->name=%s, ptr->score=%.2lf\n", \
ptr->num, ptr->name, ptr->score);
//访问结构体成员中的结构体变量
s1.bir.year = 2024; //需要使用成员运算符一级一级找到最低一级
ptr->bir.month = 3;
printf("生日:%d-%d-%d\n", ptr->bir.year, ptr->bir.month, ptr->bir.day);
return 0;
}
结构体数组
1.本质是一个数值,每个元素都是结构体变量
2.结构体数组的定义和使用跟普通数组一致,但是,访问到数组元素时,每个元素需要继续是成员运算符找到其结构体成员进行操作
#include<myhead.h>
//定义一个英雄类型
struct Hero
{
char name[20]; //英雄名称
int kill; //击杀人头数
int die; //死亡此处
int assist; //辅助次数
};
//定义函数,录入英雄本局比赛的信息
void input_hero_msg(struct Hero *ptr, int n)
{
//录入信息
for(int i=0; i<n; i++)
{
printf("请输入第%d个英雄的名称:", i+1);
scanf("%s", ptr[i].name); //(ptr+i)->name
printf("请输入第%d个英雄的人头数:", i+1);
scanf("%d", &ptr[i].kill);
printf("请输入第%d个英雄的死亡数:", i+1);
scanf("%d", &ptr[i].die);
printf("请输入第%d个英雄的辅助数:", i+1);
scanf("%d", &ptr[i].assist);
printf("\n");
}
printf("录入成功\n");
}
//定义输出函数
void output_hero_msg(struct Hero *ptr, int n)
{
printf("本局比赛结算如下:\n");
printf("英雄\tkill\tdie\tassist\n");
for(int i=0; i<n; i++)
{
printf("%s\t%d\t%d\t%d\n", ptr[i].name,ptr[i].kill, ptr[i].die, ptr[i].assist);
}
}
//定义求mvp函数
struct Hero mvp_hero(struct Hero *ptr, int n)
{
struct Hero mvp = ptr[0]; //将第一个英雄当做mvp
for(int i=0; i<n; i++)
{
//拿着当前mvp的数据跟任意一个进行比较
if( mvp.kill*0.9+mvp.assist*0.5-mvp.die*0.4 < \
ptr[i].kill*0.9+ptr[i].assist*0.5-ptr[i].die*0.4 )
{
//更新mvp
mvp = ptr[i];
}
}
//将mvp返回
return mvp;
}
int main(int argc, const char *argv[])
{
//定义数组存储五个英雄
struct Hero hero[5]; //hero[0]---hero[4]
//录入英雄的信息
input_hero_msg(hero, 5);
//输出英雄信息
output_hero_msg(hero, 5);
//求 mvp
struct Hero res = mvp_hero(hero, 5);
printf("本局比赛的mvp为:%s,%d,%d,%d\n", \
res.name,res.kill,res.die,res.assist);
return 0;
}
结构体大小
1.定义结构体类型时,系统不分配内存空间,只有使用结构体类型定义变量时才分配内存空间
2.系统为结构体变量分配的空间是连续的,系统会将结构体每个成员看成一个整体进行操作
3.结构体大小:是每个成员变量所占内存大小之和-->字节对齐原则
结构体变量实际大小>=各个成员变量所占内存之和
4.C语言中,一个空的结构体所占内存为0字节
#include<myhead.h>
struct AA //空结构体所占内存为0字节
{
};
struct BB //只有一个字符成员
{
char value; // 1
};
struct CC //有两个成员
{
short key; // 1120
char value;
};
struct DD //有三个成员
{
short key;
char value;
int temp; //11203333
};
struct EE //两个成员
{
char value;
int temp; //10002222
};
struct FF //有四个成员
{
int num;
char value;
char *ptr;
short key; // 111120003333333344000000
};
struct GG //有四个成员
{
char *ptr;
int num;
char value;
short key; // 1111111122223044
};
struct HH //一个普通变量和一个结构体变量
{
short num;
struct EE value; //110010002222
};
struct II //一个普通变量和一个结构体变量
{
char temp;
struct GG g; // 100000001111111122223044
};
int main(int argc, const char *argv[])
{
printf("sizeof(struct AA) = %zd\n", sizeof(struct AA)); //0
printf("sizeof(struct BB) = %zd\n", sizeof(struct BB)); //1
printf("sizeof(struct CC) = %zd\n", sizeof(struct CC)); //4
struct CC c;
printf("&c=%p, &c.value=%p, &c.key = %p\n", \
&c, &c.value, &c.key);
printf("sizeof(struct DD) = %zd\n", sizeof(struct DD)); //8
printf("sizeof(struct EE) = %zd\n", sizeof(struct EE)); //8
printf("sizeof(struct FF) = %zd\n", sizeof(struct FF)); //24
printf("sizeof(struct GG) = %zd\n", sizeof(struct GG)); //16
printf("sizeof(struct HH) = %zd\n", sizeof(struct HH)); //12
struct HH h;
printf("&h=%p, &h.num=%p, &h.value = %p, &h.value.value=%p, &h.value.temp=%p\n", \
&h, &h.num, &h.value, &h.value.value, &h.value.temp);
printf("sizeof(struct II) = %zd\n", sizeof(struct II)); //24
return 0;
}
特殊关键字的使用
const
1.在定义变量后,变量有两个属性,读属性(获取值)写属性(修改值)
2.const:保护数据的作用,给变量以常属性
3.const的用法
const可以修饰普通变量:修饰普通变量时表示给该变量以常属性(不能通过该变量进行写操作)
const修饰的局部变量,必须进行初始化,如果不初始化,则为随机值,并且后期不能更改,可以使用指针指向该变量,使用指针更改其值是没有问题的
#include<myhead.h>
int main(int argc, const char *argv[])
{
//1、const修饰普通变量
const int num = 520;
printf("num = %d\n", num); //具有读属性
//num = 1314; //不能通过变量名进行更改
int *ptr = # //定义指针指向num
*ptr = 1314; //使用指针更改num的值
printf("num = %d\n", num); //具有读属性
return 0;
}
const修饰指针变量
const int*ptr:保护的是指针指向内存空间的值,指针的指向可以改变,但是不能通过改变指针改变其指向内存空间的值
int const *ptr:保护的是指针指向内存空间的值,指针的指向可以改变,但是不能通过改变指针改变其指向内存空间的值
int *const ptr:保护的是指针的值(指针指向)。指针指向不可以改变,但是可以通过指针改变其指向的内存空间中的值
const int *const ptr:全部进行保护,既不能改变指针的指向,也不能通过指针改变其指向内存空间中的值
#include<myhead.h>
int main(int argc, const char *argv[])
{
int num = 520; //普通变量
int key = 1314;
//定义指针变量
const int *ptr1 = # //保护指针指向的内存空间中的值
//*ptr1 = 666; //执行不成功 不能进行写操作
printf("*ptr1 = %d\n", *ptr1);
ptr1 = &key; //可以改变指针的指向
//定义指针变量
int const *ptr2 = # //保护指针指向的内存空间中的值
//*ptr2 = 666; //执行不成功 不能进行写操作
printf("*ptr2 = %d\n", *ptr2);
ptr2 = &key; //可以改变指针的指向
//定义指针变量
int * const ptr3 = # //保护的是指针的指向(指针的值)
*ptr3 = 666; //可以通过指针改变指向的内存空间中的值
printf("*ptr3 = %d\n", *ptr3); //读取数据
//ptr3 = &key; //不可以改变指针的值(指向)
//定义指针变量
const int *const ptr4 = #
//*ptr4 = 999; //不可以通过指针改变指向的内存空间中的值
//ptr4 = &key; //不可以改变指针的指向
return 0;
}
const修饰函数的形式参数,表示保护形参数据不被修改
#include<myhead.h>
//保护形参的指向不被修改
void fun(int * const ptr)
{
//如果没有保护指针的指向,那么下面会出现段错误
if(ptr=NULL)
{
}
*ptr = 1314;
}
//保护传入的指针指向的内存空间中的值不会在函数体内被更改
void hun(const char *ptr)
{
//如果没有保护指针指向的空间的值,则下面程序会出现段错误
ptr[0] = 'A';
}
int main(int argc, const char *argv[])
{
int num = 520;
fun(&num); //调用函数传递地址
printf("num = %d\n", num);
hun("hello a"); //调用函数传递一个字符串
return 0;
}
const修饰函数的返回值,表示保护函数的返回结果不被修改,常用于指针函数
*fun() = 520
ubuntu@ubuntu:day17$ cat 08const.c
#include<myhead.h>
//const修饰的函数,称为常函数,表示保护返回结果不能被修改
const int *fun()
{
static int num = 520;
return # //指针函数返回静态局部变量的地址
}
int main(int argc, const char *argv[])
{
//*fun() = 1314; //不能被修改,因为函数由const修饰了
printf("*fun() = %d\n", *fun()); //?
return 0;
}
static
1> static修饰局部变量,表示相当于在函数体内定义一个全局变量,该变量的生命周期不会随着所在函数的开始而开始结束而结束,并且static修饰的变量的空间不依附所在函数。其生命周期从程序运行时就开始了。
2> static修饰全局变量,表示该变量只能在所在文件中使用,不能被其他文件所引入
3> static修饰函数时,表示该函数也只能在该文件中被使用,其他文件不能使用
extern
1.可以在某个文件中使用extern来引入其他文件中的全局变量
2.在局部变量和全局变量同名时,函数体内优先使用局部变量,如果非要使用全局变量,可以使用extern引入后使用
#include<myhead.h>
int num = 520; //全局变量
int main(int argc, const char *argv[])
{
int num = 1314; //局部变量
if(1)
{
extern int num; //引入外部变量
printf("num = %d\n", num); //?
}
return 0;
}
作业
1> 使用结构体数组完成班级学生管理系统,学生类型中包含 (学号、姓名、成绩、性别、TEL)
2> 完成菜单选项
3> 功能1:信息录入,提示并输入班级人数,完成每个学生信息的录入
4> 功能2:信息展示
5> 功能3:添加一个学生操作
6> 功能4:提示并输入一个学号,并将该学号对应的学生信息删除
7> 功能5:提示并输入一个学生姓名,更改其联系方式
8> 功能6:提示并输入一个学生姓名,展示该学生的所有信息
9> 功能7:将学生按成绩进行降序排序
10> 功能0:退出系统
#include<luochen.h>
#include "homework.h"
int menu()
{
int menu;
printf("请输入你想要实现的功能:\n");
printf("1.信息录入\n");
printf("2.信息展示\n");
printf("3.添加成绩\n");
printf("4.删除成绩\n");
printf("5.更改成绩\n");
printf("6.查找学生\n");
printf("7.降序排列\n");
printf("0.退出系统\n");
scanf ("%d",&menu);
return menu;
}
void gn1(struct student *body,int *n)
{
printf("请输入需要录入的人数:");
scanf("%d",n);
for (int i=0;i<*n;i++)
{
printf("请输入第%d同学的学号:",i+1);
scanf("%d",&body[i].num);
printf("请输入第%d同学的姓名:",i+1);
scanf("%s",body[i].name);
printf("请输入第%d同学的成绩:",i+1);
scanf("%d",&body[i].score);
printf("请输入第%d的性别(m/w):",i+1);
getchar();
scanf("%c",&body[i].sex);
printf("请输入第%d同学的电话:",i+1);
scanf("%s",body[i].tel);
printf("\n");
} printf("录入成功\n");
printf("\n");
}
void gn2(struct student *body,int *n)
{
//信息展示
for(int i=0;i<*n;i++)
{
printf("%d/t%s/t%d/t%c/t%s\n",body[i].num,body[i].name,body[i].score,body[i].sex,body[i].tel);
}
}
void gn3(struct student *body,int *n)
{
printf("请输入学生学号:");
scanf("%d",&body[*n].num);
printf("请输入学生姓名:");
scanf("%s",body[*n].name);
printf("请输入学生成绩:");
scanf("%d",&body[*n].score);
printf("请输入的性别(m/w):");
getchar();
scanf("%c",&body[*n].sex);
printf("请输入学生电话:");
scanf("%s",body[*n].tel);
*n+=1;
printf("添加成功\n");
}
void gn4(struct student *body,int *n)
{
int a;
int b=0;
//判断是否删除成功
printf("请输入学生学号");
scanf("%d",&a);
for (int i=0;i<*n;i++)
{
if(a==body[i].num)
{
for (int j=i;j<*n-1;j++)
{
body[j]=body[j+1];
}(*n)--;
b=1;
}
}
if(b)
{
printf("删除成功\n");
}else{
printf("为查询到该同学\n");
}
}
void gn5(struct student *body,int *n)
{
int a=0;
//判断是否更改成功
char name[20]="";
printf("请输入更改电话的学生姓名:");
scanf("%s",name);
for (int i=0;i<*n;i++)
{
if(strcmp(name,body[i].name)==0)
{
printf("请输入更改后的电话:");
scanf("%s",body[i].tel);
a=1;
}
}if(a)
{
printf("更改成功\n");
}else{
printf("未查询到该学生\n");
}
}
void gn6(struct student *body,int *n)
{
//查找学生信息
char name[20]="";
int a=0;
printf("请输入你要查找的学生姓名:");
scanf("%s",name);
for (int i=0;i<*n;i++)
{
if(strcmp(name,body[i].name)==0)
{
printf("%d\t%s\t%d\t%c\t%s\n",body[i].num,body[i].name,body[i].score,body[i].sex,body[i].tel);
a=1;
}
}if(!a)
{
printf("查询失败\n");
}
}
void gn7(struct student *body,int *n)
//降序排列
{
for (int i=1;i<*n;i++)
{
for (int j=0;j<*n-i;i++)
{
if(body[j].score<body[j+1].score)
{
struct student temp=body[j];
body[j]=body[j+1];
body[j+1]=temp;
}
}
}printf("排序成功\n");
}
#include <luochen.h>
#include "homework.h"
int main(int argc, const char *argv[])
{
struct student body[MAX];
int num = 0;
//定义学生人数
while (1)
{
switch(menu())
{
case 1:{
gn1(body,&num);
}break;
case 2:{
gn2(body,&num);
}break;
case 3:{
gn3(body,&num);
}break;
case 4:{
gn4(body,&num);
}break;
case 5:{
gn5(body,&num);
}break;
case 6:{
gn6(body,&num);
}break;
case 7:{
gn7(body,&num);
}
case 0:exit(0);
break;
default:
printf("输入有误,请重新输入\n");
}
}
return 0;
}
#ifndef HOMEWORK_H
#define HOMEWORK_H
#define MAX 100
struct student
{
int num;//记录学号
char name[20];//记录名字
int score;//记录成绩
char sex;//记录性别
char tel[11];//记录电话号码
};
void gn1(struct student *body,int *n);
int menu();
void gn2(struct student *body,int *n);
void gn3(struct student *body,int *n);
void gn4(struct student *body,int *n);
void gn5(struct student *body,int *n);
void gn6(struct student *body,int *n);
void gn7(struct student *body,int *n);
#endif