0.问题引入
         C语言是允许程序员定义自己的组合类型
                        构造类型  =》 数组
                                               结构体
                                               枚举
                                               共用体/联合体
             现实世界的物体需要抽象成计算机语言中的数据类型(对象)
             学生:
                     学号 ==> int num;
                     姓名 ==> char name[32];
                     性别 ==> char gender;
                     年龄 ==> int age;
                     .....
                 把这些物体的属性组合到一个数据类型中去,用来表述“学生”这样的类型
                        结构体
                       
1.结构体: 自定义的一种类型
1.1 struct 结构体名
               {
                     成员类型1   成员名1;
                     成员类型2   成员名2;
                     ....
                     成员类型n   成员名n;
                };
                结构体类型是由多个成员构成,而且每个成员都有自己的类型(只要是合法的类型都可以)
                不同的成员,类型可以相同,但是成员名不能相同!!!
                
                struct 结构体名 ==> 定义的新类型的名字。
                
                struct student
                {
                 ......
                };
                struct student 是我们新定义的类型名,
                       这样的类型是用来描述”学生“的
                     
                      定义变量:
                              变量的类型 变量名;
                              struct student s ; // s就是一个结构体变量
                                                 // typeof(s) ==> struct student
                     
                     "成员类型1,成员类型2....." ;
                             只要是c语言中合法的类型都可以。
                             可以是基本类型/构造类型/指针......
                     “成员名1,成员名2....“
                             只要符合c语言标识符的规定,成员名之间不能相同。
                             
                 例子:
                        构造一个”学生“这样的结构体类型。
                        ”学生“:
                                学号,姓名,性别,年龄,成绩
                             struct student
                             {
                                 int num; //学号
                                 char name[32];//姓名
                                 char gender; // 性别
                                 int age ; // 年龄
                                 float score ;// 成绩
                             };
                             上面的语句,构造了一个新的类型 :struct student
                             并且这个新类型里面包含了 num ,name,gender,age,score这些成员变量。
                             
                                  如果要定义一个新类型的变量s(结构体变量)
                                  变量的类型 变量名 {=初始值};
                                  struct student s { = 初始值};
                                  此时,s里面会有一个int类型的num,name.....
                                 
                             问题:   这些成员变量在内存中是如何分布的呢?
                            
1.2结构体成员的内存布局
                (1)结构体类型所占空间是各个成员变量所占空间之和
                 (2)结构体内各个成员变量按他们定义时出现的次序,依次排列              
1.3结构体变量的引用
                结构体内部的成员变量的引用
                 
                 (1) .
                          域操作符 :取某个子成员变量
                          用法:
                                  结构体变量名 . 成员名
                         例子:
                                 struct student s; //(s=>num,name,gender,....)
                                 
                                 s.num = 115200 ;// write
                                 int n = s.num; // read
                                 
                                 s.name
                                 s.gender
                                 s.age
                                 s.score
                                 ....
                             引用成员变量和普通变量是一样的,他们既有左值,也有右值!
                         
                         可以定义一个指针,来保存s的地址:
                             指向对象的类型 * p;
                             typeof(s) *p;
                             ===>struct student *p; //p就是一个结构体指针
                                  p = &s; //保存了结构体变量s的地址,p指向s
                                 
                         想通过指针p来访问s里面的成员变量
                         
                 (2)(*结构体指针).成员变量名
                       (*p). num
                       .....
                 (3)->
                         结构体指针 -> 成员变量名
                    
             练习:
                  1.定义一个结构体类型来描述一个日期(年,月,日)
                    再定义一个结构体类型来描述一个学生(姓名,成绩,出生年月日,年龄)
                     
                     请大家再main函数中,定义一个学生类型的变量
                     从键盘给这个结构体变量的成员赋值,
                     再输出这个结构体变量中各个成员的信息。
                     //日期
                    struct date
                    {
                        int year;
                        int month;
                        int day;
                    };
                   struct student
                    {
                      char name[32];
                      int  score;
                      struct date birthday; ----> &s.birthday.year  &s.birthday.month  &s.birthday.day
                      int age;
                    };                  
                    int main()
                    {
                       struct student s;
                        scanf("%s %d %d-%d-%d %d", s.name , &s.age, &s.brithday.year ,
                             &s.brithday.month, &s.brithday.day, &s.score);
                        printf("%s %d %d-%d-%d %d\n", s.name , s.age, s.brithday.year ,
                             s.brithday.month, s.brithday.day, s.score );
                    }
1.4初始化结构体变量的方法
定义结构体变量时候就赋值
                      struct date
                    {
                        int year;
                        int month;
                        int day;
                    };
                      struct student
                    {
                      char name[32];
                      int  score;
                      struct date birthday;
                      int age;
                    };        
            (1)按照定义时的顺序依次初始化各个成员变量,用逗号隔开。
                      struct student s =
                      {
                         "yangyang",
                          88,
                         {2010,12,12},
                         13                        
                      };
             (2)不按照顺序, .成员变量名 = 值 ,用逗号隔开
                     struct student s =
                      {
                         .score = 88,
                         .birthday = {2010,12,12},
                         ....
                         
                      };    
                     
               (3)结构体数组初始化
                    a,按照数组元素的顺序依次初始化
                          struct student class[3] =
                       {
                           // class[0]
                              {
                                  .name = xxx,
                                  .age = xxx,
                              },
                           // class[1]
                              {
                                  xxxxx
                              }
                           
                       };
                     b,不按照数组元素的顺序。  [下标] =
                        不同编译器,情况或限制不一样
                      struct student class[3] =
                       {  
                            [1] =
                                  {xxxxx},
                            [0] =
                                   {xxxx},
                       };
                       
             练习: 定义一个结构体来描述一个学生的信息(学号,成绩,姓名,出生年月日)
                    从键盘中输入5个学生的信息,假设按成绩的降序依次输出每个学生的信息
                    struct date
                     {
                         int y, m , d;
                     };
                    struct student
                     {
                         int num;
                         char name[32];
                         int score;
                         struct date  birthday;
                     };
                     int main()
                     {
                        struct student *pcls = (struct student*)malloc(5*sizeof(*pcls));
                        // p[0] ... p[4]
                        //从键盘给每一个学生进行输入
                        int i;
                        for(i=0;i<5;i++)
                        {
                           scanf("%d %s %d %d-%d-%d",
                                  &pcls[i].num,
                                  pcls[i].name,
                                  &pcls[i].score,
                                  &pcls[i].birthday.y,
                                  &pcls[i].birthday.m,
                                  &pcls[i].birthday.d);
                        }
                        //可以按照学生的成绩,由大到小排序
                        int a[5]; //保存pcls元素的下标
                                  // a[0] 成绩最高的那个学生所在数组pcls中的下标
                                     ...
                                     a[4] 成绩最低
                         for(int i = 0;i<5;i++)
                         {
                            a[i] = i;
                         }
                         for(int j = 1;j<5;j++)
                         {
                             for(i = 0; i <5-j;i++)
                             {
                                 if(pcls[a[i]].score < pcls[a[i+1]].score)
                                 {
                                    struct student s;
                                     s = pcls[i];
                                     pcls[i] = pcls[i+1];
                                     pcls[i+1] = s;
                                 }
                             }
                         }
                         for(i=0;i<5;i++)
                         {
                            printf("%d %s %d %d-%d-%d",
                                       pcls[i].num,
                                      pcls[i].name,
                                      pcls[i].score,
                                      pcls[i].birthday.y,
                                      pcls[i].birthday.m,
                                      pcls[i].birthday.d );
                         }
                     }
2.共用体/联合体
        语法:
                 union 共用体名
                 {
                    成员类型1   成员名1;
                    成员类型2   成员名2;
                    ......
                    
                 };
                 
         共用体与结构体最大的区别:
                       结构体所占内存大小是各个成员变量之和
                       共用体所占内存大小是各个成员变量中所占内存最大的那个
                       
                 union  t
                 {
                      int a;
                      char b;
                      short c;
                 };
                 sizeof(union t) == 4
                 
         共用体的存储空间是各个成员之间共用的,
         同一时刻只能用一个成员变量,为了节省内存才提出 共用体
             例子:
                   union test                  
                   {
                       int a;
                       int b;
                   };
                   sizeof(union test) == 4
                   占四个字节的空间,成员变量a和b共用这四个字节。
             
                 union test
                 {
                    char a;
                    int b;
                 };
                 union test t;
                 t.b = 256;
                 t.a = ? // 0
                 
         大端模式和小端模式
                 CPU寄存器是按bit位来存储信息的,
                 寄存器的数量是非常有限的
                 我们经常会把寄存器中的数组存储到存储器(内存)中去
                 内存不是按bit来寻址,按字节编号(地址)去寻址
                 
                 如果把寄存器中的内容存储到内存中去,那么内存的
                 低地址到底是保存寄存器中的  低字节还是高字节呢?
                 
                 大端模式:
                           存储器的低地址,存放的寄存器的高字节
                           低地址   ---------  高地址   // 0x12 34 56 78
                           高字节   ---------  低字节   // 0x12 0x34 0x56 0x78
                 小端模式:
                           存储器的低地址,存放的寄存器的低字节
                           低地址   ---------  高地址   // 0x12 34 56 78
                           低字节   ---------  高字节   // 0x78 0x56 0x34 0x12
                    共用体的存放顺序是所有成员都从低地址开始存放的,利用该特性就可以
                     轻松获得我们的电脑是属于大端模式还是小端模式
                    练习;
                             请大家写一个程序,验证一下自己的电脑是属于大端模式还是
                             小端模式
                             
                             union test
                             {
                                char a;
                                int b;
                             };
                             int main()
                             {
                                 union test t;
                                 t.b = 1;
                                 if(t.a==1)
                                 {
                                    //小端
                                 }
                                 else
                                 {
                                   //大端
                                 }
                             }                            
                         小端模式
                                 如下代码,请分析程序的运行结果
                                  union  test
                                  {
                                     char a;
                                     int b;
                                  };
                                  union test t;
                                  t.b = 255;
                                  t.b++;
                                  printf("%d\n",t.a); //0
3.枚举
        把该类型变量所有可能的值 一一列举出来。
         所以枚举类型一定是可以列举的值,整数值
            例子:
                     int weekday; // 星期几
                                 // 1,2,3,4,5,6,7
         枚举语句:
                   enum 类型名
                   {
                      //枚举的具体的值
                   };                
                   c语言在实现枚举时,枚举的具体的值,必须要用整数来表示    。
                 例子:
                         enum weekday
                         {
                              MON = 1,
                              TUE = 2,
                         };                        
                        
                         enum color
                         {
                             RED,   // 0
                             GREEN,  //1
                             YELLOW, // 2
                             PINK, // 3
                             LAN, // 4
                         };
                         enum color c = RED;
                         printf("%d\n",RED);//0
                         printf("%d\n",c);//0
                       
                         enum color
                         {
                             RED=1,
                             GREEN, //2
                             YELLOW=250,
                             PINK, // 251
                             LAN,  // 252
                         };
                         enum color c = RED;
                         printf("%d\n",RED);
                         printf("%d\n",c);
         --------------------------------------
             构造类型:
                         结构体
                         共用体
                         枚举
                         数组
                               int a[4];//定义了一个数组a,typeof(a) ===> int [4]
4.typedef
        typedef 用来声明一个新类型
                 来替换一个已经有的类型
语法:
         typedef  现有的类型名 新类型名;
       ==> 新类型名 就和  现有的类型名  类型是一样的
         
         例子:
                 struct student
                 {
                 
                 };    
                 typedef struct student STU;
                 STU s;
                      <===> struct student s;                
                 
                 typedef struct student
                 {
                    
                 }STU;    
             --------------------------------
         int num[100];//定义了一个数组,数组名为num
              //typedef int[100] type_num;    
                  ====>typedef int type_num[100];
                      type_num:新的类型名,像num这样的类型 ---》int [100]
  
                 假设让你定义一个和num一样类型的变量a:
                 typeof(num) a;
                   type_num a;
                               <====>  int a[100];
     typedef 总结
                  编程定义一个新类型。
                  int a;// a是一个变量
                  typedef int a; //a就是一个类型名,像int一样的类型
5.字节对齐问题
cpu底层为了访问的效率,一般会要求,任何对象的地址必须是对齐的
    自然对齐:
               数据地址是数据长度的倍数
              sizeof(int) ==4
              int a;
              &a 不是4的倍数的话,a不是自然对齐
             
              sizeof(short) == 2
              short a;
              &a 不是2的倍数的话,a就不是自然对齐
             
         n---字节对齐
             地址是n的倍数(n一般为2的x次幂)
              如:
                    4 - 字节对齐
                    8 - 字节对齐
                    ......
         
         结构体的每一个成员变量通常会有一个默认的对齐方式----自然对齐
                 struct test
                 {
                     char a; // a的地址必须是1的倍数                    
                     int b;  // b的地址必须是4的倍数
                 };
                 sizeof (struct test)  ==  ?8
                     a _ _ _
                     b b b b
                 
                 
                 struct test
                {
                   char a; //
                   short b;//
                   int c; // 4                      
                };
                sizeof(struct test) == ? //8
                struct tese 是几字节对齐??
                
                在64bits x86的编译器中
                       GNU(gcc):
                                char  ---> 1字节对齐
                                short ---> 2字节对齐
                                int   ---> 4字节对齐
                                long  ---> 8字节对齐
                                float ---> 4字节对齐
                                double---> 8字节对齐
                                .....
                                指针类型 ---> 8字节对齐
                                
         练习:
                 struct MixedData
                 {
                    char D1;
                    short D2;
                    int D3;
                    char D4;                   
                 };
                 sizeof(struct MixedData)==? // 12
                    D1 _ D2 D2 D3 D3 D3 D3 D4 _ _ _
                    
                    D1 _ D2 D2
                    D3 D3 D3 D3
                    D4 _ _ _
             ---------------------------------                
                 struct FinalPad
                 {
                    float x;
                    char n[1];
                 };
                 sizeof(struct FinalPad) ==?  8
                    x x x x
                    
             -----------------------------------                
                 struct FinalPadShort
                 {
                    short x;
                    char n[3];
                 };
                 sizeof(struct FinalPadShort)==? 6
                    x x _
                    n0 n1 n2
             ------------------------------------    
                 struct MixeData
                 {
                     char D1;
                     short D2;
                     int D3;
                     char D4;
                 };
                     s _ _ _
                     D1 _ D2 D2
                     D3 D3 D3 D3
                     D4 _ _ _
                 struct test
                 {
                    char s; // 1字节对齐
                    struct MixeData m;//4字节对齐
                 };
                 结构体是按照最大的原始类型的对齐方式对齐。
                 sizeof(struct test) ==?   16          
以下对枚举类型名的定义中正确的是? (B)
A enum a={one,two,three};
B enum a {one=9,two=-1,three};
C enum a={"one","two","three"};
D enum a {"one","two","three"};
以下程序的执行结果是?(小端模式) (266)
#include <stdio.h>
union un
{
int i;
char c[2];
};
void main()
{
union un x;
x.c[0]=10;
x.c[1]=1;
printf("\n%d",x.i);
}
以下程序的运行结果为? (80)
typedef union student
{
char name[10];
long sno;
char sex;
float score[4];
}stu;
main()
{
stu a[5];
printf("%d\n",sizeof(a));
}