C语言DAY 13 - 结构体和枚举

指针与函数
指针作为函数的参数
  1. 函数有多个返回值时
  2. 地址传递
  3. 如果函数的参数是一个指针,函数希望传递给调用者一个信心,保证不会修改值,就可以加一个 const 修饰
指针最为函数的返回值
  1. 当然可以作为返回值.
  2. 函数中的数组作为返回值,如果是在函数内创建的数组,函数执行完后就会回收,返回的地址虽然可以访问,数据就不一定在了.
  3. 你返回的指针指向的变量一定要保证函数结束后,那个空间没被回收.
  4. 如果一定要返回一个指针,就将空间申请在堆区.

    int* test();
    {
    int* arr = calloc(3,sizof(int));
    *arr = 10;
    *(arr+1) = 20;
    *(arr+2) = 30;
    
    return arr;
    }
    调用者:
    int* arr = test();
    
  5. 调用者用完后一定要

    free(arr);
    
    • 注意:返回值可以返回局部变量的值,但不能返回局部变量的地址!
案例

返回一个1-7数字对应的英文星期几

    char* getWeekDay(int day);
    //如果返回值是字符串,那么类型就是 char 指针
    {
        switc(day)
        {
            case 1:
                return "Monday";
        }
    }

另一种写法

//返回字符指针.
char* weekDay = "未知";
switch(day)
{
    case 1:
        weekDay = "Monday";
        break;
}
return weekDay;
调用
char* str = getWeekDay(1);
//申请在常量区的空间是不会回收的,程序结束才会回收.
指向函数的指针
程序在运行的时候,会将程序加载到内存
  1. 程序中主要有代码/指令.
  2. 代码段中主要存储程序的代码
  3. 程序的代码就包括函数
  4. 既然函数要存储到内存中,就肯定需要空间,有空间就有地址,有地址就可以声明一个指向函数的指针!
    那么调用函数就有了两种方式.
  5. 直接用函数名
  6. 用指向函数的指针.声明语法(有点复杂)并不是任意函数都可以指向的

a. 只能指向没有返回值并且没有参数的函数

返回值类型(*指针名)([参数列表]);
void(*pFunction)();
表示声明了一个指向函数的指针,名字叫做 pFunction.

b.只能指向返回值为 int 类型,并且有两个整形的参数的函数.

int(*pfun)(int num1,int num2);

c.初始化

1. 取到符合指针条件的函数地址
2. 函数的名称就代表函数的地址.
printf("%p",test);
//千万不要加小括号,加了就是执行函数,拿到返回值.

3. 将地址赋给指针变量,直接将符合条件的函数的名称赋给这个指针.
void(*pfun)() = test;
//pFun指针就指向了 test 函数.

d.调用

//简写
pFunc();
//原样.
(*pFunc)();

e.小技巧

拷贝函数头
int test(int num1,int num2);

删除函数名,用小括弧代替,里面写上*加指针名
int (*pFunction)(int num1,int num2);

后面加赋值符号,将原来的函数名(函数的地址)赋值给这个指针
int (*pFunction)(int num1,int num2) = getSum;

然后直接写指针名加括号调用
int num = pFunction(10,20);
结构体

存一个人的年龄你会怎么存?

int age = 18;
占用了4个字节,浪费

一个人的年龄0-200之间,没有负数,一个字节就够了
unsigned char age = 18;

保存一个人的身高.一个班级的平均分.
//一个变量就是来存储一个数据描述这个数据的.

  • 如果让你保存一个学生呢?

使用我们之前任何一个变量都不行
1. 姓名 字符串
2. 年龄 int
3. 性别 char
4. 成绩 double

我们要表示一个学生,就需要多个普通的变量的和起来描述.

  • 怎么合起来?

exa: 数组

数组要求元素的类型一致,所以拜拜

exa: 一种有几种普通变量合成的一个大变量.

没有这种类型的变量,但是我们可以自己定义这样的数据类型.

指定这个数据类型的变量是由哪些小变量合成的
  • 使用结构体来创建新类型的数据

    struct 新类型名称
    {
    数据类型1 变量名称1
    数据类型2 变量名称2
    数据类型3 变量名称3
    }

    创建了一个数据类型,由 char 指针 int 变量,float 联合而成.
    struct Student
    {
    char* name;
    int age;
    int score
    float height;
    };
    创建了类型,保存在代码段.

  • 刚刚只是创建了一个类型,指定了由哪些数据类型组成,还没有变量.

    再声明刚才创建的结构体类型的变量
    struct 结构体名称 变量名;
    struct Student stu;
    //这个结构体大变量是由结构里规定的小变量组合而成的.
    stu 结构体变量的类型是 struct Student.

  • 结构体变量的初始化(赋值)

    stu.name = “jack”;
    stu.score = 100;
    stu.age = 17;
    stu.height = 189.8;
    结构体名称.成员名 = 数据;
    printf(“姓名:%s 年龄:%d 成绩:%d 身高:%.2f\n”,stu.name,stu.age,stu.score,stu.height);

  • 总结,什么时候定义结构体
    1. 我们要保存一个数据, 但是发现这个数据是一个大数据,由其他小数据联合起来组成的
    2. 保存一个坐标点
    3. 一个图片的长和宽等等,使用数组每个变量没有名称,结构体更容易让人理解
    4. 注意的几个小问题

    1. 一定要先使用结构体定义新的类型,然后才可以根据这个类型声明这个类型的变量.
    2. 结构体是一个变量.所以可以批量声明

      struct Student xiaoHua,jin,meiMei,liLei

    3. 结构体的命名规范,要求每个单词首字母大写
    4. 声明结构体同时声明变量

    struct Computer
    {
    char* cpumodel;
    int menSize;
    char* brand;
    } iMac,lenvol,hp,dell;

    1. 匿名结构体

    struct
    {
    char* barnd;
    char* color;
    int price;
    }fengshan1;
    //只能在声明结构体的同时就命名
    //声明后不能单独的声明这个结构体的变量

结构体变量的初始化
刚才我们是先声明变量再使用点. 一个个的为成员赋值,你是不是觉得很麻烦?
  • 在声明结构体变量的同时,怎么为结构体变量的成员初始化呢?

    //最常用的
    struct Student xiaoHua = {“小花”,18,89}; *
    //或者初始化一部分
    struct Student liLei = {“李雷”};
    //还可以指定成员初始化
    struct Student jim = {.name = “基姆”,.age = 17,.score = 100};

  • 结构体变量的初始值

    1. 声明一个结构体变量,如果没有为成员赋值,成员就是垃圾值.
    2. 只要在声明结构体变量的同时,初始化一个成员,其他成员的值就是0;
  • 结构体的作用域.

    1. 结构体是定义在函数内部的,那么结构体类型只能在函数的内部使用
    2. 如果希望所有函数都可以使用,就定义在最顶上
    3. 一般情况下我们的结构体类型都是定义在函数的外面的,让所有的函数来用
结构体之间的相互赋值
  1. 相同结构体类型的变量之间绝对是可以相互赋值的

    struct Student xiaoMing = {“小明,19,100};
    struct Student xiaoHua = xiaoMing;

  2. 赋值原理

    将源结构体当中的成员变量的值拷贝一份给目标结构体的变量.

  3. 结构体变量赋值是值传递

    结构体数组
如果你有5个学生的信息你会怎么做?

声明5个结构体?

struct Student s1 = {"小明1",16,56};
struct Student s2 = {"小明2",18,77};
struct Student s3 = {"小明3",19,98};
struct Student s4 = {"小明4",16,67};
struct Student s5 = {"小明5",17,100};
  • 其实可以用结构体数组来管理

    声明:
    struct 结构体类型名称 数组名称[数组长度];
    struct Student students[5];
    表示我们声明了一个长度为5的结构体数组
    数组名叫 students
    数组的元素的类型的 struct Student

结构体数组
如果你有5个学生的信息你会怎么做?

声明5个结构体?

struct Student s1 = {"小明1",16,56};
struct Student s2 = {"小明2",18,77};
struct Student s3 = {"小明3",19,98};
struct Student s4 = {"小明4",16,67};
struct Student s5 = {"小明5",17,100};
  • 其实可以用结构体数组来管理

    声明:
    struct 结构体类型名称 数组名称[数组长度];

    1. 表示我们声明了一个长度为5的结构体数组
    2. 数组名叫 students
    3. 数组的元素的类型的 struct Student
      struct Student students[5];
      students[0] = s1;
      students[1] = s2;
      students[2] = s3;
      students[3] = s4;
      students[4] = s5;
  • 遍历

    一个 for 循环就可以搞定了
    for(int i = 0;i < 5;i++)
    {
    printf(“姓名:%s 年龄:%d 成绩:%d\n”,
    students[i].name,
    students[i].age,
    students[i].score
    );

  • 结构体数组的初始化

    1. 刚才那种用下标一个个初始化的方式仍然是很傻的方式.(以下的方法可以认为是给部分指定的元素初始化)

    struct Student students[5];
    students[0] = (struct Student){“小明1”,16,56};
    students[1] = (struct Student){“小明2”,18,100};
    students[2] = (struct Student){“小明3”,19,10};
    students[3] = (struct Student){“小明4”,21,100};
    students[4] = (struct Student){“小明5”,13,3};
    注意: 当我们为结构体数组的元素赋值的时候.如果直接使用大括弧来初始化.
    就必须要前面加1个小括弧,来告诉编译器我们的给的数据类型.

    1. 在声明结构体数组的同时就初始化(按顺序初始化)

    //5 可以省略
    struct Student students[5] =
    {
    {“小明1”,16,56},
    {“小明2”,18,100},
    {“小明3”,19,10},
    {“小明4”,21,100},
    {“小明5”,13,3}
    };

  • 计算长度

    怎么计算总长度?
    int len = sizeof(students)
    len == 80
    在计算每个元素的字节数,用总长度一除
    int len = sizeof(students)/sizeof(struct Student);
    //students是数组名,student 是结构体类型名称

结构体指针
struct student
{
    char* name;
    int age;
    int score;
};
struct Student xiaoMing = {"小明",18,100};
xiaoMing 是一个变量.类型是 sturct Student 类型的
既然是变量就有地址,有地址就有指针.

格式

struct 结构体类型名称* 指针名;
struct Student* pStu; 
//声明了一个能指向 struct Student 类型的指针变量.

初始化

取出结构体变量的地址
&xiaoMing;

赋值给指针变量
pStu = &xiaoMing;
struct Student* pStu = &xiaoMing;

操作

(*结构体指针名).成员
(*pStu).name = "jack";
(*pStu).age = 18;
(*pStu).score = 99;

第二种方式
pStu->name = "jack";
pStu->age = 18;
代表把18 赋给 pStu 指向的 age 成员.
结构体的嵌套
当我们在为结构体定义成员的时候. 发现某个成员也是1个大数据 需要其他的几个小变量合起来描述,那么这个时候你就可以再定义1个数据类型.来表示这个类型.
struct Date
{
    int year;
    int month;
    int day;
};
//在 Person 结构体中加入一个 Date 结构体保存出生年月日.也可以加入农历出生年月日.
struct Person
{
    char* name;
    int age;
    double money;
    struct Date birthday;
};

struct Person xiaoMing = {"曾凡怡",22,100000000.0,{1994,2,23}};
printf("姓名:%s--年龄%d--财产:%lf--生日:%d--%d--%d",
        xiaoMing.name,
        xiaoMing.age,
        xiaoMing.money,
        xiaoMing.birthday.year,
        xiaoMing.birthday.month,
        xiaoMing.birthday.day
        );
结构体作为函数的参数
  1. 结构图是一种数据类型,当然也可以作为函数的参数和返回值.
  2. 结构体作为参数传值是值传递

    struct Student
    {
    char *name;
    int age;
    int score;
    };
    void panDuanXueSheng(struct Student stu)
    {
    if(stu,score >= 60)
    {
    printf(“恭喜%s 你及格了.\n”,stu.name);
    }
    else
    {
    printf(“抱歉%s 你落榜了”,stu.name);
    }
    }
    这个函数接收的是值,函数内部如果想更改实参变量的值是不行的.

  3. 如果你就是希望函数的内部可以修改实参的值,就使用指针

    void jiaFen(struct Student* stu)
    {
    stu->score = 100;
    }

  4. 结构体作为返回值

    struct getAStudent()
    {
    struct Student s1 = {“rose”,21,100};
    return s1;
    }

  5. 返回一个结构体指针

    include

枚举

限定一个变量中只能给指定的几个值,除此之外都不行.C 语言默认没有提供限定取值类型的变量,我们就自己定义一个,枚举就支持我们新创建一个这种类型.
enum 新类型的名称
{
    限定取值1,限定取值2,限定取值3,......
};
enum Direction
{
    East,
    South,
    West,
    North
};
1. 表示新创建了一个数据类型,名字叫做 enum Direction.
2. 可以声明这个类型的变量.
3. 这个变量中就只能存储这其中指定的任意一个.
  • 声明

    enum 枚举类型名称 变量名;
    enum Direction dir;

    1. 表示声明了一个变量,名字叫 dir
    2. 类型是 enum Direction.
    3. 只能存储这个枚举类型限定的取值之一.

    enum Direction dir = East;

  • 注意

    1. 枚举的作用域在定义的函数内部使用,希望所有的都是用就定义在最顶部.
    2. 每一个 枚举值/枚举项 都有一个对应的整形的数,默认从0开始,依次递增.%d 打印就看得到

    这个枚举有多大?
    int len = sizeof(dir); 无论是什么类型的枚举变量都是4个字节.
    枚举变量当中(dir),真正存的就是对应的整形得数.

    printf(“%d\n”,dir);

    1. 所以也可以给枚举变量赋值一个整形的数据…
    2. 虽然我们可以给整数,但我们并不会这么做.代码的可读性会很差.
    3. 给1和 South 完全等价
    4. 手动指定对应的数.

    enum Direction
    {
    East = 10,
    South = 20,
    West = 30,
    North = 40
    };
    East 对应的整形的数就是10.

  • 一些规范

    1. 枚举类型的命名规范:

    首字母大写.每个单词的首字母也大写

  • 枚举值的命名规范:
    以枚举的类型开头.这样你敲出前面几个字符就可以出现提示,枚举中有哪些值枚举.
typedef

作用:

  1. 为一个已经存在的数据类型取一个别名.
  2. 如果我们想要使用这个类型,就直接使用别名.
    语法格式:
typedef 已经存在的数据类型 别名;
typedef int itheima;
为 int 数据类型取了一个别名,叫做 itheima
int == itheima;//这两个名字现在就完全等价了.
typedef char* string;
string name = "rose";
//一秒钟让你的 C 语言有 string 类型

我们前面了解的 size_t 其实就是 typedef 定义的 unsigned long
3. 什么时候用?
当数据类型很长的时候 unsigned long long int 这种,定义以后用起来就很方便了.
4. 还有呢?
struct Student 好长好难写. typedef 他,写在外面最顶上. typedef struct Student Student;
5. 先声明再重命名,也可以声明的同时就重命名.

typedef struct Student//这个 Student 是结构体名称
{
    char* name;
    int age;
    int score;
}Student;//这个 Student 是重命名后的数据类型名称.

6. 声明匿名结构体的同时就为结构体取一个短别名 *

上面的第一个 Student 删掉,就是一个匿名结构体,后面一个数据类型的 Student 就成了这个匿名结构体的短名.***
7. typedef 为枚举取一个短别名

enum Direction
{
    DirectionEast
};
type enum Direction Direction;

声明同时改名
type enum Direction 
{
    DirectionEast
}Direction;
//其实....使用上面这种方式和匿名结构体一样,第一个 Direction 也不用写了.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值