• C语言3——结构体、联合体、枚举、宏定义


    1、初始化

    方式一

    #include <stdio.h>
    #include <string.h>
    #pragma warning(disable:4996)
    struct student{
        char name[100];
        int age;
        int sex;
    };//说明了一个结构体的数据成员类型
    int main(){
        struct student st;//定义一个结构体变量st
        st.age=20;
        st.sex=0;
        strcpy(st.name,"myj");
        printf();
        return 0;
    }

    方式二

    //定义结构体的时候就可以初始化
    struct student st={"myj",24,0};
    struct student st={0};//将所有成员值初始化为0,字符串将会初始化为空串

    方式三

    scanf("%s",st.name);
    scanf("%d",&st.age);
    scanf("%d",&st.sex);

    方式四

    struct student st={.age=20,.name="myj3",.sex=0};
    struct student st={.name="myj3",.sex=0};//没有给age初始化,默认为0

    方式五

    struct student st;
    memset(&st,0,sizeof(st));

    2、结构体内存对齐模式

    struct A{
        int a;
        char b;
    };
    int main(){
        struct A s;
        printf("%d",sizeof(s));//8 计算长度的时候会以所有成员里面最长的那个为基准
    }
    struct A{
        int a;
        char b;
        char c;
        char d;
        char e;
    };
    int main(){
        struct A s;
        printf("%d",sizeof(s));//还是8
    }
    struct A{
        int a;
        char b;
        char c;
        char d;
        char e;
        char f;
    };
    int main(){
        struct A s;
        printf("%d",sizeof(s));//12
    }
    struct A{
        char a[10];
        char b;
        char c;
        char d;
        char e;
        char f;
        char g;
    };
    int main(){
        struct A s;
        printf("%d",sizeof(s));//16 只会把数组中的一个元素当成一个成员
    }
    struct A{
        char int[10];
        char b;
        char c;
        char d;
        char e;
        char f;
        char g;
    };
    int main(){
        struct A s;
        printf("%d",sizeof(s));//48 只会把数组中的一个元素当成一个成员
    }
    struct A{
        char c;
        int a;
        char b;
    }
    int main(){
        struct A s;
        printf("%d",sizeof(s));//12 由于第一个成员是char类型 char会扩展为和第2个int成员相同的空间
    }
    struct A{
        char a;
        short b;
        int c;
        short d;
        char e;
    }
    int main(){
        struct A a;
        a.a=1;
        a.b=2;
        a.c=3;
        a.d=4;
        a.e=5;
    }
    //实例化之后的结构体a在内存中结构为
    //01 xx 00 02
    //00 00 00 03
    //00 04 05 xx
    //其中xx表示没有被利用的空间 所以sizeof(a)是12
    //结构体变量在占内存时总是以2的倍数为开始索引来占据的,再例如:
    struct A{
        char a;
        short b;
        int c;
        char f;
        short d;
        char e;
    }
    int main(){
        struct A a;
        a.a=1;
        a.b=2;
        a.c=3;
        a.d=4;
        a.e=5;
        a.f=6;
    }
    //结构体变量a在内存中的结构为
    //01 xx 00 02
    //00 00 00 03
    //06 xx xx xx
    //00 04 05 xx
    //------------------
    //所以sizeof(a)是16
    struct A{
        char a;
        short b;
        int c;
        short d;
        char e;
        char f;
    }
    int main(){
        struct A a;
        a.a=1;
        a.b=2;
        a.c=3;
        a.d=4;
        a.e=5;
        a.f=6;
    }
    -----------------------
    //01 xx 00 02
    //00 00 00 03
    //00 04 05 06
    //sizeof(a)是12

    指定结构体成员的位字段

    struct D{
        char a:2;//定义一个成员类型为char,但这个成员只是用2个bit
    }
    int main(){
        struct D d;
        d.a=3;
        printf("%d
    ",d.a);//-1
        printf("%d
    ",sizeof(d));//1
        return 0;
    }
    struct D{
        char a:2;
        char b:2;
    }
    int main(){
        struct D d;
        printf("%d
    ",sizeof(d));//还是1,也就是说我们可以通过字节下的比字节更小的单位bit来控制
        return 0;
    }
    struct D{
        char a:2;
        unsigned char b:4;
    }
    int main(){
        struct D d;
        d.a=3;
        d.b=16;
        printf("%d %d
    ",d.a,d.b);//-1 0
        printf("%d
    ",sizeof(d));//
        return 0;
    }
    //在嵌入式的场合通常使用:
    struct D{
        char a:1;//只有两种状态,开或者关
    }
    //一个LED灯的话用这样的八个成员就可以了(不要忘了小数点)
    struct F{
        int i:2;
        char c:2;
    }
    int main(){
        printf("%d
    ",sizeof(struct F));//8
    }
    struct G{
        char a;
        char b;
        char c;
    }
    int main(){
        struct G g;
        char *p=&g;
        p[0]=3;
        p[1]=4;
        p[2]=0;
        printf("%d %d %d",g.a,g.b,g.c);//3 4 0
    }
    struct H{
        char a;
        int b;
    }
    int main(){
        struct H h={1,2};
        char *p1=&h;
        p1++;
        *p1=4;
        p1++;
        *p1=5;
        printf("%p
    ",&h);//01 04 05 xx 02 00 00 00 从结果中可以看出我们已经把第一行char类型浪费的字节利用起来了
        return 0;
    }

    3、结构体数组

    struct student{
        char name[16];
        unsigned char age;
        unsigned char score;
        char classes[100];
    };
    int main(){
        struct student st[3]={{"aaa",20,50,"c"},{"bbb",21,60,"c++"},{"ccc",22,70,"java"}};
        int i;
        for(i=0;i<5;i++){
            printf("姓名=%s 年龄=%d 成绩=%d 班级=%s
    ",st[i].name,st[i].age,st[i].score,st[i].classes);
        }
        return 0;
    }
    struct A{
        char array[10];
    }
    int main(){
        struct A a1={"hello"};//如果结构体的成员是数组,通过结构可以变相的实现数组的赋值,而数组之间是不可以直接赋值的
        struct A a2={0};
        a2=a1;//这样写是合法的
        printf("%s",a2.array);
    }
    void swap(struct student *a,struct student *b){
        struct student tmp=*a;
        *a=*b;
        *b=tmp;
    }
    int main(){
        int i;
        int j;
        for(i=0;i<5;i++){
            for(j=1;j<5-i;j++){
                if(st[j].age<st[j-1].age){
                    swap(&st[j],&st[j-1]);
                }else if(st[j].age==st[j-1].age){
                    //如果年龄一样就比较分数
                    if(st[j].score<st[j-1].score){
                        swap(&st[j],&st[j-1]);
                    }
                }
            }
        }
    }
    //如果通过班级来排序,即给字符串排序
    int main(){
        int i;
        int j;
        for(i=0;i<5;i++){
            for(j=1;j<5-i;j++){
                if(strcmp(st[j].classes,st[j-1].classes)>0){
                    swap(&st[j],&st[j-1]);
                }
            }
        }
        return 0;
    }

    4、结构体嵌套

    struct A{
        int a;
        char b;
    };
    struct B{
        struct A a;
        char b;
    }
    int main(){
        printf("%d
    ",sizeof(struct B));//12
        return 0;
    }
    struct A{
        int a;
        int b;
    };
    struct B{
        int c;
        struct A a;
        int b;
    };
    struct C{
        struct B b;//仍然以int对齐
        char d;
    };
    int main(){
        printf("%d",sizeof(struct C));//20
    }
    struct A1{
        char a;
    }
    struct A2{
        struct A1 a;
        char b;
    }
    struct A3{
        struct A2 a;
        char b;
    }
    int main(){
        struct A3 a3;
        //a3.a.a.a=100; 栈中的结构体通过点访问
        struct A3 *ap=malloc(sizeof(struct A3));
        ap->a.a.a=20;//堆中的结构体通过->访问
    }

    5、结构体拷贝

    struct A a1,a2;
    a1.a=1;
    a1.b=2;
    a2=a1;//结构体之间内存的拷贝 相当于memcpy(&a2,&a1,sizeof(a1));

    6、指向结构体的指针

    struct A a;
    struct A *p=&a;
    p->a=10;//相当于(*p).a=10;
    p->b=20;//相当于(*p).b=10;

    7、堆内存结构体

    int main(){
        struct A *p=malloc(sizeof(struct A)*10);
        memset(p,0,sizeof(struct A)*10);
        struct A *array=p;
        p->a=1;
        p->b=2;
        p++;
        p->a=3;
        p->b=4;
        int i;
        for(i=0;i<10;i++){
            printf("%d %d",array[i].a,array[i].b);
        }
        free(array);//不能free掉p,因为p已经赋给array了,free知道要去释放10个字节的内存,如果要去free p的话,从第2个字节的内存开始free,free到第10个字节的内存时还没有free完,就再去free没有通过malloc分配的内存,这样一定会出问题
        return 0;
    }

    8、结构体中的数组成员和指针成员

    struct student{
        char name[100];
        int age;
    }
    struct student_a{
        char *name;
        int age;
    }
    int main(){
        struct student_a st={NULL,0};
        st.age=30;
        st.name=malloc(100);
        strcpy(st.name,"myj");
        printf("%d %s",st.age,st.name);
        free(st.name);
    }
    struct student{
        char name[100];
        int age;
    }
    struct student_a{
        char *name;
        int age;
    }
    int main(){
        struct student_a st={NULL,0};
        st.age=30;
        st.name=malloc(100);
        strcpy(st.name,"myj");
        struct student_a st1=st;
        printf("%d %s",st1.age,st1.name);//30 乱码
        free(st.name);
    }
    struct man{
        char *name;
        int age;
    }
    int main(){
        struct man m={"tom",20};
        printf("name=%s age=%d
    ",m.name,m.age);//看起来结果正常
    }
    //但是假如用strcpy改一下name:
    int main(){
        struct man m={"tom",20};
        strcpy(m.name,"mike");//程序会挂掉
        printf("name=%s age=%d
    ",m.name,m.age);
    }
    //如果结构体中name的定义改成了char name[100] 就没问题
    //name是指针类型时,struct man m={"tom",20}; 执行完了之后m.name就指向了常量"tom"的地址
    //strcpy(m.name,"mike");这种操作是去修改一个常量,而常量是不可以修改的,所以会挂掉
    //结构体中的成员是指针类型的时候通常通过以下方式赋值
    struct man m;
    m.name=malloc(sizeof(char)*100);//在堆中给name分配100字节
    strcpy(m.name,"mike");
    free(m.name);//要记得释放
    struct student{
        char name[100];
        int age;
    }
    int main(){
        //struct student st; 在栈里面
        struct student *p=malloc(sizeof(struct student)); //在堆里面
        free(p);
        return 0;
    }
    struct man{
        char *name;
        int age;
    };
    int main(){
        struct man *p=malloc(sizeof(struct man));//结构体变量p在堆中
        p->name=malloc(sizeof(char)*100);//结构体变量p的成员name也在堆中
        strcpy(name,"tom");
        p->age=30;
        printf("name=%s age=%d
    ",p->name,p->age);
        free(p->name);//注意释放的顺序一定得是先释放p的成员name,再释放整个结构体
        free(p);
    }

    9、将结构体作为函数参数

    struct student{
        char name[100];
        int age;
    };
    void print_student(struct student s){//一般不把结构类型变量作为函数参数,因为结构类型可能很大,函数调用式,实参和形参在栈内存中同时存在,就会极大浪费栈内存
        printf("name=%s age=%d",s.name,s.age);
    }
    void set_student(struct student s,const char *name,int age){
        strcpy(s.name,name);
        s.age=age;
    }
    int main(){
        struct student st={"tom",20};
        set_student(st,"mike",100);
        print_student(st);//打印出来的是tom 就是普通类型的变量
    }
    //想要改的话只能传递地址:
    struct student{
        char name[100];
        int age;
    };
    void print_student(struct student s){
        printf("name=%s age=%d",s.name,s.age);
    }
    void set_student(struct student *s,const char *name,int age){
        strcpy(s->name,name);
        s->age=age;
    }
    int main(){
        struct student st={"tom",20};
        set_student(&st,"mike",100);
        print_student(st);//打印出来的是mike
    }

    10、联合体

    union A{
        int a;
        int b;
    };
    int main(){
        union A a;
        a.a=10;
        a.b=20;
        printf("%d",sizeof(union A));//4
        printf("%p %p",&a.a,&a.b);//内存一样
        printf("a=%d",a.a);//20 因为共用一块内存
    }

    联合体union是一个能在同一个存储空间存储不同类型数据的类型
    联合体所占的内存长度等于其最长成员的长度,也有叫做共用体
    联合体虽然可以有多个成员,但同一时间只能存放其中一种
    在上面的例子中联合体内成员a和b是共用一块地址的

    11、联合体长度

    union A{
        unsigned char a;
        char b;
    };
    int main(){
        union A a;
        a.a=128;
        //a.b=20;
        printf("%d",sizeof(union A));//1 以联合体中长度最大的那个成员为准
        printf("%p %p",&a.a,&a.b);
        printf("a=%d",a.a);//128
        printf("b=%d",a.b);//-128
    }

    12、联合体中的指针成员

    union A{
        char a;
        char *b;
    };
    int main(){
        union A a;
        a.b=malloc(100);//b指向了一个堆的地址
        a.a=10;//但是给a赋值之后b的值成了10,所以下面就free不了了
        free(a.b);
        return 0;
    }

    如果联合体中有指针成员,那么一定要使用完这个指针,并且free指针之后才能使用其他成员

    union A{
        char a;
        char *b;
    };
    int main(){
        union A a;
        a.b=malloc(100);//b指向了一个堆的地址
        free(a.b);
        a.a=10;//但是给a赋值之后b的值成了10,所以下面就free不了了
        return 0;
    }

    13、枚举类型

    enum A{
        red,green,black,yellow //相当于定义了4个常量,都是int类型的 值分别为0 1 2 相当于用#define定义的常量
    };
    int main(){
        int color=black;//但是这里不可以给black赋值 枚举是整数常量
        printf("%d",red);//0
        printf("%d",green);//1
        printf("%d",black);//2
        printf("%d",yellow);//3
    
        printf("%d",color);//2
    }
    enum A{
        red=5,black,yellow,green
    };
    //现在black变成了6 yellow是7 ...
    enum A{
        red=5,black=2,yellow,green
    };
    //yellow是3

    14、typeof

    typedef是一种高级数据特性,它能使某一类型创建自己的名字
    typedef char BYTE;//定义了新的数据类型,名字叫BYTE,类型是char

    #define BYTE1 char//define只是做了一个语法替换,但是typedef就是定义了一种类型,并不单单是替换
    int main(){
        BYTE a;
        BYTE1 a1;
        a=10;
        printf("%d",a);
        return 0;
    }

    #define MAX 10不可以替换成typedef 10 AAA;

    struct abc{
        int a;
        char b;
    };
    typedef struct abc A;
    int main(){
        A a;//就不用一直写struct关键字了
    }
    //================上面的typedef定义的类型还有一种简化写法,而这种写法是define做不到的
    typedef struct{
        int a;
    } A2;
    char *mystrcat(char *s1,char *s2){
        strcat(s1,s2);
        return s1;
    }
    char *test( char *(*p)(char *,char *),char *s1,char *s2 ){
        return p(s1,s2);
    }
    int main(){
        char s1[100]="hello";
        char s2[100]="world";
        char *s=test(mystrcat,s1,s2);
        printf("%s",s);//helloworld
        return 0;
    }

    上面的代码中,test函数的第一个参数是函数指针 但是这个函数的定义太复杂 所以我们一般避免这样写

    char *mystrcat(char *s1,char *s2){
        strcat(s1,s2);
        return s1;
    }
    typedef char *(*STRCAT)(char *,char *)//STRCAT可以指向两个参数是char*类型,返回值也是char*类型的函数
    char *test( STRCAT p, char *s1, char *s2 ){
        return p(s1,s2);
    }
    int main(){
        STRCAT array[10];//定义了一个每个成员的类型都是STRCAT的数组,该数组长度是10
        //如果用原始的方法定义的话可读性极差:char *(*p[10])(char *s1,char *s2); 而且在这里typedef是无法用#define替换的
    
        char s1[100]="hello";
        char s2[100]="world";
        char *s=test(mystrcat,s1,s2);
        printf("%s",s);//helloworld
        return 0;
    }
  • 相关阅读:
    Java NIO学习笔记---I/O与NIO概述
    JAVA多线程和并发基础面试问答
    Java线程面试题 Top 50
    Java并发编程:Timer和TimerTask
    Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
    Java并发编程:线程池的使用
    Java并发编程:阻塞队列
    程序猿写客户端,没有视觉设计师,怎么搞到图?
    hm nsis edit请求的操作需要提升
    MFC修改系统托盘的图标
  • 原文地址:https://www.cnblogs.com/zhaohuiziwo901/p/5093151.html
Copyright © 2020-2023  润新知