2023/8/1 -- 数据结构

一、值传递、地址传递、值返回、地址返回

i) 左值和右值

左值:既能放到等号左侧、也能放到等号右侧的值称为左值,有地址空间,例如:变量、堆区申请的空间、地址返回的函数返回值

右值:只能放到等号右侧的值,没有地址空间,例如:常量、临时值、表达式的结果、值返回的函数返回值

ii)值传递

普通变量作为函数参数传递是单向的值传递,只是将实参的值复制一份给形参变量,形参的改变不会影响实参的值,因为所在内存空间不同

如果传递的是地址,被调函数使用指针接收,如果在被调函数中,没有更改指针指向空间中的内容,只改变指向,依然是值传递

iii) 地址传递

指针、数组名作为函数参数传递,是地址传递,需要在被调函数中更改指针指向空间中的内容,形参内容的改变,实参也跟着改变

iv)值返回

普通变量通过函数返回值进行返回是单向的值返回,在主调函数中,该函数的返回值只能作为右值使用,不能被重新赋值

v)地址返回

需要返回生命周期比较长的变量地址(全局变量、静态局部变量、堆区申请空间、主调函数地址传递的空间),该函数的返回值是一个左值,可以直接使用,也可以被重新赋值,被重新赋值后,被调函数中该空间中的内容也跟着改变

例:

#include<stdio.h>

/*************************值传递************************/
void swap(int m, int n) //传递num和key
{
    int temp;
    temp = m;
    m = n; 
    n = temp; //三杯水交换

    printf("m = %d, n = %d\n", m, n);       //1314  520
}

/************************值传递*****************************/
void fun(int *p, int *q) //传递num和key的地址
{
    int *temp;
    temp = p;
    p = q; 
    q = temp; //三杯水交换,这里交换的是p和q的指向,并没有改变*p和*q的内容
    printf("*p = %d, *q = %d\n", *p, *q);   //1314  520
}

/*********************地址传递********************************/
void gun(int *p, int *q) //传递num和key的地址
{
    int temp;
    temp = *p;
    *p = *q; 
    *q = temp; //三杯水交换,交换*p和*q的内容
    printf("*p = %d, *q = %d\n", *p, *q);   //1314  520
}

/***********************值返回********************************/
int hun()           //int k = 100;
{
    int value = 666;

    return value; //返回value
}

/*************************地址返回*****************************/
int *iun()
{
    static int value = 999;    //静态变量虽然在函数体内定义,但是不占函数的内存空间

    return &value;              //返回静态局部变量的地址
}


/*************************主函数***************************/
int main(int argc, const char *argv[])
{
    int num = 520;
    int key = 1314;

    //调用swap函数交换两数
    swap(num, key);
    printf("调用swap后,主函数中num = %d, key = %d\n", num, key);

    //调用fun函数交换两数
    fun(&num, &key);
    printf("调用fun后,主函数中num = %d, key = %d\n", num, key);

    //调用gun函数交换两数
    gun(&num, &key);
    printf("调用gun后,主函数中num = %d, key = %d\n", num, key);


    //调用hun函数
    //hun() = 999;
    int ret = hun();          //值返回的函数返回值只能是右值
    printf("hun() = %d\n", hun());         //666

    //调用iun函数
    int *ptr = iun();        //地址返回的结果可以作为右值
    *iun() = 555;            //地址返回的结果可以作为左值

    printf("*iun() = %d\n", *iun());        //555
    printf("*ptr = %d\n", *ptr);           //555

    return 0;
}

二、内存分区

  • 一个进程启动后,系统会为该进程分配4G的虚拟内存
  • 0--3G是用户空间,程序员写代码操作部分
  • 3--4G是内核空间,主要与底层驱动打交道
  • 所有进程会共享3--4G的内核空间,但是每个进程独立拥有0--3G的用户空间
  • 0--3G用户空间又被划分为三部分:栈区、堆区、静态区(全局区)
  • 全局区又分为四个段:.bss段、.data段、.ro段、.txt段
  • 应用层主要操作0--3G的用户空间,底层主要操作3--4G的内核空间

例:

#include<stdio.h>

int m;                   //未初始化的全局变量,在全局区的.bss段
int n = 520;            //已初始化的全局变量,在全局区的.data段
static int k;          //未初始化的静态变量,在全局区的.bss段
static int l = 666;   //已初始化的静态变量,在全局区的.data段

char arr[100] = "hello world";  //arr数组在全局取的.data段,而"hello world"在.ro段
char *p = "hello";           //指针在.data段,而“hello”在.ro段


int main(int argc, const char *argv[])
{
    double b = 999.0;    //局部变量在栈区申请
    int a;           //局部变量,在栈区申请,初始值为随机值
    //printf("&a = %p, &b = %p\n", &a, &b);
    static int c;         //静态局部变量,在全局区的.bss段申请
    static int d = 520;  //静态局部变量,在全局区的.data段
    char *q = "nihao";   //q在栈区申请的8字节,但是"nihao"在全局区的.ro段
    char e[100] = "hello world";  //数组在栈区申请,"hello world"在全局区的.ro段
    
    int *ptr = (int *)malloc(sizeof(int));  //ptr是在栈区,而申请的空间在堆区
    
    return 0;
}

三、动态内存分配和回收(malloc、free)

C语言中可以使用malloc和free来对堆区空间进行操作

malloc、free函数原型:

        #include <stdlib.h>         //函数头文件

       void *malloc(size_t size);
功能:允许程序员手动从堆区空间申请内存
参数:要申请的空间大小,以字节为单位,一般格式为 sizeof(类型名)*n
返回值:是一个万能指针,可以使用强制类型转化为自己想要的类型的指针,如果申请成功,则将堆区申请的空间地址返回,如果申请失败,返回NULL
               
       void free(void *ptr);
功能:释放程序员从堆区申请的空间
参数:要释放的空间首地址
返回值:无     

 单个空间内存的申请:数据类型 *指针名    = (数据类型*)malloc(sizeof(数据类型)));
 连续内存的申请:  数据类型 *指针名    = (数据类型*)malloc(sizeof(数据类型)*n));    
 释放:free(指针名);                                                         
#include<stdio.h>
#include<stdlib.h>       //malloc所在的头文件

int main(int argc, const char *argv[])
{
    //在堆区申请一个int类型的空间大小
    int *p1 = (int *)malloc(4);          //申请4字节的大小
    printf("*p1 = %d\n", *p1);              //随机值

    int *p2 = (int *)malloc(sizeof(int));     //申请一个int类型的大小
    *p2 = 520;            //给堆区空间进行赋值
    printf("*p2 = %d\n", *p2);                   //520

    //连续申请5个int类型的大小
    int *p3 = (int *)malloc(sizeof(int)*5);
    //输出默认值
    for(int i=0; i<5; i++)
    {
        //printf("%d\t", p3[i]);
        printf("%d\t", *(p3+i));
    }
    printf("\n");

    //释放堆区空间
    free(p1);
    p1 = NULL;            //防止野指针
    free(p2);
    p2 = NULL;
    free(p3);
    p3 = NULL;
    
    return 0;
}

四、类型重定义

i)概念

类型重定义本质上是给类型重新起个名字,使得代码更加易于理解,例如:将unsigned long int 改名为uint64,将unsigned short int 改名为uint16

ii)使用格式

typedef 数据类型名 新名;

例:typedef unsigned long int uint64;

       typedef unsigned short int uint16;

iii)所学类型

1> 使用变量定义类型
int  a;                //定义普通变量
int *ptr;                 //定义指针类型变量
int arr[5];               //定义数组类型变量
int *ptr_arr[5];          //定义指针数组变量
int (*arr_ptr)[5];          //定义数组指针变量
int (*fun_ptr)(int,int);    //定义函数指针变量
int (*fun_ptr_arr[3])(int, int);     //定义函数指针数组的指针变量
int **pptr;                       //定义二级指针变量
2> 提取类型
int  ;                //定义普通数据类型
int *;                 //定义指针类型
int [5];               //定义数组类型
int *[5];          //定义指针数组
int (*)[5];          //定义数组指针
int (*)(int,int);    //定义函数指针
int (*[3])(int, int);     //定义函数指针数组的指针
int **;                       //定义二级指针
3> 给类型重新起名
typedef int  A;                //重定义int类型为A类型
typedef int *Ptr;                 //重定义指针类型
typedef int ARR[5];               //重定义数组类型
typedef int *Ptr_Arr[5];          //重定义指针数组
typedef int (*Arr_Ptr)[5];          //重定义数组指针
typedef int (*Fun_Ptr)(int,int);    //重定义函数指针
typedef int (*Fun_Ptr_Arr[3])(int, int);     //重定义函数指针数组的指针
typedef int **PPtr;                       //重定义二级指针

例:

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

typedef unsigned short int uint16;     //将无符号短整形重命名为uint16

typedef int * Ptr_i;          //将int*类型重命名为Ptr_i

typedef char String[10];          //将char [5]类型重命名为String

int main(int argc, const char *argv[])
{
    uint16 num = 520;           //等价于unsigned short int num = 520
    printf("sizeof num = %ld\n", sizeof(num));          //2
    printf("num = %d\n", num);               //520

    Ptr_i p1 = NULL;            //此时p1是指针变量  int *p1;
    printf("sizeof p1 = %ld\n", sizeof(p1));        //8

    String s1;           //此时是定义的长度为10的字符数组
    strcpy(s1, "hello");
    printf("s1 = %s\n", s1);         //hello
    printf("sizeof s1 = %ld\n", sizeof(s1));      //10
    printf("strlen s1 = %ld\n", strlen(s1));       //5

    return 0;
}

iv)给类型起多个名

#include<stdio.h>

typedef int *Ptr_i, int32;    //int32是一个int类型的重命名
                                //Ptr_i是int*类型的重命名

int main(int argc, const char *argv[])
{
    int32 num;          //num是一个普通变量
    Ptr_i p;              //p是一个int×类型的变量

    printf("sizeof num = %ld\n", sizeof(num));      //4
    printf("sizeof p = %ld\n", sizeof(p));      //8 
    return 0;
}

v)类型重定义与宏定义的区别

  1. 宏定义只是单纯的替换,不做任何正确性检测,是一个预处理指令
  2.  类型重定义,需要做正确性检测,是一条语句
  3.  宏替换发生在预处理阶段,而类型重定义发生在编译阶段
  4. 如果是对普通单个重命名没有问题,但是对指针重命名就有问题了
#include<stdio.h>

#define ptr_i int*            //会将uint32替换成int
typedef int * pptr_i;   //类型重定义

int main(int argc, const char *argv[])
{
    ptr_i a,b;         //a是指针类型,b是普通int类型   int *a, b;
    pptr_i m,n;          //m和n都是指针类型

    printf("sizeof a = %ld\n", sizeof(a));         //8
    printf("sizeof b = %ld\n", sizeof(b));        //4
    printf("sizeof m = %ld\n", sizeof(m));         //8
    printf("sizeof n = %ld\n", sizeof(n));         //8
    return 0;
}

五、结构体

i)引入目的

系统提供的数据类型不够用了,没有条件创造条件,自己定义数据类型,自己用

ii)定义

有相同数据类型和不同数据类型构成的集合叫结构体,属于构造数据类型

iii)定义格式

struct 结构体名
{
    //属性列表
    成员类型1 成员变量1;
    成员类型2 成员变量2;
    。。。
    成员类型n 成员变量n;
};

注意:
1、struct是定义结构体的关键字
2、结构体名:是一个标识符,要符合标识符的命名规则,一般首字母大写
3、所有的成员属性使用一对花括号包裹,最后用分号结束,分号不能省略
4、成员属性类型可以是基本数据类型,也可以是构造数据类型
5、声明结构体不占内存空间,使用结构体类型定义变量时,变量会占用内存空间
6、一般将声明结构体放在文件头部或者头文件中,也可以声明在其他部分,但是,至少要声明在使用之前

iv)结构体变量的定义及初始化

  • 定义结构体变量格式:struct 结构体类型名 变量名1,变量名2;
  • 也可以在声明结构体类型时,顺便定义结构体变量(二合一)
  • 定义结构体变量初始化时,可以指定某个属性进行初始化工作,没有初始化的内容全部时随机值
  • 可以使用无名结构体,定义结构体类型时,需要定义结构体变量,后期该结构体就不能定义变量了
#include<stdio.h>

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
}h3 = {"盖伦", 3500, 500, 5};

//练习:定义一个商品类型,成员属性:商品名称(name)、产地(position)、单价(price)、重量(weight)
struct                           //无名结构体
{
    char name[40];       //名称
    char position[40];     //产地
    double price;            //单价
    double weight;           //重量
}g1 = {"三鹿奶粉", "China", 350, 1000};


int main(int argc, const char *argv[])
{

    //使用英雄类型定义一个英雄变量
    struct Hero h1 = {"亚索", 650, 350, 0};            //此时定一个英雄变量h1

    //定义英雄变量,指定某个成员进行赋值
    struct Hero h2 = {.Hp=2000, .speed = 1000};

    return 0;
}

v)结构体变量访问成员

  • 成员运算符 ".",个人读作"的";例如h1.name 表示h1结构体变量的name成员
  • 不要企图通过结构体变量名直接输出所有成员,如果想要输出结构体变量中的成员,需要使用成员运算符一个一个找
  • 相同类型的结构体变量之间是可以之间互相赋值的
  • 也可以对结构体变量取地址运算,其地址跟第一个成员属性的地址保持一致

vi) 结构体指针访问成员

结构体指针访问成员使用运算符"->"

使用格式:指针名->属性名

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

//声明一个英雄结构体类型
struct Hero
{
    char name[20];      //姓名
    int Hp;            //血量
    double speed;        //基础位移
    int kill;            //人头数
}h3 = {"盖伦", 3500, 500, 5};

//练习:定义一个商品类型,成员属性:商品名称(name)、产地(position)、单价(price)、重量(weight)
struct                           //无名结构体
{
    char name[40];       //名称
    char position[40];     //产地
    double price;            //单价
    double weight;           //重量
}g1 = {"三鹿奶粉", "China", 350, 1000};


int main(int argc, const char *argv[])
{

    //使用英雄类型定义一个英雄变量
    struct Hero h1 = {"亚索", 650, 350, 0};            //此时定一个英雄变量h1

    //定义英雄变量,指定某个成员进行赋值
    struct Hero h2 = {.Hp=2000, .speed = 1000};

    //输出英雄变量h1中的所有内容
    printf("h1.name = %s\n", h1.name);
    printf("h1.Hp = %d\n", h1.Hp);
    printf("h1.speed = %lf\n", h1.speed);
    printf("h1.kill = %d\n", h1.kill);

    //在堆区申请一个英雄类型,完成初始化并输出相应的属性
    struct Hero *ptr = (struct Hero*)malloc(sizeof(struct Hero));
    //给英雄名字赋值
    strcpy(ptr->name,"亚瑟");     //给姓名赋值
    ptr->Hp = 3000;            //给hp属性赋值
    ptr->speed = 350;      
    ptr->kill = 3;

    //输出英雄指针指向堆区空间中的内容
    printf("英雄信息为:%s  %d  %.2lf  %d\n", ptr->name, ptr->Hp, ptr->speed, ptr->kill);

    printf("*********************************************************************\n");

    //练习:使用商品结构体定义一个商品变量,不用初始化,使用scanf将每个属性进行输入
    //输入完该变量后,再将该变量的所有信息输出
    printf("请输入商品的名称:");
    scanf("%s", g1.name);
    printf("请输入商品的产地:");
    scanf("%s", g1.position);
    printf("请输入商品的单价:");
    scanf("%lf", &g1.price);
    printf("请输入商品的重量:");
    scanf("%lf", &g1.weight);

    //输出信息
    printf("商品信息:%s %s %.2lf %.2lf\n", g1.name, g1.position, g1.price, g1.weight);

    return 0;
}

vii)结构体数组

结构体数组本质上是一个数组,数组的每个元素都是结构体类型

数组的操作跟基本数据类型一致

#include<stdio.h>
//定义一个学生结构体
struct Stu
{
    char name[20];       //姓名属性
    int age;                //年龄
    double score;           //分数
    char sex;                //性别
};



int main(int argc, const char *argv[])
{
    //定义三个学生,对这三个学生进行管理
    struct Stu s[3] =\
    {{"张三",18,99,'M'},\
        {"李四", 20, 80, 'W'},\
        {"王五", 25, 100, 'M'}};            //定义了三个学生变量,s[0]、s[1]、s[2]
    
    //输出三名学生的信息
    printf("姓名\t年龄\t分数\t性别\n");
    for(int i=0; i<3; i++)
    {
        printf("%s\t%d\t%.2lf\t%c\n", s[i].name, s[i].age, s[i].score, s[i].sex);
    }

    //通过键盘更新第二名学生的信息  s[1]
    printf("请输入第二名学生的姓名:");
    scanf("%s", s[1].name);     //  (s+1)->name
    printf("请输入第二名学生的分数:");
    scanf("%lf", &s[1].score);
    printf("%s\t%d\t%.2lf\t%c\n", s[1].name, s[1].age, s[1].score, s[1].sex);


    return 0;
}

练习:

声明一个结构体类型,英雄类型,成员属性:姓名(name)、人头数(kill)、死亡数(death)、助攻(asst),使用该英雄类型定义5个英雄,调用函数输入这5名英雄的数据,再调用函数实现对五名英雄按人头数降序排序,封装函数完成求出本场的MVP(计算方式:人头数-死亡数据+助攻数,最大值为MVP),最后输出排序后的所有英雄的信息,以及本场MVP的信息

#include<myhead.h>
#include<stdlib.h>      //malloc的头文件
#include<string.h>

//定义英雄类型
struct Hero
{
    char name[20];       //姓名
    int kill;            //人头
    int death;          //死亡数
    int asst;           //助攻数
};

/**************************定义申请内存空间函数**********************/
struct Hero *getHeros(int num)
{
    //在堆区进行申请num个英雄
    struct Hero * H = (struct Hero *)malloc(sizeof(struct Hero)*num);
    if(NULL==H)
    {
        printf("空间申请失败\n");
        return NULL;
    }else
    {
        printf("最强团队已经产生\n");
        return H;
    }
}

/*****************************定义输入英雄数据函数******************/
void inputHeros(struct Hero *H, int num)
{
    //判断传过来的指针是否合法
    if(NULL == H)
    {
        printf("英雄团队不合法\n");
        return;
    }

    //开始输入
    for(int i=0; i<num; i++)
    {
        printf("请输入第%d个英雄的姓名:", i+1);
        scanf("%s", (H+i)->name);
        printf("请输入第%d个英雄斩获人头数:", i+1);
        scanf("%d", &(H+i)->kill);
        printf("请输入第%d个英雄的死亡数据:", i+1);
        scanf("%d", &(H+i)->death);
        printf("请输入第%d个英雄的助攻数据:", i+1);
        scanf("%d", &(H+i)->asst);
        printf("\n");
    }

    printf("本局比赛已经结束\n");
}

/********************对英雄数据按人头进行排序***************/
void sortHeros(struct Hero *H, int num)
{
    //判断传过来的指针是否合法
    if(NULL == H)
    {
        printf("英雄团队不合法\n");
        return;
    }

    //开始排序
    for(int i=1; i<num; i++)
    {
        for(int j=0; j<num-i; j++ )
        {
            if(H[j].kill < H[j+1].kill)     //比较的是成员
            {
                //交换英雄位置
                struct Hero temp = H[j];
                H[j] = H[j+1];
                H[j+1] = temp;

            }
        }
    }

    printf("战场已被清理,排序结束\n");
}

/***************************定义展示函数***********************/
void outputHeros(struct Hero *H, int num)
{
    //判断传过来的指针是否合法
    if(NULL == H)
    {
        printf("英雄团队不合法\n");
        return;
    }

    //输出团队信息
    printf("英雄\t人头数\t死亡数\t助攻数\n");
    for(int i=0; i<num; i++)
    {
        printf("%s\t%d\t%d\t%d\n", H[i].name, H[i].kill, H[i].death, H[i].asst);
    }
}

/***************************释放英雄空间****************************/
void freeHero(struct Hero *H)
{
    if(NULL != H)
    {
        free(H);           //释放堆区空间使用权
        H = NULL;
    }
}

/********************************定义mvp函数*************************/
struct Hero mvpHero(struct Hero* H, int num)
{
    //判断传过来的指针是否合法
    if(NULL == H)
    {
        printf("英雄团队不合法\n");
        return ;
    }
    
    //求mvp
    struct Hero mvp = H[0];          //将第一个英雄当中mvp
    //将该英雄数据跟任意一个英雄进行比较
    for(int i=0; i<num; i++)
    {
        if(mvp.kill-mvp.death+mvp.asst  < H[i].kill-H[i].death+H[i].asst)
        {
            //更新mvp
            mvp = H[i];
        }
    }

    return mvp;

}

    


int main(int argc, const char *argv[])
{
    //申请5个英雄
    struct Hero *H = getHeros(5);

    //调用函数输入5个英雄的数据
    inputHeros(H, 5);

    //调用排序函数
    sortHeros(H, 5);

    //输出结果
    outputHeros(H, 5);

    //获取当前的mvp
    struct Hero m = mvpHero(H, 5);
    printf("本场mvp信息为:%s\t%d\t%d\t%d\n", m.name,m.kill,m.death,m.asst);

    //释放空间
    freeHero(H);
    H = NULL;
    
    return 0;
}

viii)结构体嵌套

  1. 在一个结构体变量中可以嵌套定义一个结构体变量
  2. 如果需要访问结构体变量中最低一级的成员,需要使用成员运算符一级一级找到最低一级数据
  3. 结构体变量找成员使用运算符".",而结构体指针找成员使用运算符"->"
  4. 结构体变量访问成员时,以最后一级的数据类型为主
#include<myhead.h>
//定义一个生日类型
struct Birthday
{
    int year;
    int month;
    int day;
};

//定义一个书本类型
struct Book
{
    char name[20];       //书名
    double price;          //价格
};


//定义一个学生类型
struct Stu
{
    char name[20];         //姓名
    int score;            //分数

    //定义一个生日变量
    struct Birthday bir;        //生日
    
    //定义一个书本类型的指针
    struct Book *b;
    

};



int main(int argc, const char *argv[])
{
    struct Stu s1;
    strcpy(s1.name, "张三");
    s1.score = 90;

    //如果结构体变量中包含一个结构体变量,要找到最底层的数据
    //需要使用成员运算符,一级一级找到最低一级数据进行访问
    s1.bir.year = 2023;
    s1.bir.month = 8;
    s1.bir.day = 2;

    printf("%s\t%d\t%4d\t%02d\t%02d\n", s1.name,s1.score, s1.bir.year, s1.bir.month, s1.bir.day);

    /****************************定义结构体指针指向结构体变量*****************************/
    struct Stu *p = &s1;
    p->bir.month = 10;
    printf("%s\t%d\t%4d\t%02d\t%02d\n", p->name,p->score, p->bir.year, p->bir.month, p->bir.day);

    /*****************************结构体变量访问成员结构体指针中的数据*****************/
    s1.b = (struct Book*)malloc(sizeof(struct Book));

    //通过结构体变量访问成员变量结构体指针中的数据
    strcpy(s1.b->name,"朝花夕拾");

    //使用结构体指针访问成员指针的属性
    p->b->price = 100;

    return 0;
}

viiii)结构体中封装函数

  • C语言中的结构体,不能封装函数
  • 如果非要在结构体中封装函数,需要使用函数指针完成,在结构体中封装一个函数指针变量,后期使用函数回调即可
  • 本质上封装的是一个函数指针变量,后期可以对该变量赋值后,调用相关功能
#include<myhead.h>

//定义一个全局函数
void show(const char* s)
{
    printf("%s\n", s);
}

//定义一个结构体,封装一个函数指针
struct Stu
{
    char name[20];
    int age;

    void (*display)(const char *);      //封装一个函数指针
};



int main(int argc, const char *argv[])
{
    struct Stu s;

    s.display = show;       //给成员指针赋值

    s.display("hello world");     //调用成员函数
    
    return 0;
}

vv)结构体与typedef结合使用

可以给结构体类型重新命名,也可以给结构体指针重新命名

#include<myhead.h>

//方式1:定义结构体时,顺便重命名
typedef struct Stu
{
    char name[20];         //姓名
    int age;                //年龄
    double score;           //分数
}stu_struct, *stu_ptr;       //给结构体名两个名


typedef struct Stu Stu;         //方式2:给结构体类型重命名

//方式3:给无名结构体重命名
typedef struct
{
    char name[20];      //狗名
    char class[20];      //品种
}Dog;



int main(int argc, const char *argv[])
{
    stu_struct s1;      //结构体变量
    stu_ptr p1;            //结构体指针
    printf("sizeof s1 = %ld\n", sizeof(s1));   //?
    printf("sizeof p1 = %ld\n", sizeof(p1));   //8

    
    return 0;
}

vvi)结构体大小

一般而言,结构体的大小是各个成员变量所在内存之和

 特殊而言,结构体需要进行字节对齐,有利于数据偏移,和寻址功能

在C语言中,一个空的结构体,所占内存大小为0字节

字节对齐:

  1. 在结构体中的每个属性会根据自己所占内存大小,来设置起始存储位置,起始存储位置必须是自身类型的整数倍
  2. 在上面对齐的基础上,最后整体会进行一次对齐,整体的字节个数要是一个数据的整数倍,这个数据是系统字节对齐和结构体中最大成员所占字节的之间取最小值。min(系统对齐字节数,结构体中所占内存最大的字节数)
#include<myhead.h>

struct A
{
};

struct B           // 1
{
    char ch;
};

struct C          // 1022 
{
    char ch;
    short num;   
};


struct D          // 1120 
{
    short num;   
    char ch;
};

struct E         // 11203333 
{
    short num;   
    char ch;
    int key;
};


struct F         // 100022223300
{
    char ch;
    int key;
    short num;   
};

struct G         // 1000222233333333
{
    char ch;
    int key;
    short* num;   
};


struct H        // 100000002222222233330000
{
    char ch;
    short* num;   
    int key;
};

struct I       // 1000222233004400
{

    struct F ch;
    short num;

};

struct J       // 1100100022223300
{

    short num;
    struct F ch;

};

struct K      // 
{

    char arr[5];
    int num;

};



int main(int argc, const char *argv[])
{
    printf("sizeof(A) = %ld\n", sizeof(struct A));     //?
    printf("sizeof(B) = %ld\n", sizeof(struct B));     //1
    printf("sizeof(C) = %ld\n", sizeof(struct C));     //4
#if 0
    struct C temp;
    printf("&temp.ch = %p\n", &temp.ch);
    printf("&temp.num = %p\n", &temp.num);
#endif

    printf("sizeof(D) = %ld\n", sizeof(struct D));     //4
    struct D temp;
    printf("&temp.ch = %p\n", &temp.ch);
    printf("&temp.num = %p\n", &temp.num);


    printf("sizeof(E) = %ld\n", sizeof(struct E));     //8
    printf("sizeof(F) = %ld\n", sizeof(struct F));     //12
    printf("sizeof(G) = %ld\n", sizeof(struct G));     //16
    printf("sizeof(H) = %ld\n", sizeof(struct H));     //24
    printf("sizeof(I) = %ld\n", sizeof(struct I));     //16
    printf("sizeof(J) = %ld\n", sizeof(struct J));     //16
    printf("sizeof(K) = %ld\n", sizeof(struct K));     //12
    


    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值