九、复合类型(自定义类型):结构体、共用体(联合体)、枚举、typedef

结构体

复制代码
//结构体的定义与使用
//结构体一般卸载.h文件中,不卸载.c文件内
struct student {
    int age;
    char name[20];
    char grade[20];
};
struct student1 {
    int age;
    char name[20];
    char grade[20];
}std1, std2, std3;        //在定义结构体时,可以声明结构体变量
struct student2 {
    int age;
    char name[20];
    char grade[20];
}std22 = { .name = "allen3",.age = 20, };//在定义结构体时,可以初始化结构体变量
int main()
{
    //结构体使用
    struct student s1, s2;
    //###初始化方式1
    s1.age = 18; strcpy(s1.name, "allen"); strcpy(s1.grade, "3年1班");        //我说这里为什么要用strcpy()函数,因为定义name为数组,而"allen"是字符串数组。不能用赋值语句将一个字符数组直接给一个字符数组赋值
    //###初始化方式2
    (&s2)->age = 19; strcpy((&s2)->name, "allen2"); strcpy((&s2)->grade, "3年2班");        //这个不就是指针的方式嘛。。。
    //###初始化方式3
    struct student  s3 = { .name = "allen3",.age = 20, };    //可以不全部赋值,可以不按顺序
    //###初始化方式4
    struct student  s4 = { 21,"allen4","3年4班" };

    printf("%d\t%s\t%s\n", (&s1)->age, (&s1)->name, (&s1)->grade);    //18      allen   3年1班
    printf("%d\t%s\t%s\n", s2.age, s2.name, s2.grade);                //19      allen2  3年2班
    printf("%d\t%s\t%s\n", s3.age, s3.name, s3.grade);                //20      allen3
    printf("%d\t%s\t%s\n", s4.age, s4.name, s4.grade);                //21      allen4  3年4班
}
结构体的定义与使用
复制代码
复制代码
我的结构体 "."用法、"->"用法 总结:
    其实就是看结构体变量的变量类型
        若结构体变量是普通变量,那么直接用"."进行操作;or 将普通变量用"&"转为地址后,使用"->"进行操作
        若结构体变量是指针变量,那么直接用"->"进行操作;or 将指针变量用"*"转为普通变量后,用"."进行操作
    再看结构体成员的变量类型
        都可以用strcpy或"="方式进行赋值,区别在于赋值两边的类型要相同
            若结构体成员为普通变量,那么右边也是普通变量
            若结构体成员为指针变量,那么右边也是指针变量or地址

若结构体成员为指针变量,赋值过程:
    struct Device* DD = malloc(sizeof(struct Device*)); 
    DD->name = malloc(sizeof(char) * 20); 
    (*DD).name = malloc(sizeof(char) * 20); 
    sprintf(DD->name, "str_names");            //向DD->name地址空间存放内容
    sprintf(( * DD).name, "str_names");            //向DD->name地址空间存放内容


//device001->name 这种用法在不同的语境中,功能看起来是不一样的嘛。。。
//1.获取对应的内容            printf("device设备名1:%s\n",device001->name);      
//2.这个才是往内存空间中储存内容        DD->name = malloc(sizeof(char)*20);
//3.这个是将"DD->name对应的地址"进行解引用,然后再往这个内存空间储存内容            sprintf(DD->name, names);    
------------------------------------------------------------------------------------------------
结构体变量是普通变量+结构体成员是普通变量:
    使用"."进行操作:
        p.name, p.age
    使用"->"进行操作:
        strcpy((&p)->name, "test");
        (&p)->age = 22;
结构体变量是指针变量+结构体成员是普通变量:
    需要通过"->"进行操作:
        strcpy(p->name, "test");
        p->age = 22;
    使用"."进行操作:
        (*p).name
        (*p).age
我的结构体 "."用法、"->"用法 总结
复制代码

 

复制代码
//结构体内存对齐规则
//看起来是根据占用内存最长字节的类型进行调整,例如此处最大类型为int,占用4字节;
struct student1 {
    char name;    //浪费3字节
    int age;
    char grade[15];    //浪费1字节
}std1;
struct student2 {
    int age;            //定义的内容一样,只是内内容进行了优化排序
    char name;
    char grade[15];
}std2;
int main()
{
    printf("%d\n", sizeof(std1));    //24字节
    printf("%d\n", sizeof(std2));    //20字节

}
结构体内存对齐规则
复制代码

 

复制代码
//结构体数组使用
struct student {
    int age;
    char name[20];
    char grade[20];
};
int main()
{
    //###初始化方式1
    struct student boys[2] = {
        {18,"allen3","4年1班"},
        {19,"allen4","4年2班"}
    };
    printf("%d\t%s\t%s\n", (&boys[0])->age, (&boys[0])->name, (&boys[0])->grade);    //18      allen3  4年1班
    printf("%d\t%s\t%s\n", boys[1].age, boys[1].name, boys[1].grade);                //19      allen4  4年2班

    struct student girls[2];
    //girls[0] = { 18,"lili","4年1班" } ;    //这种方式不行?
    //###初始化方式2
    girls[0].age = 18, strcpy(girls[0].name, "lili"), strcpy(girls[0].grade, "5年1班");
    girls[1].age = 19, strcpy(girls[1].name, "lili2"), strcpy(girls[1].grade, "5年2班");
    printf("%d\t%s\t%s\n", (&girls[0])->age, (&girls[0])->name, (&girls[0])->grade);    //18      lili    5年1班
    printf("%d\t%s\t%s\n", girls[1].age, girls[1].name, girls[1].grade);                //19      lili2   5年2班
}
结构体数组使用
复制代码

 

复制代码
//结构体数组排序
struct student {
    char name[20];
    int scroes[3];
};
int main()
{
    struct student std[6]={
        {"allen1",89,100,35},
        {"allen2",67,78,56},
        {"allen3",99,33,55},
        {"allen4",56,34,98},
        {"allen5",23,55,99},
        {"allen6",66,99,54},
    };
    for (size_t i = 0; i < 6; i++)
    { printf("%s %d %d %d %d\n", std[i].name, std[i].scroes[0], std[i].scroes[1], std[i].scroes[2], std[i].scroes[0]+ std[i].scroes[1] + std[i].scroes[2]); }
    printf("================================================================\n");
    //冒泡排序
    for (size_t i = 0,len = 5; i < 5; i++,len--)
    {
        for (size_t j = 0; j < len; j++)
        {
            if (std[j].scroes[0] + std[j].scroes[1] + std[j].scroes[2] < std[j+1].scroes[0] + std[j+1].scroes[1] + std[j+1].scroes[2])
            {
                char tmp[20]; strcpy(tmp, std[j].name); int scroe0 = std[j].scroes[0]; int scroe1 = std[j].scroes[1]; int scroe2 = std[j].scroes[2];
                strcpy(std[j].name, std[j + 1].name); std[j].scroes[0] = std[j + 1].scroes[0]; std[j].scroes[1] = std[j + 1].scroes[1]; std[j].scroes[2] = std[j + 1].scroes[2];
                strcpy(std[j+1].name, tmp); std[j + 1].scroes[0] = scroe0; std[j + 1].scroes[1] = scroe1; std[j + 1].scroes[2] = scroe2;
            }
        }
    }
    for (size_t i = 0; i < 6; i++)
    {printf("%s %d %d %d %d\n", std[i].name, std[i].scroes[0], std[i].scroes[1], std[i].scroes[2], std[i].scroes[0] + std[i].scroes[1] + std[i].scroes[2]);}
    printf("================================================================\n");
    //排序优化,结构体变量交换
    for (size_t i = 0, len = 5; i < 5; i++, len--)
    {
        for (size_t j = 0; j < len; j++)
        {
            if (std[j].scroes[0]  < std[j + 1].scroes[0])
            {
                struct student tmp;
                tmp = std[j];
                std[j] = std[j + 1];
                std[j + 1] = tmp;
            }
        }
    }
    for (size_t i = 0; i < 6; i++)
    {
        printf("%s %d %d %d %d\n", std[i].name, std[i].scroes[0], std[i].scroes[1], std[i].scroes[2], std[i].scroes[0] + std[i].scroes[1] + std[i].scroes[2]);
    }
}
结构体数组排序
复制代码

 

复制代码
结构体成员为指针;堆空间结构体【常用】
==========================================================================================
//结构体成员为指针
//过渡状态,不常用
struct student {
    char *name;
    int age;
};
int main()
{
    struct student s[5] = {
        {(char*)malloc(sizeof(char) * 20),33},
        {(char*)malloc(sizeof(char) * 20),77},
        {(char*)malloc(sizeof(char) * 20),99},
        {(char*)malloc(sizeof(char) * 20),56},
        {(char*)malloc(sizeof(char) * 20),21},
    };
    strcpy(s[0].name, "allen0");
    strcpy((&s[1])->name, "allen1");
    strcpy(s[2].name, "allen2");
    strcpy(s[3].name, "allen3");
    strcpy(s[4].name, "allen4"); 
    for (size_t i = 0; i < 5; i++)
    {
        printf("%s    %d\n", s[i].name, s[i].age);        //打印数组,给地址就可以了。。。。
    }
    free(s[0].name);
    free(( &s[1])->name);
    free(s[2].name);
    free(s[3].name);
    free(s[4].name);
}
---------------------------------------------------------------------------------------
//堆空间结构体【常用】
struct student {
    char *name;
    int *age;
};
int main()
{
    struct student* s = (struct student*)malloc(sizeof(struct student) * 5);        //堆空间申请了5个struct结构体的指针空间
    printf("%d\n", sizeof(struct student));            //16    定义的student结构体占用16字节
    printf("%d\n", sizeof(s));                        //8        64位系统,一个指针占用8字节
    for (size_t i = 0; i < 5; i++)
    {
        (s + i)->name = (char*)malloc(sizeof(char) * 20);
        (s + i)->age = (int *)malloc(sizeof(int));
    }
    strcpy((s + 0)->name, "allen0"); *((s + 0)->age) = 11;
    strcpy((s + 1)->name, "allen1"); *((s + 1)->age) = 22;
    strcpy((s + 2)->name, "allen2"); *((s + 2)->age) = 33;
    strcpy((s + 3)->name, "allen3"); *((s + 3)->age) = 44;
    strcpy((s + 4)->name, "allen4"); *((s + 4)->age) = 55;
    
    for (size_t i = 0; i < 5; i++)
    {
        printf("%s %d\n", (s + i)->name , *((s + i)->age));
    }
    for (size_t i = 0; i < 5; i++)
    {
        free((s + i)->name);
        free((s + i)->age);
    }
    free(s);
}
结构体成员为指针;堆空间结构体【常用】
复制代码

 

复制代码
//结构体和函数
struct student {
    char name[20];
    int age;
};
void func1(struct student s) {
    strcpy(s.name, "李四");
    s.age = 20;
}
void func2(struct student *s) {
    strcpy(s->name, "李四");
    s->age = 20;
}
struct student func3() {
    struct student s3 = { "王五",18 };
    return s3;
}
struct student *func4() {
    struct student s4 = { "王七",20 };
    return &s4;
}
int main()
{
    struct student s1 = { "张三",18 };
    printf("%s %d\n", s1.name, s1.age);        //张三 18
    func1(s1);
    printf("%s %d\n", s1.name, s1.age);        //张三 18  结构体传值形式,函数不会改变原变量内容
    func2(&s1);
    printf("%s %d\n", s1.name, s1.age);        //李四 20  传地址,完成了变量内容的修改

    struct student s3 = func3();            //别搞混了啊。。。函数的返回值被s3接收了,那么就相当于s3储存了返回值而已,并不是函数栈空间的值还保留着
    printf("%s %d\n", s3.name, s3.age);        //王五 18
    struct student *s4 = func4();
    printf("%s %d\n", s4->name, s4->age);    //烫烫烫烫烫烫烫烫8鮷橰 20    这里乱码是正常的,因为函数执行结束,数据被销毁了。只是为什么20是显示正常的?
}
结构体和函数
复制代码

 

复制代码
//结构体嵌套结构体
//这个好像可以搞对象了嘛。。
struct hands_foots {            //该结构体保存了四肢详情
    int hands;
    int foots;
};

struct food {                    //该结构体保存了食物及分量
    char food[20];
    int weight;
};
struct animal {
    char name[20];
    struct hands_foots hf;
    struct food fd;
};

int main()
{
    struct animal xingxing;
    strcpy(xingxing.name, "猩猩");
    xingxing.hf.hands = 2; xingxing.hf.foots = 2;
    strcpy(xingxing.fd.food, "香蕉"); xingxing.fd.weight = 3;

    printf("动物名称:%s;手:%d;脚:%d;食物:%s;食物分量:%d斤", xingxing.name, xingxing.hf.hands, xingxing.hf.foots, xingxing.fd.food, xingxing.fd.weight = 3);
    //动物名称:猩猩;手:2;脚:2;食物:香蕉;食物分量:3斤
}
结构体嵌套结构体
复制代码

 

共用体(联合体)

 

复制代码
联合union是一个能在同一个存储空间存储不同类型数据的类型;
联合体所占的内存长度等于其最长成员的长度倍数,也有叫做共用体;
同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;
共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;
共用体变量的地址和它的各成员的地址都是同一地址。


//共用体(联合体)
//使用比较少,因为会显得数据比较乱;优点是节省内存
union MyUnion
{
    int a;
    float b;
    char c;
    double d;
    char arr[22];
};
int main()
{
    union MyUnion test;
    printf("%d\n", sizeof(union MyUnion));    //24字节    看来也受到内存对齐规则限制
    test.a = 100;
    printf("%d\n", test.a);        //100        //共用体只有最后一次赋值的数据才是准确的。
    test.b = 3.14;
    printf("%d\n", test.a);        //1078523331
    printf("%f\n", test.b);        //3.140000
    test.c = 'A';
    printf("%d\n", test.a);        //1078523201
    printf("%f\n", test.b);        //3.139969
    printf("%c\n", test.c);        //A            

    printf("%p\n", &test.a);    //000000E16D35FC58
    printf("%p\n", &test.b);    //000000E16D35FC58
    printf("%p\n", &test.c);    //000000E16D35FC58
};
共用体(联合体)
复制代码

 

枚举

 

复制代码
枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。
//枚举
enum colors
{
    red, bule, yellow, black, white, green
}clo1;
int main()
{
    clo1 = 1;
    switch (clo1)
    {
    case red:
        break;
    case bule:
        printf("当clo1 = 1时,执行这条语句");    //当clo1 = 1时,执行这条语句
        break;
    case yellow:
        break;
    case black:
        break;
    case white:
        break;
    case green:
        break;
    default:
        break;
    }
};
枚举
复制代码

 

typedef

复制代码
typedef为C语言的关键字,作用是为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。
    与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值
    #define发生在预处理,typedef发生在编译阶段


//typedef   就是起别名功能,不能定义新类型
//常用的就是给常用变量、结构体起别名,一般放在头文件.h文件中
typedef unsigned int UI;    
struct student_info_list {
    char name[20];
    char sex;
};
typedef struct student_info_list SIL;    //可以给结构体起别名
int main()
{
    unsigned int a = 10;
    typedef unsigned int UI;
    UI b = 20;
    SIL s1;                        //结构体起别名后,申请结构体变量就简单多了。。。
    printf("%d\n", a);
    printf("%d\n", b);
};
typedef
复制代码

 

打字游戏

复制代码
打字游戏
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <conio.h>
#include <time.h>


void tips()
{
    printf("==================打字游戏================\n");
    printf("================按任意键继续==============\n");
    printf("===============按Esc 退出游戏=============\n");
    char ch = _getch();
    if (ch == 27)
    {
        exit(0);
    }
}
void rand_ch(char* arr)
{
    srand((unsigned int)time(NULL));
    for (int i = 0; i < 50; i++)
    {
        arr[i] = rand() % 26 + 'a';
    }

}
void print_ch(char* arr)
{

    //变量  计时器  开始 结束   计数器  val
    unsigned int start_time;
    unsigned int end_time;
    int val = 0;
    for (int i = 0; i < 50; i++)
    {
        char ch = _getch();
        if (i == 0)
        {
            start_time = time(NULL);
        }
        if (ch == arr[i])
        {
            printf("%c", ch);
            val++;
        }
        else
        {
            printf("_");
        }
    }
    end_time = time(NULL);


    printf("\n用时:%d(秒)\n", end_time - start_time);
    printf("正确率:%.1f%%\n", val * 1.0 / 50 * 100);
    if (val * 1.0 / 50 * 100 > 80.0)
    {
        printf("打字小能手!\n");
    }

}
int main()
{
    //字库
    char arr[51];
    memset(arr, 0, 51);
    while (1)
    {
        //1、提示
        tips();
        //2、随机字符
        rand_ch(arr);
        printf("%s\n", arr);
        //3、输入字符
        print_ch(arr);
    }
    return EXIT_SUCCESS;
}
打字游戏
复制代码

 

posted @   雲淡風輕333  阅读(154)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示