C语言构造类型、共用体、枚举及动态内存分配

构造类型
  • 结构体:

    • 类型描述:结构体的类型描述不占空间,因此不能在结构体定义过程中进行初始化。
      struct 结构体名{
        数据类型 成员1;
        ....;
      };//别丢掉分号
      
    • 嵌套定义的实现:
      struct father_infro{
          int age;
          char* name
      };
      struct std_infro{
          int age;
          char* name;
          struct father_infro father;
      };//别丢掉分号
      //也可以如下定义
      struct std_infro{
          int age;
          char* name;
          struct father_infro{
          int age;
          char* name
      }father;
      };
      
    • 定义变量(变量,数组,指针),初始化及成员引用
      • 三种引用:
        • 变量名.成员名
        • 指针->成员名
        • (*指针).成员名
      //=========================变量定义==============================//
      struct std_infro a = {12,"you",{33,"you father"}};
      //a其实也是结构体的起始地址
      //如果想仅初始化一部分则如下:
      //struct std_infro a = {.name = "you", .father_infro.age = 35};
      printf("a.age = %d", a.i);
      //普通成员引用:变量名.成员名
      printf("a.father.name = %s", a.father.name);
      //嵌套成员引用:变量名.变量名.成员名
      //=========================变量定义==============================//
      //=========================指针定义==============================//
      struct std_infro a = {12,"you",{33,"you father"}};
      struct* std = &a;
      printf("a.age = %d", a->i);
      printf("a.father.name = %s", a->father.name);
      //为何father不用指针?--- 观察到,father并非指针类型
      //=========================指针定义==============================//
      //=========================数组定义==============================//
      struct std_infro a[2] = {{12,"you",{33,"you father"}}, {15,"me",{35,"my father"}}};
      struct std_infro* p = a;
      //=========================数组定义==============================//
      
    • 结构体所占内存大小:
      • 结构体开辟占用空间时存在内存对齐问题

      • 为什么要字节对齐?

        • 主要是为了提高内存的访问效率,比如intel 32位cpu,每个总线周期都是从偶地址开始读取32位的内存数据,如果数据存放地址不是从偶数开始,则可能出现需要两个总线周期才能读取到想要的数据,因此需要在内存中存放数据时进行对齐。
      • 如何计算对齐后的所占大小?公式中0可以除任何值

        • 公式:当前存放地址addr能整除sizof(当前变量)则存入,不然地址进行+1偏移
          在这里插入图片描述
      • 因此我们需要注意结构体在传给对方数据时候不能做对齐,如下做

      struct 结构体名{
        数据类型 成员1;
        ....;
      }__attribute__((packet));//这样就是各个成员的累加和
      
    • 结构体的传参问题:
      • 传参方式:直接传参,间接传参
      • 为什么更希望用指针传参?
        • 函数接受参数后,形参也需要开辟和实参一样的大小空间进行零时存储,那么显然,如果直接出入结构体,则形参的临时大小完全取决于实参的大小,这样做无疑会浪费存储空间,而指针传参的最大优点在于,其开辟临时内存大小只能是当前环境的一个指针的开销。
    • 有名结构体和无名结构体的区别?
      • 无名结构体一定要把所有变量定义出来,不然以后找不到名字,无法进行后续的定义。
  • 共用体

  • 产生及意义:多个成员共用一部分内存—根据成员中最大的一个进行内存分配—在某一时间段只能有一个成员占用

    • 理解:对于一个注册系统,可以允许多个用户同时注册(结构体),但每个用户的注册性别只能是一种性别(共用体)
  • 类型描述:

    union 共用体名
    {
        数据类型 成员名1;
        数据类型 成员名2;
        ...
    };
    
  • 嵌套定义:

    union union_struct{
        int num1;
        float num2;
        char num3;
        struct{
            int arr[10];
            float f;
        }infro;
    };//占内存大小为44个字节
    union struct_union{
        int num1;
        float num2;
        char num3;
        union {
            int a;
            char f;
        }infro;
    };//不考虑地址对齐占内存大小为10个字节
    
  • 嵌套解决的问题:

    • 什么是大端格式什么是小端格式?
      • 大端:数据低位保存在高地址中 C51
      • 小端:数据的低位保存在低地址中 ARM
    • 需求:试问一个数的高16位和低16位的和是什么?//使用共用体,不要用位运算
    union 
    {
        struct 
        {
            uint16_t i;
            uint16_t j;
        }x;
        uint32_t y;
    }a;
    int main()
    {
        a.y = 0x11223344;
        printf("%x\n", (a.x.i + a.x.j));
        //输出4466
        //解释--因为union中struct和uint32均占用了4个字节,虽然给y进行了赋值,
        //但是由于两者其实是同时处在一个存储地址中,因此可以直接读取,这是存储的特点决定的
        return 0;
    }
    
  • 定义变量(变量,数组,指针),初始化及成员引用:

    union test_union{
        int num1;
        float num2;
        char num3;
    };
    int main()
    {
        union test_union test;
        union test_union* p;
        union test_union arr[3];
        test.num1 = 3;
        p = &test;
        p->num3 = 'h';
        printf("sizof(test) = %d\n", sizeof(test));
        //共用体的内存分配是按照最大类型分配的---output:4
        printf("num1 = %d\n", test.num1);
        //output:3
        printf("num2 = %f\n", test.num2);
        //output:0.000000---无意义
        printf("num3 = %c\n", p->num3);
        //output:0.000000---无意义
        return 0;
    }
    
  • 占用内存大小

  • 函数传参(值,地址)

  • 位域:

    • 共用体的一种形式—存储上以位bit来占位
    union test_union{
        struct {
          char i:1;//存储几个bit可以指定
          char j:2;
        }a;
        char b;
    };
    
  • 枚举—一种特定情况的集合

  • 定义:

    • enum 标识符{
      成员1;
      成员2;

      };
    enum Gender{
        MALE,
        FEMALE
    };//MALE默认0,FEMALE默认1
    //可以给任意一个成员直接赋值,后面的值依次加1,成员的赋值也是可以重复的
    int main()
    {
      enum Gender xiaoming = MALE;
      //xiaoming = FEMALE;---error写法
    }
    
  • 常见使用:将enum当作一连串的宏来使用—将其当作有值的宏处理

    • 但是这不意味着他可以替代宏—宏可以传参等功能
enum State{
    STATE_RUNNING = 1,
    STATE_CANCELED,
    STATE_OVER = 0
  };//表示当前状态
struct StateMachine{
    int state;
    int num;
};
int main()
{
    struct StateMachine Project1;
    int i;
    for(i = 0; i < 3; i++){
      Project1 = i;
      switch(Project1.state)
      {  
        case 0:       
          Project1.state = STATE_RUNNING;
          printf("Now_State = %d\n", Project1.state);
          break;
        case 1:       
          Project1.state = STATE_CANCELED;
          printf("Now_State = %d\n", Project1.state);
          break;
        case 2:       
          Project1.state = STATE_OVER;
          printf("Now_State = %d\n", Project1.state);
          break;
        default:
          Project1.state = STATE_OVER;
          printf("Now_State = %d\n", Project1.state);
          break;
      }
    }
}
动态内存分配
  • auto类型—栈上分配 动态内存—堆上分配
  • 调用库函数:#include<stdlib.h>
  • malloc:一次申请的总字节数—失败返回NULL
    • void* malloc(size_t size);//堆上分配后给void* size计算是字节大小
  • calloc:连续申请多少个字节—失败返回NULL
    • void* calloc(size_t nmemb, size_t size);//想分配nmemb个成员,每个成员大小size
  • realloc:重新分配动态内存空间
    • void* realloc(void *ptr, size_t size);
    • 解释:ptr必须是malloc和calloc调用返回的指针,重新分配size大小的空间
  • free:
    • void free(void *ptr);
  • 原则:谁申请谁释放—防止内存泄漏
  • 面试问题
void func(int* p, int length){
  p = malloc(sizof(int)*length);
  if(p == NULL){
    exit(1);
  }
  return ;
}
int main()
{
  int length = 100;
  int *p = NULL;
  func(p, num);
  free(p);
  return 0;
}
/*
该代码出现什么问题了么?
已经产生了内存泄漏原因如下
1.main中的p和func中的p本质上是 两个 指针变量,只不过func中的p赋值了main中p的数值
2.在func中,指针p由于malloc而被指向了新的区域,并开辟了空间
3.main中的free无意义,因为main中的p一直都指向NULL并没有改变过
代码改进如下
*/
/*********************方法一********************/
void func(int* *p, int length){
  //int** p 表示 一个指针p指向int*类型的指针
  *p = malloc(sizof(int)*length);
  if(p == NULL){
    exit(1);
  }
  return ;
}
int main()
{
  int length = 100;
  int *p = NULL;
  func(&p, num);//p也是一个变量取它的地址就可以对它进行修改
  free(p);
  return 0;
}
/*********************方法一********************/
int* func(int* p, int length){
  *p = malloc(sizof(int)*length);
  if(p == NULL){
    exit(1);
  }
  return p;
}
int main()
{
  int length = 100;
  int *p = NULL;
  p = func(p, num);//更新p
  free(p);
  return 0;
}
一个关键字的讲解 typedef
  • 作用:为已有的数据类型改名—对于不同位数的机器端可以规避同一类型位数的不同
  • 格式:typedef 数据类型 新名称; typedef int INT
  • 与define的区别:
    .#define IP int* 定义—IP p,q; —> int *p,q;
    .typedef int *IP; 定义—IP p,q; —> int *p, *q;
  • 对于数组的定义:typedef int ARR[6] —> 理解为int [6] -> ARR — ARR a; --> int a[6];
  • 对于结构体的定义:
typedef struct{
  int i;
  float f;
}NODE,*NODEP;//struct被NODE代替 struct* 被NODEP代替
NODE p1  ---  代表p1是一个结构体
NODEP p2  ---  代表p2是一个指向结构体的指针
  • 对于函数的定义:
typedef int FUNC(int); FUNCPP p// --->   int(int)的函数被代替为FUNC
typedef int *FUNCP(int); FUNCPP p// ---> int* (int)的函数被替代为FUNCP 给指针函数改名
typedef int *(*FUNCPP)(int); FUNCPP p// ---> int* (*p)(int)FUNCPP是一个指向指针函数的指针 给函数指针改名
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值