自定义类型:结构体、枚举、联合

结构体:

所谓结构体,就是将一大堆值放在一起,创建一个新的类型,这些成员可以是不同类型的变量。

struct tag
{
member-list;
}variable-list;

tag:   要求:1.见名知意; 2.可以省略; 3.不建议省略;

member-list: C语言中,不能为空;

variable-list: 变量列表,可以省略,建议省略;

结构体之间不能互相复制;例如:

struct 
{
 int a;
 char b
 float c;
}x;
struct
{
 int a;
 char b;
 float c;
}a[20],*p;

p = &x;    (错误!)

结构体地址 = 首元素地址(在数值上)

结构体内元素地址递增;

结构体成员访问:

结构体变量的成员是通过点操作符(.)访问的;

例如:

struct
{
 char name[20];
 int age;
};
struct S s
strcpy(s.name,"zhangsan");
s.age = 20;

或者用指针访问:

(*p).a =

  (*p).b =

或:

p->a =

p->b =

void print(struct S* ps)
{
 printf("name = %s",(*ps).name);
 printf("name = %s",ps->name);
}

结构体的自引用:

正确方式(必须用指针):

struct Node
{
 int data;
 struct Node* next;
};

也可以对结构体进行重命名:

typedef ……

结构体的不完整声明,结构体之间可以互相引用吗,答案是肯定的,不过,要提前声明才行;

struct B
struct A
{
 int _a;
 struct B* pb;
};
struct B
{
 int _b;
 struct A* pa;
};

结构体与数组一样,不能被整体赋值,但可以被初始化;

结构体的内存对齐:

即结构体的大小计算:

首先,了解一下对齐规则:

1.第一个成员在与结构体变量偏移量0的地址处;

2.其他成员变量要对其到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值。(VS中默认的值是8,linux中默认的值是4)

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍;

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍;

那为什么存在内存对齐呢?

1.平台原因(移植原因):

2.性能原因:

总之,结构体对齐是拿空间来换取时间的做法;

修改对齐数:

#pragma pack( )——修改默认对齐数;

设置为1,2,4,8……

空白表示恢复默认;

结构体传参:

结构体传参不发生降维,不过,结构体传参尽量不传结构体变量,而传结构体指针;

struct S
{
 int data[1000];
 int num;
};
struct S s = {{1,2,3,4},1000};
void print(struct S* ps)
{
 printf("%d\n",ps->num);
}
int main()
{
 print(&s);
 return 0;
}

位段:

1.位段的成员必须是 int unsigned int 或singed int

2.位段的成员名后边有一个冒号和一个数字;

比如:

struct A
{
 int _a:2;
 int _b:5;
 int _c:10;
 int _d:30;
};

这个数字表示 占用的byte位

位段采用压缩存储;

位段的内存分配:

1.位段的类型可以是int  unsigned int  signed int 或者是 char 类型;

2.位段上的空间是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的;

3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植性的程序应该避免使用位段;

位段的跨平台问题:

1.int 位段被当成有符号数还是无符号数是不确定的;

2.位段中最大位的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。)

3.位段中的成员在内存中从左向右分配还是从右向左分配,标准尚未定义;

4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的;

枚举:

枚举——顾名思义就是一一列举;

枚举内的内容都是常量;

例如:

enum Day //星期
{
   Mon,
   Tues,
   Wed,
   Thur,
   Fri,
   Sat,
   Sun
};

这些枚举{ }中的内容是枚举类型的可能取值,也叫枚举常量,这些可能取值都是有值的,默认从0开始,依次递增1,当然也可以自行赋值;

枚举的优点:

1.增加代码的可读性和可维护性;

2.和#define定义的标识符比较枚举有类型检查,更加严谨;

3.防止了命名污染;

4.便于调试;

5.使用方便,一次可以定义多个常量;

联合:

联合的成员是共用同一块内存空间的,所以一个联合体的大小,至少是最大成员的大小;

例如:面试题 判断当前计算机的大小端;

union Un
{
  int i;
  char c;
};
union un;
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n",un.i);

运行上述代码,若输出结果为55223344,则该计算机为小端,若输出结果为11223355;则该计算机为大端;

联合大小计算:

联合体的大小至少是最大成员的大小;

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍;

联合体和结构体的巧妙运用:

//将long类型的IP地址,转换为点分十进制的表示形式;

union ip_addr
{
  unsigned long addr;
  struct
  {
    unsigned char c1;
    unsigned char c2;
    unsigned char c3;
    unsigned char c4;
  }ip;
};

union ip_addr my_ip;
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n",my_ip.ip.c4,my_ip.ip.c3,my_ip.ip.c2,my_ip.ip.c1);









猜你喜欢

转载自blog.csdn.net/ChenGX1996/article/details/80473861