C语言-结构体、共用体,内存管理

结构体

  • 结构体的定义及变量使用

    #include <stdio.h>
    #include <string.h>
    struct student
    {
        char name[20];
        int age;
        char sex;
    }stu3; // 定义结构体的同时定义结构体变量。此时是全局变量
    
    int main(int argc, char const *argv[])
    {
        struct student stu1, stu2; // 定义结构体变量
        strcpy(stu1.name, "zs");
        stu1.age = 20;
        stu1.sex = 'M';
    
        stu2 = stu1;            // 同类型结构体可以赋值
        strcpy(stu2.name, "ls");
    
        stu3 = stu1;
        strcpy(stu3.name, "ww");
    
        // 不能把它作为一个整体参加数据处理,参加各种运算和操作的是结构体变量的成员项数据
        printf("%s,%d,%c\n", stu1.name,stu1.age,stu1.sex);
        printf("%s,%d,%c\n", stu2.name,stu2.age,stu2.sex);
        printf("%s,%d,%c\n", stu3.name,stu3.age,stu3.sex);
    }
    
  • 结构体变量初始化

    #include <stdio.h>
    #include <string.h>
    struct student
    {
        char name[20];
        int age;
        char sex;
    }stu2 = {"ls",20,'M'}; // 变量初始化
    
    int main(int argc, char const *argv[])
    {
        struct student stu1 = {"zs",18,'M'}; // 变量初始化
    
        // struct student stu3;   
        // stu3 = {"zs",18,'M'}; // 错误写法
    
        printf("%s,%d,%c\n", stu1.name,stu1.age,stu1.sex);
        printf("%s,%d,%c\n", stu2.name,stu2.age,stu2.sex);
    }
    
  • 结构体作为成员变量

    #include <stdio.h>
    #include <string.h>
    struct birthday
    {
        int year;
        int month;
        int day;
    };
    
    struct student
    {
        char name[20];
        struct birthday date;  // 结构体作为成员变量
        char sex;
    }stu1={"zs",{2000,05,20},'M'};
    
    int main(int argc, char const *argv[])
    {
        struct student stu2;
        stu2 = stu1;
        stu2.date.year = 1999;
    
        printf("%s,%d-%d-%d,%c\n", stu1.name,stu1.date.year,stu1.date.month,stu1.date.day,stu1.sex);
        printf("%s,%d-%d-%d,%c\n", stu2.name,stu2.date.year,stu2.date.month,stu2.date.day,stu2.sex);
    }
    
  • 无名结构体

    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
        char name[20];
        struct {       // 无名结构体
            int year;
            int month;
            int day;
        }date;
        char sex;
    }stu1={"zs",{2000,05,20},'M'};
    
    int main(int argc, char const *argv[])
    {
        printf("%s,%d-%d-%d,%c\n", stu1.name,stu1.date.year,stu1.date.month,stu1.date.day,stu1.sex);
    }
    
  • 结构体数组

    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
        char name[20];
        int age;
        char sex;
    }stu1[5]={"zs",20,'M'};
    
    int main(int argc, char const *argv[])
    {
        struct student stu2[5] = {{"zs",20,'M'},{"ww",19,'M'}};
        int i;
        for (i = 0; i < sizeof(stu2)/sizeof(struct student); ++i)
        {
            printf("%s,%d,%c\n", stu2[i].name,stu2[i].age,stu2[i].sex);
        }
    }
    
  • 结构体指针

    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
        char name[20];
        int age;
        char sex;
    };
    
    int main(int argc, char const *argv[])
    {
        struct student stu[5] = {{"zs",20,'M'},{"ww",19,'M'}};
        struct student * p; // 结构体指针
    
        p = stu;
    
        int i;
        for (i = 0; i < sizeof(stu)/sizeof(struct student); ++i)
        {
            printf("%s,%d,%c\n", (*p).name, p->age, p->sex); // 结构体指针访问成员变量
            p++;
        }
    }
    

共用体

  • 概念:
    • 在C语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体。共用体在定义、说明和使用形式上与结构体相似。两者本质上的不同仅在于使用内存的方式上。
    • 由于共用体中各成员的数据长度往往不同,所以共用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间。
      #include <stdio.h>
      #include <string.h>
      
      union student
      {
          char name[20];
          int age;
          char sex;
      };
      
      int main(int argc, char const *argv[])
      {
          union student stu;
          strcpy(stu.name, "zs");
          stu.age = 20;
          stu.sex = 'M';
          printf("%s,%c,%c\n", stu.name, stu.age, stu.sex);  // M,M,M
          printf("%d\n", sizeof(union student)); // 20
      
          // union student stu3[5] = {{"zs",20,'M'},{"ww",19,'M'}};  // 报错
          // union student stu2 = {"zs",20,'M'};   // 报错
      }
      

typedef

  • typedef 定义别名

    #include <stdio.h>
    #include <string.h>
    
    typedef struct student
    {
        char name[20];
        int age;
        char sex;
    }student,STUDENT,*pStu; // 两个结构体别名,一个指针结构体别名
    
    int main(int argc, char const *argv[])
    {
        student stu; //等价于  struct student stu
        strcpy(stu.name, "zs");
        stu.age = 20;
        stu.sex = 'M';
    
        pStu p = &stu; // 等价于 struct student *p = &stu;
        printf("%s\n", p->name);
    
        printf("%s,%d,%c\n", stu.name, stu.age, stu.sex);
    }
    
    #include <stdio.h>
    #include <string.h>
    
    struct student
    {
        char name[20];
        int age;
        char sex;
    };
    typedef struct student student;
    
    int main(int argc, char const *argv[])
    {
        student stu; //等价于  struct student stu
    }
    

内存管理

  • C语言定义了4个内存区间:

    • 代码区
    • 全局变量与静态变量区
      • 通常定义变量,编译器在编译时都可以根据该变量的类型知道所需内存空间的大小,从而系统在适当的时候为他们分配确定的存储空间。
    • 局部变量区即栈区
      • 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
    • 动态存储区,即堆区
      • 有些操作对象只有在程序运行时才能确定,这样编译器在编译时就无法为他们预定存储空间,只能在程序运行时,系统根据运行时的要求进行内存分配。
      • 从堆上分配,亦称动态内存分配。
  • 动态内存管理malloc/free
    void * malloc(size_t num)

    1. malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。 
    2. malloc申请到的是一块连续的内存,有时可能会比所申请的空间大。其有时会申请不到内存,返回NULL。
    3. malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void * 转换成所需要的指针类型。 
    4. 如果free的参数是NULL的话,没有任何效果。
    5. 释放一块内存中的一部分是不被允许的。
    

    void free(void *p)

    1. 删除一个指针p(free(p);),实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针
    2. malloc与free是配对使用的, free只能释放堆空间。如果malloc返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存malloc返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
    
    #include <stdio.h>
    #include <stdlib.h>
    
    char * get_string(){
        char * s;
        s = (char *)malloc(10 * sizeof(char));  // 堆空间
        scanf("%s", s);
        // s = "wecolme";    // 字符串常量,静态变量区。此时free(p)报错
        return s;
    }
    
    int main(int argc, char const *argv[])
    {
        char * p;
        p = get_string();
        printf("%s\n", p);
    
        free(p);             // 释放堆空间
        p = NULL;            // p指针占用内容在栈空间。不置为空指针的话,就是空悬指针(野指针)
    
        return 0;
    }
    
  • 野指针
    不是NULL指针,是指向“垃圾”内存的指针。“野指针”是很危险的。
    野指针的成因主要有两种:
    1. 指针变量没有被初始化。
    2. 指针p被free之后,没有置为NULL,让人误以为p是个合法的指针。
    3. 指针操作超越了变量的作用范围。

    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char const *argv[])
    {
        char * p; // p指针未初始化,此时指向的是一个随机的地址,直接使用将报错
        
        strcpy(p,"hello");
    
        char * q; // p指针未初始化,此时指向的是一个随机的地址,直接使用将报错
        *q = 'A';
    
        return 0;
    }
    
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char const *argv[])
    {   
        char s[] = "wel";
        char * p;
    
        p = s;
        strcpy(p,"welcome"); // 指针操作超越了变量的范围
    
        return 0;
    }
    
posted @ 2023-09-08 20:34  扬帆去远航  阅读(73)  评论(0编辑  收藏  举报