C入门11自定义数据类型

Posted bryson

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C入门11自定义数据类型相关的知识,希望对你有一定的参考价值。

Summary

1)typedef并不是创建了一个新类型,而是给一个已有类型创建了一个新的名字

2)typedef主要用来简化类型名统一定义变量的方式(重命名函数和数组)

3)struct用于自定义新类型,能够将不同数据类型的变量组成一个集合。struct创建新类型是一条合法的语句,因此要以分号;结束,里面的每个定义变量的语句,也都是以分号;结束。

4)stuct结构体类型可以先声明再定义(声明只是告诉编译器结构体类型的新名字是什么,所以不定义也可以)。
如果只是前置类型声明,但是还没有定义,这时候只能定义指针(因为在定义变量的时候需要分配内存,只有类型无法确定变量大小;但可以定义指针,因为指针固定大小4字节或8字节)。

5)struct结构体类型可以省略类型名,匿名结构体。两个匿名结构体的类型即使看起来一样,但仍然是不同的两个结构体类型

6)struct结构体变量的本质是变量的集合,里面的成员占用独立的内存

7)union联合体也是C语言中用于自定义类型的一个关键字,用法和struct很像。union与struct的不同之处在于:

  • 不管union XX{}; 中有几个变量,所有的成员都共享一段内存,所有成员的起始地址都是相同
  • union XX{}; 占用的内存大小取决于成员的最大类型
  • union XX{}; 类型的变量只能以第一个成员类型的有效值进行初始化
  • 当取不同联合体里不同变量的值时,要以相应的类型去解读这一段内存

8)小端系统:低地址内存存储低位数据;大端系统:低地址处存储高位数据;
union联合体可以用于判断系统的大小端

int isLittleEndian()
{
   union 
   {
       int i;
       char c;
   } test = {0};
   
   test.i = 1;   // 如果是小端系统,低地址就存低位数据,1就会存在低地址处
   
   return (test.c == 1);  // c只占一个字节,从低地址处取一个字节,应该得到1
}

9)enum是C语言中的自定义类型关键字,能够定义整形常量的集合类型。特点:第一个枚举常量的默认值为0;可以对任意枚举常量指定值(只能指定为整形);后续常量的值默认在前一个值的基础上+1
enum类型的变量的本质仍然是一个整形变量。所以enum类型变量的大小为4
C语言中可以用任意一个整形值去初始化枚举变量(在C++中就必须用枚举值进行初始化,强类型)

10)现代程序设计中,内存使用的最小单元是字节;有一些特定场合,可以将比特位作为最小单元使用内存:位域能够指定成员变量占用内存的比特位宽度

11)深入位域位域的本质仍然是一个结构体类型

  • 位域成员必须是整型,默认情况下成员依次排列

    struct Bits1
    {
      int a : 16;
      short b : 8;
      char c : 8;
      // float f : 16;  // error, bit-field ‘f’ has invalid type\'位域的成员必须是整型!\'
      float f;  // 如果不使用 : 指定位域宽度,那么f就是一个合法的结构体成员
    };
    sizeof(Bits1) = 8; // a:2, b:1, c:1, f:4
  • 位域成员占用的位数不能超过类型宽度

    struct Bits2
    {
      char c : 16;  // error: width of ‘c’ exceeds its type
    };
  • 当存储位不足时,自动启用新单元

    struct Bits3
    {
      char a : 7;
      char b : 6;
    };
    sizeof(Bits3) = 2; // a占一个字节,b在a的字节里放不下了,只能用新的一个字节
    
    // 如果Bit3中再加一个char c;
    char c : 2;  // 此时Bit3大小为2;因为c可以放到b所在的那个字节
    char c : 3;  // 此时Bit3大小为3;因为c放到b所在的字节超出了,须启用新单元
  • 可以使用位域宽度0来表示舍弃前面未使用的位,新启用一个字节

    struct Bits4
    {
      char a : 7;
      char   : 0;  // 舍弃前面未使用的位
      char b : 1;
    };
    sizeof(Bits4) = 2; // 中间的\'char : 0\'表示舍弃了a所在单元中剩下的位,在启用新的一个字节,所以大小为2;
    
    // 如果去除中间的\'char : 0\',则大小为1
    
    // 如果Bit3中再加一个char c;
    char c : 2;  // 此时Bit3大小为2;因为c可以放到b所在的那个字节
    char c : 3;  // 此时Bit3大小为3;因为c放到b所在的字节超出了,须启用新单元

1、typedef

问题:C语言中并没有提供字节类型,如果我们需要一个字节类型该怎么办?

类型命名关键字(typedef

  • C语言中可以对类型赋予新名字
  • 语法:typedef Type NewTypeName;

    • 注意:typedef并没有创建新类型,只是创建了类型别名

      typedef unsigned char byte;
      
      byte b = 128;
      char c = 128;

typedef的应用:

  • typedef常用语简化类型名,如

    typedef unsigned char uint8;
    typedef int int32;
    typedef unsigned long long uint64;
    ...
  • typedef常用于定义类型名,能够以统一的方式创建变量(Type var; 如以ArrName arr;的形式来定义数组)

    typedef float(FArr5)[5];         // FArr5是一个数组类型:元素个数为5,元素类型是float
    typedef int(IFuncII)(int, int);  // IFuncII是一个函数类型:返回值是int,参数列表是(int, int)
    
    float _gArr[5] = {0};
    
    int add(int a, int b) {return a +b; }
    
    FArr5* pa = &_gArr;   // 定义数组指针
    IFuncII* pf = add;    // 定义函数指针
    
    int i = 0;
    for(i = 0; i<5; i++)
      printf("%f\\n", (*pa)[i]);  // 使用数组指针访问数组
      
    printf("%d\\n", pf(i, i+1));  // 使用函数指针调用函数

2、struct

问题:C语言中是否可以创建不同数据类型变量的有序集合?

2.1 结构体

  • struct是C语言中自定义类型的关键字
  • struct能够定义不同数据类型变量集合类型

    • 语法

      struct TypeName
      {
         Type1 var1;
         Type2 var2;
         ...
      };

语法示例:

struct Student
{
    char name[20];
    int id;
    short major;
};

struct Student s1 = {"Zhangsan", 908, 1};   // 结构体变量初始化列表

printf("s1.name = %s\\n", s1.name);
printf("s1.id = %d\\n", s1.id);
printf("s1.major= %d\\n", s1.major);

深入struct结构体类型

  • struct结构体变量的本质是变量的集合
  • struct结构体变量中的成员占用独立的内存
  • struct结构体类型可以用typedef赋予新类型名
  • 可以定义struct结构体类型的指针,并指向对应类型的变量
typedef struct Student Stu;   // 1)struct Student还没有定义,就在前面定义新名字合理么?

struct Student
{
    char name[20];
    int id;
    short major;
};

// main
Stu s = {{0}};     // 2)这种初始化方式可以么?
Stu* ps = &s;

strcpy(ps->name, "Lisi");
ps->id = 1;
ps->major = 825;

// 释疑
// 1)typedef struct Student Stu;只时一个声明,仅仅是声明struct Student这个类型可以用Stu来表示,所以不需要struct Student类型已经定义了
// 2)Stu s = {{0}};其实等价于Stu s = {{0}, 0, 0}。第一个成员用{0}初始化,后面没给的都用0初始化,类似于数组的初始化。字符串的本质是一个字符数组,所以可以用数组初始化的方式,用{0}初始化name。

struct可以定义无名结构体类型:

struct
{
    char name[20];
    int id;
} noname;

下面的代码是否正确?

struct { int a, b; } v1;
struct { int a, b; } v2; 
struct { int a, b; }* pv;

v1.a = 1;  v1.b = 2;

v2 = v1;
pv = &v2;

这段代码在gcc编译时就会报错

v2 = v1;  // error: \'incompatible types\' when assigning to type \'struct <anonymous>\' from type \'struct <anonymous>\'
pv = &v2; // warning: assignment from \'incompatible pointer type\'

编译器的报错指出,v1 v2 *pv都是不同的结构体类型,不能直接进行相互赋值。说明虽然v1和v2这两个匿名结构体看起来类型是一样的,存的变量也都一样,但实际上是不同的类型

2.2 位域

在现代程序中,内存使用的最小单位为字节;在一些特定场合,可以将比特位作为最小单位使用内存;结构体能够指定变量占用内存的比特位宽度(位域)

  • 位域示例

    struct BW
    {
      unsigned char a : 4;  // a占用一个字节的4位宽度
      unsigned char b : 2;  // b占用一个字节的2位宽度
      unsigned char c : 2;  // c占用一个字节的2位宽度
    };
    
    struct BW bw;
    
    bw.a = 15; bw.b = 1; bw.c = 3;
    
    printf("sizeof(struct BW) = %d\\n", sizeof(bw));  // 输出1

深入位域:

  • 位域成员必须是整型,默认情况下成员依次排列

    struct Bits1
    {
      int a : 16;
      short b : 8;
      char c : 8;
      // float f : 16;  // error, bit-field ‘f’ has invalid type\'位域的成员必须是整型!\'
      float f;  // 如果不使用 : 指定位域宽度,那么f就是一个合法的结构体成员
    };
    sizeof(Bits1) = 8; // a:2, b:1, c:1, f:4
  • 位域成员占用的位数不能超过类型宽度

    struct Bits2
    {
      char c : 16;  // error: width of ‘c’ exceeds its type
    };
  • 当存储位不足时,自动启用新单元

    struct Bits3
    {
      char a : 7;
      char b : 6;
    };
    sizeof(Bits3) = 2; // a占一个字节,b在a的字节里放不下了,只能用新的一个字节
    
    // 如果Bit3中再加一个char c;
    char c : 2;  // 此时Bit3大小为2;因为c可以放到b所在的那个字节
    char c : 3;  // 此时Bit3大小为3;因为c放到b所在的字节超出了,须启用新单元
  • 可以使用位域宽度0来表示舍弃前面未使用的位,新启用一个字节

    struct Bits4
    {
      char a : 7;
      char   : 0;  // 舍弃前面未使用的位
      char b : 1;
    };
    sizeof(Bits4) = 2; // 中间的\'char : 0\'表示舍弃了a所在单元中剩下的位,在启用新的一个字节,所以大小为2;
    
    // 如果去除中间的\'char : 0\',则大小为1
    
    // 如果Bit3中再加一个char c;
    char c : 2;  // 此时Bit3大小为2;因为c可以放到b所在的那个字节
    char c : 3;  // 此时Bit3大小为3;因为c放到b所在的字节超出了,须启用新单元

3、union

联合体 union

  • union是C语言中的自定义类型关键字
  • union是struct的兄弟关键字,用法上非常相似
  • 语法:

    union TypeName
    {
      Type1 var1;
      Type2 var2;
      ...
    };

union与struct的不同:

  • union类型所有成员共享一段内存所有成员起始地址相同
  • union类型的大小取决于成员的最大类型
  • union类型的变量只能以第一个成员类型的有效值进行初始化

union使用示例:

union Test
{
    int a;
    float f;
};

union Test t = {10};  // \'只能以第一个成员类型的有效值进行初始化\'

printf("sizeof(union Test) = %d\\n", sizeof(t));  // 4, \'大小取决于成员的最大类型\'
printf("&t.a = %p\\n", &t.a);  // 0xbfb21f78 
printf("&t.f = %p\\n", &t.f);  // 0xbfb21f78, \'共享一段内存\' \'所有成员起始地址相同\'

printf("t.a = %d\\n", t.a);    // 10, 
printf("t.f = %f\\n", t.f);    // 0.000000, 以浮点数类型去解读这个内存,自然不是预期的(浮点数在内存中的表示方式和整形不一样,详见C进阶)

t.f = 987654321.0f;
printf("t.a = %d\\n", t.a);    // 1315666339,同上,以整形的方式去解读一个表示浮点数的内存,自然不是预期的 
printf("t.f = %f\\n", t.f);    // 987654336.000000,\'浮点数在内存中的表示本就是不精确的\',所以也不是预期的完全一致的(详见C进阶)

4、enum

C语言中的枚举类型 enum

  • enum是C语言中的自定义类型关键字
  • enum能够定义整形常量的集合类型
  • 语法:

    enum TypeName
    {
      IntConst1,
      IntConst2,
      ...
    };

enum注意事项:

  • 第一个枚举常量的默认值为0
  • 后续的常量值在前一个常量值的基础上加1
  • 可以对任意枚举常量指定整型值(只能指定整型值

    enum Day{MON = 1, TUE, WED, THU, FRI, SAT, SUN};
    enum Season{Spring, Summer = 3, Autumn, Winter = -1};

本文总结自“狄泰软件学院”唐佐林老师《C语言入门课程》。
如有错漏之处,恳请指正。

以上是关于C入门11自定义数据类型的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段11——vue路由的配置

VSCode自定义代码片段11——vue路由的配置

将vscode打造成无敌的IDE添加自定义的snippet

将vscode打造成无敌的IDE添加自定义的snippet

VSCode自定义代码片段——git命令操作一个完整流程

Android - 如何将自定义对象传递给片段