其他分享
首页 > 其他分享> > C_结构体(精讲)

C_结构体(精讲)

作者:互联网

目录

知识点1【结构体的概述】(了解)

知识点2【结构体类型的定义】(重要)

1、先定义结构体类型 再用类型定义结构体变量(推荐)

2、定义结构体类型的同时 定义结构体变量

3、定义一次性结构体变量

 知识点3【结构体变量的定义】(重要)

知识点4【结构体变量的初始化】(重要)

知识点5【结构体变量的操作】(重要)

逐个成员赋值

结构体变量整体赋值

标准输入给结构体变量赋值

两个结构体变量,不能直接比大小

 知识点6【结构体指针变量】(重要)

知识点7【结构体数组】(重要)

1、遍历结构体数组

2、给结构体数组的元素获取键盘输入

知识点8【结构体成员指针】(重要)

1、结构体变量在栈区、全局区 成员指针 指向了文字常量区

2、结构体变量在栈区、全局区 成员指针 指向了堆区

3、结构体空间在堆区 成员指针 指向了堆区

4、结构体指针数组空间在堆区 结构体数组元素在堆区 成员指针 指向了堆区

知识点9【结构体嵌套结构体】(重要)

知识点10【结构体对齐问题】(重要)

1、知识点的引入

2、对齐规则(默认对齐)

第一步:确定分配单位(每行开辟多少字节)

第二步:确定成员的偏移位置。

第三步:收尾工作:

案例1:

案例2:

案例3:

3、结构体嵌套结构体

第一步:确定分配单位(每行开辟多少字节)

第二步:确定成员的偏移位置。

第三步:收尾工作:

案例1:

3、强制内存对齐

第一步:确定分配单位(每行开辟多少字节)

第二步:确定成员的偏移位置。

第三步:收尾工作:

知识点11【拓展求结构体成员的偏移量】(了解)

知识点12【位段(位域)】(重要)

在结构体中,以位为单位的成员,咱们称之为位段(位域)

 应用场景:

知识点12【共用体】(了解)

共用体和结构体 区别:

知识点13【枚举】(了解)


知识点1【结构体的概述】(了解)

基本类型:就是常见的char short int long float double

构造类型:由基本类型或其他构造类型 封装而成比如:数组、结构体、枚举、共用体

结构体:是一种或多种基本类型或构造类型的数据的集合。

知识点2【结构体类型的定义】(重要)

结构体类型的定义 必须加关键字"struct"修饰

1、先定义结构体类型 再用类型定义结构体变量(推荐)

结构体的类型 一般定义在 头文件中

定义结构体类型

struct 类型名
{
    成员定义;
};//一定要加;分号

 struct student 才能完整的表示结构体的类型

struct student
{
    int num;
    char name[32];
    int score;
};

struct student lucy;//lucy就是结构体变量名

结构体中的成员 拥有独立的空间

2、定义结构体类型的同时 定义结构体变量

struct student
{
    int num;
    char name[32];
    int score;
}lucy;//lucy是结构体变量名

struct student bob;

3、定义一次性结构体变量

struct
{
    int num;
    char name[32];
    int score;
}lucy;//lucy是结构体变量名

无法继续定义该类型的其他结构体变量

 知识点3【结构体变量的定义】(重要)

#include <stdio.h>

struct student{
    int num;        //这里是类型定义,不要赋值
    char name[16];
    float score;
};

void test01(){
    struct student lucy;    //没有初始化,内部数据为不确定值
    printf("num = %d\n", lucy.num);
    printf("name = %s\n", lucy.name);
    printf("score = %f\n", lucy.score);
}

int main(int argc, char const *argv[]){
    test01();
    return 0;
}

知识点4【结构体变量的初始化】(重要)

创建结构体变量并初始化为非零数据

#include <stdio.h>

struct student{
    int num;        //这里是类型定义,不要赋值
    char name[16];
    float score;
};

void test01(){
    struct student lucy = {100, "lucy", 88.8f};
    printf("num = %d\n", lucy.num);
    printf("name = %s\n", lucy.name);
    printf("score = %f\n", lucy.score);
}

int main(int argc, char const *argv[]){
    test01();
    return 0;
}

 创建结构体变量并初始化为零

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student{
    int num;        //这里是类型定义,不要赋值
    char name[16];
    float score;
};

void test01(){
    struct student lucy;
    memset(&lucy, 0, sizeof(lucy));
    printf("num = %d\n", lucy.num);
    printf("name = %s\n", lucy.name);
    printf("score = %f\n", lucy.score);
}

int main(int argc, char const *argv[]){
    test01();
    return 0;
}

知识点5【结构体变量的操作】(重要)

逐个成员赋值

void test02(){
    struct student lucy;
    lucy.num = 100;
    lucy.score = 88.8;
    strcpy(lucy.name, "lucy");

    printf("num = %d\n", lucy.num);
    printf("name = %s\n", lucy.name);
    printf("score = %f\n", lucy.score);
}

int main(int argc, char const *argv[]){
    test02();
    return 0;
}

结构体变量整体赋值

void test02()
{
    struct student lucy={100,"lucy", 88.8f};
    struct student bob;

    //将lucy的空间内容 赋值给bob
    #if 0
    //第一种方式:逐个成员赋值(不推荐)
    bob.num = lucy.num;
    strcpy(bob.name, lucy.name);
    bob.score = lucy.score;
    #endif

    #if 0
    //第二种方式:相同类型的结构体变量 可以整体赋值(推荐)
    bob = lucy;//单纯的值拷贝(浅拷贝)
    #endif

    #if 1
    //第三种方式:内存拷贝(浅拷贝)
    memcpy(&bob, &lucy, sizeof(struct student));
    #endif

    printf("num = %d\n", bob.num);
    printf("name = %s\n", bob.name);
    printf("score = %f\n", bob.score);
}

标准输入给结构体变量赋值

void test03()
{
    struct student lucy;
    memset(&lucy, 0, sizeof(lucy));

    printf("请输入num name score:");
    // &lucy.num:取的是lucy的num成员的地址
    scanf("%d %s %f", &lucy.num, lucy.name, &lucy.score);
    

    printf("num = %d\n", lucy.num);
    printf("name = %s\n", lucy.name);
    printf("score = %f\n", lucy.score);
}

两个结构体变量,不能直接比大小

lucy > bob;//err
lucy.num > bob.num;//ok
lucy.name < bob.name;//此处只是比较地址的大小  而不是字符串的大小
strcmp(lucy.name, bob.name) == 0;相等
lucy.score != bob.score;//ok

 知识点6【结构体指针变量】(重要)

void test04()
{
    struct student lucy = {100, "lucy", 99.9f};
    //定义指针变量p指向lucy
    struct student *p = &lucy;

    //第一种方式:*p访问 ==lucy
    printf("num = %d\n", (*p).num);
    printf("name = %s\n", (*p).name);
    printf("score = %f\n", (*p).score);
    
    //第二种方式:通过p(地址)直接访问成员内容
    printf("num = %d\n", p->num);
    printf("name = %s\n", p->name);
    printf("score = %f\n", p->score);

    //不管是.还是-> 只看左边是普通变量就用.  如果是地址 就用->
}
struct student lucy;
struct student *p=&lucy;
一下访问成员正确的是__AB___
A:lucy.num       B:(&lucy)->num   C:*p->num   D:(&(*p)).num

知识点7【结构体数组】(重要)

1、遍历结构体数组

void test01()
{
    HERO hero_buf[]={ {"德玛西亚", 80, 70}, {"小炮", 50, 80}, {"小法", 40, 90}};
    int n = sizeof(hero_buf)/sizeof(hero_buf[0]);
    

    int i = 0;
    for ( i = 0; i < n; i++)
    {
        //printf("%s %d %d\n", hero_buf[i].name, hero_buf[i].def, hero_buf[i].atk);
        printf("%s %d %d\n", (hero_buf+i)->name, (hero_buf+i)->def, (hero_buf+i)->atk);
    }
}

2、给结构体数组的元素获取键盘输入

#include <stdio.h>
typedef struct hero
{
    char name[32];
    int def;
    int atk;
}HERO;//HERO是类型名

#include <string.h>
void test01()
{
    HERO hero_buf[3];
    memset(hero_buf, 0, sizeof(hero_buf));
    int n = sizeof(hero_buf)/sizeof(hero_buf[0]);
    
    //获取键盘输入
    int i = 0;
    for ( i = 0; i < n; i++)
    {
        printf("请输入第%d个英雄的属性:", i+1);
        scanf("%s %d %d", hero_buf[i].name, &hero_buf[i].def, &hero_buf[i].atk);
    }
       
    for ( i = 0; i < n; i++)
    {
        //printf("%s %d %d\n", hero_buf[i].name, hero_buf[i].def, hero_buf[i].atk);
        printf("%s %d %d\n", (hero_buf+i)->name, (hero_buf+i)->def, (hero_buf+i)->atk);
    }
}
int main(int argc, char const *argv[])
{
    test01();
    return 0;
}

知识点8【结构体成员指针】(重要)

typedef struct hero
{
    char *name;//成员指针
    int def;
    int atk;
}HERO;//HERO是类型名

1、结构体变量在栈区、全局区 成员指针 指向了文字常量区

typedef struct 
{
    char *name;
    int def;
    int atk;
}HERO2;//HERO2是类型名
void test02()
{
    printf("%d\n", sizeof(HERO2));
    HERO2 hero;
    hero.name = "德玛西亚";
    hero.def = 80;
    hero.atk = 100;
    hero.name[2]='h';//操作文字常量区 出现段错误
    printf("%s %d %d\n", hero.name, hero.def, hero.atk);
}

2、结构体变量在栈区、全局区 成员指针 指向了堆区

#include <string.h>
#include <stdlib.h>
void test03()
{
    HERO2 hero;
    hero.name = (char *)calloc(1, 16);
    if(hero.name == NULL)
    {
        perror("calloc");
        return;
    }
    
    strcpy(hero.name, "德玛西亚");
    hero.def = 80;
    hero.atk = 100;
    printf("%s %d %d\n", hero.name, hero.def, hero.atk);

    if(hero.name != NULL)
    {
        free(hero.name);
        hero.name = NULL;
    }
}

3、结构体空间在堆区 成员指针 指向了堆区

void test04()
{
    HERO2 *p = NULL;
    //为结构体指针变量p申请结构体空间
    p = (HERO2 *)calloc(1,sizeof(HERO2));
    if(p == NULL)
    {
        perror("calloc");
        return;
    }

    p->def = 80;
    p->atk = 100;

    //为p->name指针成员 申请指向的空间
    p->name = (char *)calloc(1, 16);
    if(p->name == NULL)
    {
        perror("calloc");
        return;
    }
    strcpy(p->name, "德玛西亚");
    printf("%s %d %d\n", p->name, p->def, p->atk);

    //释放成员指向的空间
    if(p->name != NULL)
    {
        free(p->name);
        p->name = NULL;
    }
    //释放结构体的空间
    if(p != NULL)
    {
        free(p);
        p = NULL;
    }

}

4、结构体指针数组空间在堆区 结构体数组元素在堆区 成员指针 指向了堆区

typedef struct 
{
    char *name;
    int def;
    int atk;
}HERO2;//HERO2是类型名
void test05()
{
    int n = 0;
    printf("请输入结构体指针数组的元素个数:");
    scanf("%d", &n);

    //p_arr保存堆区空间的结构体指针数组的首元素地址
    HERO2 **p_arr = NULL;
    p_arr = (HERO2 **)calloc(n,sizeof(HERO2 *));
    if(NULL == p_arr)
    {
        perror("calloc");
        return;
    }

    //给结构体指针数组的每个元素的指向 申请空间
    int  i = 0;
    for ( i = 0; i < n; i++)
    {
        p_arr[i] = (HERO2 *)calloc(1,sizeof(HERO2));
        if(NULL == p_arr[i])
        {
            perror("calloc");
            return;
        }

        printf("请输入第%d个英雄的信息:", i+1);
        char name[128]="";
        int def;
        int atk;
        scanf("%s %d %d", name, &def, &atk);

        p_arr[i]->def = def;
        p_arr[i]->atk = atk;

        //给结构体空间中的成员 申请指向
        p_arr[i]->name = (char *)calloc(1, strlen(name)+1);
        if(NULL == p_arr[i]->name)
        {
            perror("calloc");
            return;
        }
        strcpy(p_arr[i]->name, name);
    }

    //遍历
    for ( i = 0; i < n; i++)
    {
        printf("%s %d %d\n", \
        p_arr[i]->name, p_arr[i]->def, p_arr[i]->atk);
    }
    
    //释放空间
    for ( i = 0; i < n; i++)
    {
        //释放结构体空间中的name的指向
        if(p_arr[i]->name != NULL)
        {
            free(p_arr[i]->name);
            p_arr[i]->name = NULL;
        }

        //释放结构体的空间
        if(p_arr[i] != NULL)
        {
            free(p_arr[i]);
            p_arr[i] =NULL;
        }
    }
    //释放整个结构体指针数组的空间
    if(p_arr != NULL)
    {
        free(p_arr);
        p_arr = NULL;
    }
    
    return;
}
int main(int argc, char const *argv[])
{
    test05();
    return 0;
}

知识点9【结构体嵌套结构体】(重要)

struct stu
{
    int num;
    char name[16];
    int age;
};
struct info 
{
    //结构体变量 作为结构体的成员
    struct stu student; 

    int score;
};

void test06()
{
    struct info  ob = {{100,"lucy", 18}, 99};

    //如果结构体 嵌套 结构体   访问必须到最底层
    printf("%d %s %d %d\n", \
    ob.student.num, ob.student.name, ob.student.age, ob.score);
}

知识点10【结构体对齐问题】(重要)

1、知识点的引入

struct data1
{
    char a;//1B
    int b;//4B
};
void test01()
{
    printf("%d\n",sizeof(struct data1));//8B 为啥?
}

2、对齐规则(默认对齐)

第一步:确定分配单位(每行开辟多少字节)

结构体中最大的基本类型的长度 为分配单位。

第二步:确定成员的偏移位置。

偏移位置:成员自身类型的整数倍(0~n倍)

第三步:收尾工作:

结构体的总大小必须是分配单位的整数倍

案例1:

struct data1
{
    char a;//1B
    int b;//4B
};

案例2:

struct data2
{
    char a;
    short b;
    char c;
    int d;
};

案例3:

struct data2
{
    char a;
    short b;
    short c;
    char d;
};

3、结构体嵌套结构体

第一步:确定分配单位(每行开辟多少字节)

所有结构体中最大的基本类型的长度 为分配单位。

第二步:确定成员的偏移位置。

普通成员偏移位置:成员自身类型的整数倍(0~n倍)

结构体成员的偏移量:该结构体的最大基本类型的整数倍

第三步:收尾工作:

结构体成员:是该结构体的最大基本类型整数倍。

结构体的总大小必须是分配单位的整数倍

struct A
{
    char b;
    short c;
};
struct B
{
    int a;
    struct A ob;//结构体成员的偏移量
    int d;
};

案例1:

struct A
{
    short b;
    char c;
};
struct B
{
    int f;
    char a;
    struct A ob;//结构体成员的偏移量
    char d;
};

#include <stdio.h>
struct A
{
    short b;
    char c;
};
struct B
{
    int f;
    char a;
    struct A ob;//结构体成员的偏移量
    char d;
};
void test01()
{
    printf("%d\n",sizeof(struct B));//8B 为啥?

    struct B data = {10,'A',{20,'c'}, 'd'};
    char *p = (char *)&data;
    printf("%hd\n", *(short *)(p+6));//20
}

3、强制内存对齐

#pragma pack (value)时的指定对齐值value

第一步:确定分配单位(每行开辟多少字节)

min(value,最大的基本类型的长度) 为分配单位。

第二步:确定成员的偏移位置。

偏移位置:成员自身类型的整数倍(0~n倍)

第三步:收尾工作:

结构体的总大小必须是分配单位的整数倍

#include <stdio.h>
#include<stdio.h>
#pragma pack (4)
struct stu
{
    char a;
    short b;
    short c;
};
void test01()
{
    printf("%d\n",sizeof(struct stu));//6
}
struct stu1{
    char a;
    int b;
    char c;
    int d;
};

struct stu2{
    char a;
    char c;
    int b;
    int d;
};

void test03(){
    printf("%lu\n",sizeof(struct stu1));//16
    printf("%lu\n",sizeof(struct stu2));//12
}

int main(int argc, char const *argv[]){
    test03();
    return 0;
}

知识点11【拓展求结构体成员的偏移量】(了解)

struct stu1
{
    char a;
    int b;
    char c;
    int d;
};

#include <stdio.h>
#include<stdio.h>
struct stu1
{
    char a;
    int b;
    char c;
    int d;
};

#define  OFF_SET(TYPE, member) (int)&(((TYPE *)0)->member)
void test01()
{
    struct stu1 data;
    printf("偏移量:%d\n",   OFF_SET(struct stu1, b) );//8
    
}

知识点12【位段(位域)】(重要)

在结构体中,以位为单位的成员,咱们称之为位段(位域)

struct packed_data{ 
unsigned int a:2; //成员a只占unsigned int类型中的2位
unsigned int b:6; 
unsigned int c:4; 
unsigned int d:4; // a b  c  d相邻位域
unsigned int i; //非位域
} data;

相邻位域可以压缩,但是压缩的位数 不能超过 自身类型的大小。

struct stu1
{
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:1;
    unsigned char d:2;
};

void test01()
{
    printf("大小:%d\n",   sizeof(struct stu1) );//1 
}
struct stu1
{
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:1;
    unsigned char d:3;
};

void test01()
{
    printf("大小:%d\n",   sizeof(struct stu1) );//2 
}

如果给位域赋值 不能超过位域大小 如果超过 那么超过部分自动丢弃

struct stu1
{
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:1;
    unsigned char d:2;
};

void test01()
{
    struct stu1 data;
    data.a = 7;//0111只取11
    printf("data.a = %d\n", data.a);//3
}
struct stu1{
    unsigned char a:2;
    unsigned char b:3;
    unsigned char c:1;
    unsigned char d:2;
};

void test04(){
    struct stu1 data;
    &data.a;    //错误,无法取得位段‘a’的地址
}

int main(int argc, char const *argv[]){
    test03();
    return 0;
}
register int data;//还记得为啥不能&data吗?

如一个位段要从另一个存储单元开始。

unsigned char a:1; 
unsigned char b:2; 
unsigned char :0; 
unsigned char c:3;(另一个单元)
struct stu4{
    unsigned char a:2;
    unsigned char b:3;
    unsigned char :0;   //另起一个存储单元
    unsigned char c:1;
    unsigned char d:2;
};

void test05(){
    struct stu4 data;
    printf("%d\n", sizeof(data)); //2
}

无意义位段

struct stu1{
    unsigned char a: 2; 
    unsigned char : 2; 
    unsigned char b: 4;
}

 应用场景:

知识点12【共用体】(了解)

共用体和结构体 区别:

结构体的成员拥有独立的空间

共用体成员共享同一份的空间, 空间的大小由最大的成员类型长度决定

union data
{
    char a;
    short b;
    int c;
};

void test01(){
    printf("%d\n", sizeof(union data));
    union data ob;
    ob.a = 10;
    ob.b = 20;
    ob.c = 30;
    printf("%d\n", ob.a + ob.b + ob.c);
}

共用体的成员虽然共享同一块空间 但是成员操作空间的大小由自身大小决定。

知识点13【枚举】(了解)

枚举:将变量要赋的值 一一列举出来

enum poke_color{HONGTAO, MEIHUA, FANGKUAI, HEITAO};
enum poke_color p_color;//p_color只能赋HONGTAO, MEIHUA, FANGKUAI, HEITAO中的某一个值

p_color = 100;//无意义

enum poke_color{HONGTAO, MEIHUA, FANGKUAI, HEITAO};
void test02()
{
    //p_color只能赋HONGTAO, MEIHUA, FANGKUAI, HEITAO中的某一个值
    enum poke_color p_color;
    p_color = MEIHUA;
    printf("p_color = %d\n", p_color);//1

    //默认情况下 枚举的符号常量 从0开始递增
    printf("%d %d %d %d\n", HONGTAO, MEIHUA, FANGKUAI, HEITAO);//0 1 2 3
    
    //可以在定义枚举符号常量时 修改枚举符号常量的值
    enum poke_color2{HONGTAO, MEIHUA=5, FANGKUAI, HEITAO};
    printf("%d %d %d %d\n", HONGTAO, MEIHUA, FANGKUAI, HEITAO);//0 1 2 3
}

标签:struct,int,精讲,lucy,char,printf,结构,name
来源: https://blog.csdn.net/qq_34981463/article/details/118443753