day13

一、共用体

1.1 共用体的引入目的


 当需要将多个变量共享同一块内存时,可以使用共用体

2.2 定义及初始化

1 > 概念:由相同数据类型或不同数据类型构成的集合,但是,该集合中的成员共享同一块内存空间

2 > 定义格式
                                                                                                                                                         

    union 结构体名称
{
    成员类型1 成员变量1;
    成员类型2 成员变量2;
      。。。 成员类型n 成员变量n;
};


3 > 共用体初始化
       

1、共用体初始化方式跟结构体初始化方式一致

2、共用体变量初始化时,只给定一个值即可,就是第一个成员的值

3 > 共用体访问成员跟结构体一致

4> 共用体成员中可以包含基本数据类型,也可以包含构造数据类型

5 > 共用体变量的大小,是所有成员中占内存最大的成员的大小

6> 共用体变量一般不作为函数 参数传递

7 > 举个例子
                                                                                                                                                                                                                                                                                                             Plain 

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

    //定义共用体类型
    union Info
{
    char value_a;
    int value_b;
    double value_c;
};

int main(int argc, const char *argv[])
{
    //测试共用体类型的大小
    printf("sizeof union Info = %ld\n", sizeof(union Info)); //8

    //定义共用体变量
    union Info temp = {30}; //只需要初始化一个值

    //访问成员
    temp.value_a = 100;
    //printf("value_a = %d\n", temp.value_a);    //100
    temp.value_b = 520;
    //printf("value_a = %d\n", temp.value_b);    //
    temp.value_c = 3.14;
    printf("value_c = %.2lf\n", temp.value_c); //3.14
    printf("value_b = %d\n", temp.value_b);    //520?
    printf("value_a = %c\n", temp.value_a);    //100?

    return 0;
}

8> 实战常用例子
     

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

    //定义共用体类型
    union Info
{
    char ch; //字符数据
    int num; //整形数据
} temp;

int main(int argc, const char *argv[])
{
    //使用共用体判断自己的主机的大小端问题
    temp.num = 0x12345678;

    //判断temp.ch的值即可
    if (temp.ch == 0x12)
    {
        printf("big edinan\n");
    }
    else if (temp.ch == 0x78)
    {
        printf("little endian\n");
    }

    return 0;
}

9 > 共用体变量的各个成员的地址和共用体变量都是同一地址

二、类型重定义

2.1 引入目的


             当程序员写程序时,可能会因为数据类型的问题,在定义变量时,导致该程序比较晦涩难懂,例如:unsigned long int,struct Stu
            为了提高代码的简洁性,以及为了更加方便理解使得代码更加优雅,我们可以将这些类型重新起一个简洁好记的名字

2.2 使用方式

1 > 类型重定义,我们引入了关键字 typedef

2 > 使用方式: typedef 旧名字 新名字;
                                                                                                                                                            例如:typedef unsigned long int uint64;


2.3 目前所接触的数据类型

1 > 总结定义的变量
                                

    int a;                   //定义一个普通变量,变量名为 a
int arr[5];                  //定义一个长度位5的整型数组,数组名位arr
int *ptr;                    //定义一个一级指针变量,变量名为ptr
int **pptr;                  //定义了一个二级指针变量,变量名为pptr
int *ptr_arr[5];             //定义一个长度为5的指针数组,数组名为ptr_arr
int (*arr_ptr)[5];           //定义了一个数组指针,指向长度为5的数组,变量名为arr_ptr
int (*fun_ptr)(void);        //定义了一个函数指针变量,指向返回值为int参数为空的函数,变量名为fun_ptr
int (*fun_ptr_arr[5])(void); //定义了一个函数指针数组,数组名为fun_ptr_arr
struct Info
{
    ...
} buf; //定义了一个结构体变量
2 > 提取类型
       

    int;          //定义一个普通变量类型
int[5];           //定义一个长度位5的整型数组类型
int *;            //定义一个一级指针变量类型
int **;           //定义了一个二级指针变量类型
int *[5];         //定义一个长度为5的指针数组类型
int (*)[5];       //定义了一个数组指针类型
int (*)(void);    //定义了一个函数指针变量类型
int (*[5])(void); //定义了一个函数指针数组类型
struct Info
{
    ...
}; //定义了一个结构体类型
3 > 将上面类型进行重定义
   

    typedef int a;                   //定义一个普通变量,变量名为 a
typedef int arr[5];                  //定义一个长度位5的整型数组,数组名位arr
typedef int *ptr;                    //定义一个一级指针变量,变量名为ptr
typedef int **pptr;                  //定义了一个二级指针变量,变量名为pptr
typedef int *ptr_arr[5];             //定义一个长度为5的指针数组,数组名为ptr_arr
typedef int (*arr_ptr)[5];           //定义了一个数组指针,指向长度为5的数组,变量名为arr_ptr
typedef int (*fun_ptr)(void);        //定义了一个函数指针变量,指向返回值为int参数为空的函数,变量名为fun_ptr
typedef int (*fun_ptr_arr[5])(void); //定义了一个函数指针数组,数组名为fun_ptr_arr
typedef struct Info
{
    ...
} buf; //定义了一个结构体类型
实例:
 

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

    typedef int int32;     //给int类型重新起个名字
typedef int *ptr;          //给int *重新命名
typedef int arr[5];        //给int [5] 重新命名
typedef int (*arr_ptr)[5]; //给数组指针重命名
typedef struct Stu
{
    char name[20]; //姓名
    int age;
    double score;
} Stu; //给结构体重新命名

int main(int argc, const char *argv[])
{
    int32 num = 520;                           //使用重命名的类型定义变量
    printf("sizeof num = %ld\n", sizeof(num)); //4

    ptr p = &num;                          //定义的p是一个指针变量
    printf("sizeof p = %ld\n", sizeof(p)); //8

    arr a = {1, 2, 3, 4, 5};               //此时a就是一个数组名,长度为5
    printf("sizeof a = %ld\n", sizeof(a)); //20

    arr_ptr ap; //此时ap就是一个数组指针变量
    ap = &a;
    printf("sizeof ap = %ld\n", sizeof(ap)); //8

    Stu s = {"二狗", 20, 80};              //定义一个结构体变量
    printf("sizeof s = %ld\n", sizeof(s)); //32

    return 0;
}

4 > #define 与 typedef的区别
      

 1、#define是宏定义,使用宏名取代字符串,typedef是进行类型重命名,要求必须给定的是数据类型

2、#define是预处理指令,在预处理时,进行替换,typedef时C语言语句,发生在程序执行阶段 3、在使用上面两种方式命名一个变量时,没有太大区别,但是,命名多个指针类型时,就有区别了

       

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define my_int int *     //宏定义
    typedef int *My_int; //类型重定义

int main(int argc, const char *argv[])
{
    my_int a, b; //定义两个变量 int a,b;
    //int *a, b;

    My_int m, n; //定义两个普通变量 int m,n;
    //int *a;   int *b;

    printf("sizeof a = %ld, sizeof b = %ld\n", sizeof(a), sizeof(b));
    printf("sizeof m = %ld, sizeof n = %ld\n", sizeof(m), sizeof(n));

    return 0;
}

5 > 类型重定义,也可以连续定义
       

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

    typedef int my_int,
    *my_ptr; //表示 my_int 是int类型, my_ptr 是int *类型

typedef struct Node
{
    int data;          //数据域
    struct Node *next; //指针域
} Node, *Node_ptr;     //类型重定义两个名字

//此后,Node定义的变量是结构体普通变量
//Node_ptr定义的变量是结构体指针变量

int main(int argc, const char *argv[])
{
    my_int num = 520;
    my_ptr ptr = &num;

    printf("sizeof(num) = %ld, sizeof(ptr) = %ld\n", sizeof(num), sizeof(ptr));
    printf("sizeof(Node) = %ld, sizeof(Node_ptr) = %ld\n",
           sizeof(Node), sizeof(Node_ptr));

    return 0;
}

三、内存划分

1 > 一个进程一旦启动后,操作系统会为其分配 4G 的虚拟内存

2 > 分为两部分:内核空间(1G高地址)、用户空间(3G低地址)

3 > 多个进程独立拥有0-- 3G的用户空间,共享3 --4G的内核空间

4 > 用户空间又分为多个部分,具体如同所示

5 > 通过指令:cat / proc / 进程ID / maps 可以查看当前进程的内存分

布  

564a73455000-564a73456000 r-xp 00000000 08:01 402924                     /home/ubuntu/24061/数据结构/day2/a.out         //对应 .text段
564a73655000-564a73656000 r--p 00000000 08:01 402924                     /home/ubuntu/24061/数据结构/day2/a.out          //对应 .ro段
564a73656000-564a73657000 rw-p 00001000 08:01 402924                     /home/ubuntu/24061/数据结构/day2/a.out          //对应 .bss 和 .data段
564a7451b000-564a7453c000 rw-p 00000000 00:00 0                          [heap]                                          //对应堆区
7f22cfd55000-7f22cff3c000 r-xp 00000000 08:01 924106                     /lib/x86_64-linux-gnu/libc-2.27.so              //动态库的空间
7f22cff3c000-7f22d013c000 ---p 001e7000 08:01 924106                     /lib/x86_64-linux-gnu/libc-2.27.so
7f22d013c000-7f22d0140000 r--p 001e7000 08:01 924106                     /lib/x86_64-linux-gnu/libc-2.27.so
7f22d0140000-7f22d0142000 rw-p 001eb000 08:01 924106                     /lib/x86_64-linux-gnu/libc-2.27.so
7f22d0142000-7f22d0146000 rw-p 00000000 00:00 0 
7f22d0146000-7f22d016d000 r-xp 00000000 08:01 924078                     /lib/x86_64-linux-gnu/ld-2.27.so
7f22d0358000-7f22d035a000 rw-p 00000000 00:00 0 
7f22d036d000-7f22d036e000 r--p 00027000 08:01 924078                     /lib/x86_64-linux-gnu/ld-2.27.so
7f22d036e000-7f22d036f000 rw-p 00028000 08:01 924078                     /lib/x86_64-linux-gnu/ld-2.27.so
7f22d036f000-7f22d0370000 rw-p 00000000 00:00 0 
7ffe04466000-7ffe04487000 rw-p 00000000 00:00 0                          [stack]                                       //栈区
7ffe04595000-7ffe04598000 r--p 00000000 00:00 0                          [vvar]
7ffe04598000-7ffe04599000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]                                //内核空间
 

                      

6 >使用指令:pmap - d 进程id号 可以查看当前进程所有的空间分配大小

7 >案例说明
      

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

int num;         //在全局区的 .bss
static int key;      //在全局区的 .bss
int num_1 = 520;     //在全局区的 .data, 520在.ro段
char *ptr = "hello"; //ptr在全局区的.data段,"hello"在.ro段

int main(int argc, const char *argv[])
{
    int value;                 //栈区,随机值
    static int value_1;        //在全局区的 .bss段
    static int value_2 = 1314; //value_2在全局区的.data段, 1314在.ro段

    return 0;
}

四、动态内存分配和回收

1 > 对于堆区空间的数据,我们也可以利用起来,对于栈区的内存空间,会随着所在的函数空间的结束而结束

2 > 可以在堆区由程序员手动申请一片内存来使用,其生命周期到程序员手动释放该空间为止,或者整个程序结束

3 > 对堆区空间的使用函数
                                                                                                                                                                                                                                   

#include <stdlib.h>

    void *malloc(size_t size);
功能:在堆区申请出给定字节大小的空间,并返回该空间的内存起始地址
参数:要申请的空间大小,以字节为单位
返回值:成功分配空间后,返回该空间的起始地址,失败返回NULL
注意:由于返回结果是一个万能指针,想要使用该内存中的内容时,需要转换为具体的指针

void free(void *ptr);
功能:释放给定的堆区空间
参数:堆区空间的起始地址
返回值:无

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

    int main(int argc, const char *argv[])
{
    //申请1字节空间
    char *ptr = (char *)malloc(1); //在堆区申请1字节大小的空间
    //ptr占8字节,在栈区
    //malloc(1) 申请的是堆区空间

    printf("*ptr = %d\n", *ptr); //0
    *ptr = 100;                  //给堆区空间进行赋值
    printf("*ptr = %d\n", *ptr); //100

    //申请4字节空间大小
    int *qtr = (int *)malloc(sizeof(int)); //单个数据申请
    printf("*qtr = %d\n", *qtr);           //0

    //连续数据的申请
    int *dtr = (int *)malloc(sizeof(int) * 5);
    for (int i = 0; i < 5; i++)
    {
        printf("%d\t", dtr[i]);
    }
    printf("\n");

    //释放空间
    free(ptr);
    free(qtr);
    free(dtr);
    ptr = NULL;
    qtr = NULL;
    dtr = NULL;

    return 0;
}
在堆区申请5个int类型的内存空间,用于存储5个学生的成绩
    定义函数实现:
 完成学生成绩的录入、输出、排序

          

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

    //定义申请空间的函数
    int *
    create()
{
    //在堆区申请5个int类型的空间大小
    int *ptr = (int *)malloc(sizeof(int) * 5);
    if (NULL == ptr)
    {
        printf("申请失败\n");
        return NULL;
    }

    //程序执行至此,表示内存申请成功
    //给内存空间进行初始化
    memset(ptr, 0, sizeof(int) * 5);

    //将内存地址返回
    return ptr;
}

//成绩的录入
void input(int *ptr)
{
    //完成录入
    if (NULL == ptr)
    {
        printf("录入失败\n");
        return;
    }
    //正常录入工作
    for (int i = 0; i < 5; i++)
    {
        printf("请输入第%d个学生的成绩:", i + 1);
        scanf("%d", &ptr[i]);
    }
    printf("录入成功\n");
}

//输出成绩
void output(int *ptr)
{
    //判断逻辑
    if (NULL == ptr)
    {
        printf("error\n");
        return;
    }

    //正常输出
    printf("学生分数分别是:");
    for (int i = 0; i < 5; i++)
    {
        printf("%d\t", ptr[i]);
    }
    printf("输出结束\n");
}

//定义排序函数
void sort(int *ptr)
{
    //判断逻辑
    if (NULL == ptr)
    {
        printf("error\n");
        return;
    }

    //排序
    for (int i = 1; i < 5; i++)
    {
        for (int j = 0; j < 5 - i; j++)
        {
            if (ptr[j] < ptr[j + 1])
            {
                int temp = ptr[j];
                ptr[j] = ptr[j + 1];
                ptr[j + 1] = temp;
            }
        }
    }

    printf("排序成功\n");
}

//释放内存的函数
void destroy(int *ptr)
{
    //释放内存
    if (NULL != ptr)
    {
        free(ptr); //释放空间
        ptr = NULL;
    }
}

/*********************主程序***********************/
int main(int argc, const char *argv[])
{
    //调用创建函数,创建一个成绩表
    int *P = create();
    if (NULL == P)
    {
        return -1;
    }

    //调用录入函数
    input(P);

    //调用输出函数
    output(P);

    //排序函数
    sort(P);

    //输出成绩
    output(P);

    //释放内存的函数
    destroy(P);
    P = NULL;

    //调用输出函数
    output(P);

    return 0;
}

五、预处理指令

1 > 预处理指令,执行在分步编译的预处理阶段,所有的预处理指令都是以 #开头,没有分号结束

2 > 文件包含指令:相当于将引入的文件中的内容,在引入部分写了一份


1、 #include<stdio.h> : 直接使用的是系统提供的头文件 / usr / include#include "myhed.h" : 从当前路径下,找该头文件,如果有,则直接使用,如果没有再向系统库中找
2、头文件中:主要完成函数的声明、全局变量的定义、结构体类型的声

3 >宏定义指令:就是定义一个常量,用宏名表示后面的字符串,使用中,只替换不计算不做正确性检测
 

    1、#define 宏名 字符串 2、#define 宏名(参数) 含参字符串

    3、#undef 宏名 :表示取消宏定义

        

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

#define MAX 40

    int main(int argc, const char *argv[])
{
    printf("%d\n", MAX); //使用宏   40

#undef MAX
#define MAX 100

    printf("%d\n", MAX); //100

    return 0;
}

4 > 条件编译

    1、 #if 数据
        程序代码;
#endif
            判断数据是否为真,如果为真,则编译程序代码,否则不编译程序代码

    2、#if 数据
        程序代码1;
#elif
程序代码2;
#endif
判断数据是否为真,如果为真,编译程序代码1,否则编译程序代码2

3、#ifdef 宏名
    程序代码;
#endif
        判断宏明是否被定义,如果有该宏名,则编译程序代码,否则不编译

4、#ifndef 宏名
        程序代码;
#endif
判断宏明是否被定义,如果没有该宏名,则编译程序代码,否则不编译           

//防止头文件重复包含
#ifndef TEST_H
#define TEST_H

 int * create(); //创建函数的声明

void input(int *ptr); //录入函数的声明

void output(int *ptr); //输出函数的声明

void sort(int *ptr); //排序函数的声明

void destroy(int *ptr); //销毁函数的声明

int num = 520; //定义一个全局变量

#endif

5 > 分文件编译

      
完成学生信息管理系统
要求:定义一个班级,包括多个学生,以及记录实际学生的个数
                 

#define MAX 100 //最大容量
    //定义学生类型
    struct Stu
{
    char name[20];
    int age;
    double score;
};
//定义班级类型
struct Class
{
    struct Stu student[MAX]; //存放学生的容器
    int size;                //实际人数
};

1 > 完成班级的创建,创建时,需要传递班级实际人数

2 > 完成班级学生的信息录入工作

3 > 完成将班级学生按成绩进行降序排序工作

4 > 输出班级中成绩最好和最差学生的信息 5 > 完成信息的输出工作

6 > 完成班级的销毁工作
                                                                                                                                                            要求:班级创建在堆区,尽量分文件编译完成

text.h

int N=0;//菜单变量
int sum=0;//班级实际人数
void *create(int sum);//创造结构体
void in(struct Class *q);//输入
void print(struct Class *q);//输出
void ch(struct Class *q);//选择最好与最差
void paixun(struct Class *q);//降序排序
void destroy(struct Class *q);//销毁

dff.h

#define MAX 100 //班级最大值
//定义学生类型
struct Stu
{
    char name[20];//姓名
    int age;//年龄
    double score;//成绩
};
//定义班级类型
struct Class
{
    struct Stu student[MAX];       //存放学生的容器
    int size;                      //实际人数
};

y.c

#include <stdio.h>
#include <string.h>
#include "dff.h"
#include "text.h"
int main()
{
    while (1)
    {
            printf("*************菜单*************\n");
            printf("1>创建班级\n2>班级学生信息录入\n3>将班级学生按成绩进行降序排序\n4>输出班级中成绩最好和最差学生的信息\n5>输出信息\n6>销毁班级\n0>退出\n");//打印菜单
            printf("******************************\n");
            scanf("%d", &N);//接受菜单变量
        switch (N)//switch语句实现菜单功能
        {
        case 1:
            printf("请输入班级人数\n");
            scanf("%d", &sum);//接受用户输入的数字
            struct Class *q = (struct Class *)create(sum);
            q->size = sum;//sum赋值给结构体中对应变量
            break;
        case 2:
            in(q);
            break;
        case 3:
            paixun(q);
            break;
        case 4:
            ch(q);
            break;
        case 5:
            print(q);
            break;
        case 6:
            destroy(q);
            q = NULL;
            break;
        case 0:
            return 0;//结束程序
        default:
            break;
        }
    }
    return 0;
}

ee.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dff.h"
void *create(int sum)
{
    struct Class *ptr = (struct Class *)malloc(sizeof(struct Class) * sum); //定义一个结构体指针接受开辟的堆区空间
    if (NULL == ptr)                                                        //实际空间为0
    {
        printf("申请失败\n");
        return NULL;
    }
    //程序执行至此,表示内存申请成功
    //给内存空间进行初始化
    memset(ptr, 0, sizeof(int) * sum);

    //将内存地址返回
    return ptr;
}
void in(struct Class *q)
{
    for (int i = 0; i < q->size; i++) //遍历结构体的student数组
    {
        printf("请输入第%d位学生姓名\n", i + 1);
        scanf("%s", q->student[i].name);
        printf("请输入第%d位学生年龄\n", i + 1);
        scanf("%d", &q->student[i].age);
        printf("请输入第%d位学生的成绩\n", i + 1);
        scanf("%lf", &q->student[i].score);
    }
}
void print(struct Class *q)
{
    if (q == NULL) //无数据
    {
        printf("输出失败\n");
        return;
    }
    printf("姓名\t年龄\t成绩\n");     //打印开头名称
    for (int i = 0; i < q->size; i++) //遍历结构体的student数组
    {

        printf("%s\t%d\t%.2f\n", q->student[i].name, q->student[i].age, q->student[i].score); //输出各个数据
    }
}
void ch(struct Class *q)
{
    if (q == NULL) //数据为空无输出
    {
        printf("选择失败\n");
        return;
    }
    int maxi = 0, mini = 0;           //定义最大最小值小标
    for (int i = 0; i < q->size; i++) //遍历结构体的student数组
    {
        if (q->student[maxi].score < q->student[i].score)//成绩最大值比数组中数据小
        {
            maxi = i;//下标转移
        }
        if (q->student[mini].score > q->student[i].score)//成绩最小值比数组中数据大
        {
            mini = i;//下标转移
        }
    }
        printf("成绩最高学生姓名%s\t年龄%d\t成绩%.2f\n", q->student[maxi].name, q->student[maxi].age, q->student[maxi].score); //输出最好学生的信息
        printf("成绩最低学生姓名%s\t年龄%d\t成绩%.2f\n", q->student[mini].name, q->student[mini].age, q->student[mini].score); //输出最差学生的信息
    }
    void paixun(struct Class * q)
    {
        for (int i = 1; i < q->size; i++) //外循环
        {
            for (int j = 0; j < q->size - 1; j++)                  //遍历结构体的student数组
                if (q->student[j].score < q->student[j + 1].score) //读取student[j]的score与student[j+1]的score进行比较
                {
                    struct Stu temp;                       //定义一个学生类型
                    strcpy(temp.name, q->student[j].name); //将student[j]赋值给temp
                    temp.age = q->student[j].age;
                    temp.score = q->student[j].score;
                    strcpy(q->student[j].name, q->student[j + 1].name); //将student[j+1]赋值给student[j]
                    q->student[j].age = q->student[j + 1].age;
                    q->student[j].score = q->student[j + 1].score;
                    strcpy(q->student[j + 1].name, temp.name); //将temp赋值给student[j+1]
                    q->student[j + 1].age = temp.age;
                    q->student[j + 1].score = temp.score;
                }
        }
        printf("排序成功\n"); //提示成功
    }
    void destroy(struct Class * q)
    {
        //释放内存
        if (NULL != q)
        {
            free(q); //释放空间
            q = NULL;
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值