结构体
struct stu
{char name[20];int age;
}s1,s2; //s1,s2是全局变量int main()
{struct stu s3; //s3是局部变量return 0;
}
匿名结构体类型 只能用一次
struct
{char name[20];int age;
}s1;
以下两种写法相同
//1
typedef struct node
{int data;struct node* next;
}*linklist;//2
struct node
{int data;struct node* next;
};
typedef struct node* linklist;
定义及初始化
struct point
{int x;int y;
}p1 = { 2,3 };struct score
{int s;char ch;
};struct stu
{char name[20];int age;struct score s;
};int main()
{struct point p2 = { 3,4 };struct stu s1 = { "manba",24 , {100,'q'} };printf("%c", s1.s.ch); //q
}
结构体内存对齐
往年面试笔试题常考
对齐规则:
第一个成员在与结构体变量偏移量为0的地址处。
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的值为8
gcc上没有默认对齐数,对齐数就是自身大小
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
因此,结构体中会有内存浪费
struct s1
{char c1; //1int i; //4char c2; //1
};struct s2
{char c1;char c2;int i;};int main(){struct s1 s1;struct s2 s2;printf("%d ", sizeof(struct s1)); //12printf("%d ", sizeof(struct s2)); //8printf("%d ", offsetof(struct s1, c1)); //0printf("%d ", offsetof(struct s1, i)); //4printf("%d ", offsetof(struct s1, c2)); //8printf("%d ", offsetof(struct s2, c1)); //0printf("%d ", offsetof(struct s2, c2)); //1printf("%d ", offsetof(struct s2, i)); //4return 0;
}
//修改默认对齐数
#pragma pack(4) //默认对齐数改成4
#pragma pack() //恢复默认对齐数
结构体传参
struct s
{int data[1000];int num;
};
void print1(struct s ss)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ss.data[i]);}printf("%d", ss.num);
}
void print2(const struct s* ps)
{int i = 0;for (i = 0; i < 3; i++){printf("%d ", ps->data[i]);}printf("%d ", ps->num);}
int main()
{struct s s = { {1,2,3},100 };print1(s); //传值调用print2(&s); //传址调用 更推荐使用 函数传参的时候,参数需要压栈,有时间和空间上的开销return 0;
}
位段不跨平台
因此可移植的程序应该避免使用
struct a
{//先开辟4byte 32bitint a : 2; //a占两个比特位int b : 5; //b占5个int c : 10;//再开辟 4byteint d : 30;
}; //47bit
//共开辟 8byte(字节)
int main()
{printf("%d ", sizeof(struct a)); // 8 即64bitreturn 0;
}
枚举 就是一一列举
enum day
{Mon = 1, //枚举常量,后续不能修改 都用逗号隔开Tues,Wed,Thur,Fri,Sat,Sun
};int main()
{enum day d = Fri;printf("%d\n", Mon); //1printf("%d\n", Tues); //2printf("%d\n", Wed); //3return 0;
}
为什么用枚举而不用#define?
举例:https://gitee.com/wu-jiale26/c-language-leetcode/blob/master/C%E8%AF%AD%E8%A8%80%E9%9D%99%E6%80%81%E9%80%9A%E8%AE%AF%E5%BD%95/test.c
提高代码的可读性和可维护性。
便于调试
与#define定义的标识符比较 , 具有类型检查 ,更加严谨
防止命名污染(封装)
一次可定义多个常量
联合(共用体): 特殊的自定义类型 这些成共用一块空间
union un
{int a;char c;
};struct st
{int a;char c;
};int main()
{union un u;printf("%d\n", sizeof(u)); //4 printf("%p\n", &(u)); //012FF960printf("%p\n", &(u.a)); //012FF960printf("%p\n", &(u.c)); //012FF960u.a = 0x11223344; //内存中 44 33 22 11u.c = 0x00; // 00 33 22 11}
两种方法判断当前计算机的大小端储存
//
int checksys()
{int a = 1; //00 00 00 01//低->高 小端 01 00 00 00//00 00 00 01 大端return *(char*)&a;
}
int checksys2()
{union //匿名类型{char c;int i;}u;u.i = 1;return u.c;
}
int main()
{int ret = checksys();ret = checksys2();if (ret == 1){printf("小端\n");}elseprintf("大端\n");}
联合体对齐数
union un
{char arr[5]; //5 对齐数是1int i; //4 对齐数是4
};
int main()
{printf("%d", sizeof(union un)); //8}
union un
{short arr[7]; //14 对齐数是2int i; //4 对齐数是4
};
int main()
{printf("%d", sizeof(union un)); //16}