乐趣区

关于c: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 语言入门课程》。
如有错漏之处,恳请斧正。

退出移动版