结构体

  • 结构体:主要用来组织不同类型的数据(注:结尾有分号)
struct 结构体名称
{
    结构体成员1;
    结构体成员2;
    ......
} 变量名;
//访问结构体成员:‘.’(成员选择运算符)、‘->’(指针选择运算符);如:book.title,(&book)->title 就是引用book结构体的title成员;
  • 结构体声明只是进行一个框架的描绘,不会直接在内存中分配空间,直到定义时才会分配空间
  • 成员选择运算符:'.',指针选择运算符:'->'(注意:都属于第一优先级,与括号同属第一优先级,比 '++' 、'&' 和 '*' 解应用优先级高)
  • 结构体中的指针:因为指针保存的是地址,所以不可以直接用 ‘=’ 给结构体中字符数组赋值,要用  strcpy() 函数间接为指针赋值
#include <string.h>
struct SS{
    char s[10];
} s;

int main()
{
    char *str="asdf";
    strcpy(s.s,str);
    //错误:s.s=str;
    printf("%s",s.s);
}
//输出:asdf
  • 结构体赋值形式:
struct student{
    int id;
    char name[10];
    char sex;
    double grade;
};

int main()
{
    struct student std_1,std_2;
    //1、整个赋值只可在定义时使用(也可以指定元素赋值:struct student std_3={.id=1003,.grade=87.5})
    struct student std_3={1003,"lisi",'W',87.5};
    //2、分别赋值
    scanf("%d%s %c%lf",&std_1.id,std_1.name,&std_1.sex,&std_1.grade);
    //3、因为结构体相当于变量,不是地址,所以不可以直接为结构体整体赋值(定义时除外)
    std_2=std_1;std_2.id=1002;
    printf("%-6d%-10s%-3c%-5.2lf\n",std_1.id,std_1.name,std_1.sex,std_1.grade);
    printf("%-6d%-10s%-3c%-5.2lf\n",std_2.id,std_2.name,std_2.sex,std_2.grade);
    printf("%-6d%-10s%-3c%-5.2lf\n",std_3.id,std_3.name,std_3.sex,std_3.grade);
}
//输入:1001zhangshan M98.89
//      1001  zhangshan M  98.89
//      1002  zhangshan M  98.89
//      1003  kisi      W  78.50
  • 结构体数组:struct 结构体名称 数组名[长度];
  1. 数组名是数组第一个元素的地址(是地址常量,不为指针),所以数组名可以直接赋值给指针;
  • 结构体指针:结构体的变量名不是结构体的地址;所以结构体可以直接赋值给另一个结构体( book_1=book_2 ),同时赋值给指针时需要取地址;(struct Book *pt=&book;)
  • 结构体指针访问结构体:
  1. 先对指针解引用,然后用 '.' 正常访问结构体成员【 (*pt).price 注:'.' 优先级高于 '*' 】
  2. 直接用 '->' 访问

注:一个结构体可以直接赋值给另一个结构体的;即:book1=book2;(前提是两个结构体的类型必须相同)所以结构体变量就可以作为参数返回值进行传递由于结构体所占内存比较大会影响程序效率,所以一般不会直接传递结构体,而是传递结构体指针;

  • 传值时可以直接传结构体的地址:如:fun(&book);
  • 结构体存储方式:结构体存储时,所有的成员在分配内存时都要与所有成员中占内存最多的数据类型所占内存空间的字节数对齐(所以会导致内存浪费)

结构体分配空间的对齐的原则

  1. 假如占内存最多的数据类型字节数为 N
  2. 理论上所有成员在分配内存时都是紧接在前一个变量后面依次填充的
  3. 但是如果是“以 N 对齐”为原则,那么,如果一行中剩下的空间不足以填充某成员变量,即剩下的空间小于某成员变量的数据类型所占的字节数,则该成员变量在分配内存时另起一行分配
struct STUDENT
{
    char name[10];
    int age;
    double score[3];
    char sex;
}data;
int main(void)
{
    printf("占字节数:%d",sizeof(data));
}
//占字节数:48

所以声明结构体类型时就按成员类型所占字节数从小到大写,或从大到小写可以节省空间;

  • 动态的申请结构体:使用 malloc 函数为结构体分配存储空间;
struct Book book=( struct Book * )malloc ( sizeof( struct Book));

 共同体

  • 共用体:定义格式与结构体相像
union 共用体名称 //共用体的名字不是必须的,有时可以没有名字
{
    共用体成员1;    //访问与结构体相同,访问指针用 '->',其他成员用 '.';
    共用体成员2;
    ......
};
  • 共用体的所有成员共享同一个内存地址
union A{
    char c;
    int i;
}aa={48+9};

int main()
{
    aa.c='a';
    printf("aa=%d aa.c=%c aa.i=%d\n",aa,aa.c,aa.i);
    //由于共用体成员共享同一内存地址,所以直接通过共用体名格式类型打印是不会出错的
    //如果直接用给共用体赋值则非法的;例如:aa='A'
    aa.i=65;
    printf("aa=%c aa.c=%c aa.i=%d",aa,aa.c,aa.i);
}
  • 结构体同时只能使用一个值
union data{
    int i;
    char ch;
};
//同时只能使用一个值:错误一:union data a={520,'a'};
//不能直接对公用体赋值:错误二:union data b=520;
union data a={520}; //赋值一:初始化第一个成员
union data b=a; //赋值二:直接用一个共用体初始化另一个共用体
union data c={ .ch='c' }; //赋值三:指定初始化成员
  • 共用体虽说占用的内存应该等于最大成员的所占内存,但有时候也不一定

枚举

  • 枚举:他的枚举值都叫枚举常量(定义枚举时的枚举值名称)

 

enum  枚举类型名称 {
    枚举值名称1,
    枚举值名称2,
    ... 
};
//如果在中间的枚举类型名称赋值:则赋值前,从0开始一一对应,赋值后,从赋的值开始一一对应
//更改对应枚举类型名称对应值:在定义枚举时就赋值;enum Color { blue,red=10,green };

 

  • 定义的枚举常量的值都是整性的,并且枚举类型名称从默认0开始一一对应
enum A{a,b,c=10,d,e=0,f,g};
int main()
{
    printf("a=%d b=%d c=%d d=%d e=%d f=%d g=%d",a,b,c,d,e,f,g);
}
//输出:a=0 b=1 c=10 d=11 e=0 f=1 g=2
  • 枚举变量可以赋值非枚举名称对应的其他整性值
enum A{a,b}en1=b;
int main()
{
    printf("覆盖前:%d\n",en1);
    en1=65535;
    printf("覆盖后:%d",en1);
}
//覆盖前:1
//覆盖后:65535
  • 与宏定义的区别:枚举常量中的值看似和宏定义替换整性常量相似;但枚举定以后,可以定义枚举名称的同名变量;宏定义后,则不能定义相同宏名的变量
#define B 10
enum A{a,b};
int main()
{
    double a=3.14;
    //double B=3.14;错误语句,不能将浮点型常量赋值给整性常量
    //注意:变量不可以覆盖宏定义;但宏定义可以覆盖变量
    printf("%.2f",a);
}
//3.14

注:枚举类型名称是可以自加的;如:enum Color rgb=red; if( rgb==red ) rgb++;【只在在c语言中允许,c++中是不允许的】