C语言3——结构体、联合体、枚举、宏定义
2016-01-01 12:07
597 查看
1、初始化
方式一
方式二
方式三
方式四
方式五
2、结构体内存对齐模式
指定结构体成员的位字段
3、结构体数组
4、结构体嵌套
5、结构体拷贝
6、指向结构体的指针
7、堆内存结构体
8、结构体中的数组成员和指针成员
9、将结构体作为函数参数
10、联合体
联合体union是一个能在同一个存储空间存储不同类型数据的类型
联合体所占的内存长度等于其最长成员的长度,也有叫做共用体
联合体虽然可以有多个成员,但同一时间只能存放其中一种
在上面的例子中联合体内成员a和b是共用一块地址的
11、联合体长度
12、联合体中的指针成员
如果联合体中有指针成员,那么一定要使用完这个指针,并且free指针之后才能使用其他成员
13、枚举类型
14、typeof
typedef是一种高级数据特性,它能使某一类型创建自己的名字
typedef char BYTE;//定义了新的数据类型,名字叫BYTE,类型是char
#define MAX 10不可以替换成typedef 10 AAA;
上面的代码中,test函数的第一个参数是函数指针 但是这个函数的定义太复杂 所以我们一般避免这样写
方式一
#include <stdio.h> #include <string.h> #pragma warning(disable:4996) struct student{ char name[100]; int age; int sex; };//说明了一个结构体的数据成员类型 int main(){ struct student st;//定义一个结构体变量st st.age=20; st.sex=0; strcpy(st.name,"myj"); printf(); return 0; }
方式二
//定义结构体的时候就可以初始化 struct student st={"myj",24,0}; struct student st={0};//将所有成员值初始化为0,字符串将会初始化为空串
方式三
scanf("%s",st.name); scanf("%d",&st.age); scanf("%d",&st.sex);
方式四
struct student st={.age=20,.name="myj3",.sex=0}; struct student st={.name="myj3",.sex=0};//没有给age初始化,默认为0
方式五
struct student st; memset(&st,0,sizeof(st));
2、结构体内存对齐模式
struct A{ int a; char b; }; int main(){ struct A s; printf("%d",sizeof(s));//8 计算长度的时候会以所有成员里面最长的那个为基准 }
struct A{ int a; char b; char c; char d; char e; }; int main(){ struct A s; printf("%d",sizeof(s));//还是8 }
struct A{ int a; char b; char c; char d; char e; char f; }; int main(){ struct A s; printf("%d",sizeof(s));//12 }
struct A{ char a[10]; char b; char c; char d; char e; char f; char g; }; int main(){ struct A s; printf("%d",sizeof(s));//16 只会把数组中的一个元素当成一个成员 }
struct A{ char int[10]; char b; char c; char d; char e; char f; char g; }; int main(){ struct A s; printf("%d",sizeof(s));//48 只会把数组中的一个元素当成一个成员 }
struct A{ char c; int a; char b; } int main(){ struct A s; printf("%d",sizeof(s));//12 由于第一个成员是char类型 char会扩展为和第2个int成员相同的空间 }
struct A{ char a; short b; int c; short d; char e; } int main(){ struct A a; a.a=1; a.b=2; a.c=3; a.d=4; a.e=5; } //实例化之后的结构体a在内存中结构为 //01 xx 00 02 //00 00 00 03 //00 04 05 xx //其中xx表示没有被利用的空间 所以sizeof(a)是12 //结构体变量在占内存时总是以2的倍数为开始索引来占据的,再例如: struct A{ char a; short b; int c; char f; short d; char e; } int main(){ struct A a; a.a=1; a.b=2; a.c=3; a.d=4; a.e=5; a.f=6; } //结构体变量a在内存中的结构为 //01 xx 00 02 //00 00 00 03 //06 xx xx xx //00 04 05 xx //------------------ //所以sizeof(a)是16 struct A{ char a; short b; int c; short d; char e; char f; } int main(){ struct A a; a.a=1; a.b=2; a.c=3; a.d=4; a.e=5; a.f=6; } ----------------------- //01 xx 00 02 //00 00 00 03 //00 04 05 06 //sizeof(a)是12
指定结构体成员的位字段
struct D{ char a:2;//定义一个成员类型为char,但这个成员只是用2个bit } int main(){ struct D d; d.a=3; printf("%d\n",d.a);//-1 printf("%d\n",sizeof(d));//1 return 0; }
struct D{ char a:2; char b:2; } int main(){ struct D d; printf("%d\n",sizeof(d));//还是1,也就是说我们可以通过字节下的比字节更小的单位bit来控制 return 0; }
struct D{ char a:2; unsigned char b:4; } int main(){ struct D d; d.a=3; d.b=16; printf("%d %d\n",d.a,d.b);//-1 0 printf("%d\n",sizeof(d));// return 0; }
//在嵌入式的场合通常使用: struct D{ char a:1;//只有两种状态,开或者关 } //一个LED灯的话用这样的八个成员就可以了(不要忘了小数点)
struct F{ int i:2; char c:2; } int main(){ printf("%d\n",sizeof(struct F));//8 }
struct G{ char a; char b; char c; } int main(){ struct G g; char *p=&g; p[0]=3; p[1]=4; p[2]=0; printf("%d %d %d",g.a,g.b,g.c);//3 4 0 }
struct H{ char a; int b; } int main(){ struct H h={1,2}; char *p1=&h; p1++; *p1=4; p1++; *p1=5; printf("%p\n",&h);//01 04 05 xx 02 00 00 00 从结果中可以看出我们已经把第一行char类型浪费的字节利用起来了 return 0; }
3、结构体数组
struct student{ char name[16]; unsigned char age; unsigned char score; char classes[100]; }; int main(){ struct student st[3]={{"aaa",20,50,"c"},{"bbb",21,60,"c++"},{"ccc",22,70,"java"}}; int i; for(i=0;i<5;i++){ printf("姓名=%s 年龄=%d 成绩=%d 班级=%s\n",st[i].name,st[i].age,st[i].score,st[i].classes); } return 0; }
struct A{ char array[10]; } int main(){ struct A a1={"hello"};//如果结构体的成员是数组,通过结构可以变相的实现数组的赋值,而数组之间是不可以直接赋值的 struct A a2={0}; a2=a1;//这样写是合法的 printf("%s",a2.array); }
void swap(struct student *a,struct student *b){ struct student tmp=*a; *a=*b; *b=tmp; } int main(){ int i; int j; for(i=0;i<5;i++){ for(j=1;j<5-i;j++){ if(st[j].age<st[j-1].age){ swap(&st[j],&st[j-1]); }else if(st[j].age==st[j-1].age){ //如果年龄一样就比较分数 if(st[j].score<st[j-1].score){ swap(&st[j],&st[j-1]); } } } } }
//如果通过班级来排序,即给字符串排序 int main(){ int i; int j; for(i=0;i<5;i++){ for(j=1;j<5-i;j++){ if(strcmp(st[j].classes,st[j-1].classes)>0){ swap(&st[j],&st[j-1]); } } } return 0; }
4、结构体嵌套
struct A{ int a; char b; }; struct B{ struct A a; char b; } int main(){ printf("%d\n",sizeof(struct B));//12 return 0; }
struct A{ int a; int b; }; struct B{ int c; struct A a; int b; }; struct C{ struct B b;//仍然以int对齐 char d; }; int main(){ printf("%d",sizeof(struct C));//20 }
struct A1{ char a; } struct A2{ struct A1 a; char b; } struct A3{ struct A2 a; char b; } int main(){ struct A3 a3; //a3.a.a.a=100; 栈中的结构体通过点访问 struct A3 *ap=malloc(sizeof(struct A3)); ap->a.a.a=20;//堆中的结构体通过->访问 }
5、结构体拷贝
struct A a1,a2; a1.a=1; a1.b=2; a2=a1;//结构体之间内存的拷贝 相当于memcpy(&a2,&a1,sizeof(a1));
6、指向结构体的指针
struct A a; struct A *p=&a; p->a=10;//相当于(*p).a=10; p->b=20;//相当于(*p).b=10;
7、堆内存结构体
int main(){ struct A *p=malloc(sizeof(struct A)*10); memset(p,0,sizeof(struct A)*10); struct A *array=p; p->a=1; p->b=2; p++; p->a=3; p->b=4; int i; for(i=0;i<10;i++){ printf("%d %d",array[i].a,array[i].b); } free(array);//不能free掉p,因为p已经赋给array了,free知道要去释放10个字节的内存,如果要去free p的话,从第2个字节的内存开始free,free到第10个字节的内存时还没有free完,就再去free没有通过malloc分配的内存,这样一定会出问题 return 0; }
8、结构体中的数组成员和指针成员
struct student{ char name[100]; int age; } struct student_a{ char *name; int age; } int main(){ struct student_a st={NULL,0}; st.age=30; st.name=malloc(100); strcpy(st.name,"myj"); printf("%d %s",st.age,st.name); free(st.name); }
struct student{ char name[100]; int age; } struct student_a{ char *name; int age; } int main(){ struct student_a st={NULL,0}; st.age=30; st.name=malloc(100); strcpy(st.name,"myj"); struct student_a st1=st; printf("%d %s",st1.age,st1.name);//30 乱码 free(st.name); }
struct man{ char *name; int age; } int main(){ struct man m={"tom",20}; printf("name=%s age=%d\n",m.name,m.age);//看起来结果正常 } //但是假如用strcpy改一下name: int main(){ struct man m={"tom",20}; strcpy(m.name,"mike");//程序会挂掉 printf("name=%s age=%d\n",m.name,m.age); } //如果结构体中name的定义改成了char name[100] 就没问题 //name是指针类型时,struct man m={"tom",20}; 执行完了之后m.name就指向了常量"tom"的地址 //strcpy(m.name,"mike");这种操作是去修改一个常量,而常量是不可以修改的,所以会挂掉 //结构体中的成员是指针类型的时候通常通过以下方式赋值 struct man m; m.name=malloc(sizeof(char)*100);//在堆中给name分配100字节 strcpy(m.name,"mike"); free(m.name);//要记得释放
struct student{ char name[100]; int age; } int main(){ //struct student st; 在栈里面 struct student *p=malloc(sizeof(struct student)); //在堆里面 free(p); return 0; }
struct man{ char *name; int age; }; int main(){ struct man *p=malloc(sizeof(struct man));//结构体变量p在堆中 p->name=malloc(sizeof(char)*100);//结构体变量p的成员name也在堆中 strcpy(name,"tom"); p->age=30; printf("name=%s age=%d\n",p->name,p->age); free(p->name);//注意释放的顺序一定得是先释放p的成员name,再释放整个结构体 free(p); }
9、将结构体作为函数参数
struct student{ char name[100]; int age; }; void print_student(struct student s){//一般不把结构类型变量作为函数参数,因为结构类型可能很大,函数调用式,实参和形参在栈内存中同时存在,就会极大浪费栈内存 printf("name=%s age=%d",s.name,s.age); } void set_student(struct student s,const char *name,int age){ strcpy(s.name,name); s.age=age; } int main(){ struct student st={"tom",20}; set_student(st,"mike",100); print_student(st);//打印出来的是tom 就是普通类型的变量 }
//想要改的话只能传递地址: struct student{ char name[100]; int age; }; void print_student(struct student s){ printf("name=%s age=%d",s.name,s.age); } void set_student(struct student *s,const char *name,int age){ strcpy(s->name,name); s->age=age; } int main(){ struct student st={"tom",20}; set_student(&st,"mike",100); print_student(st);//打印出来的是mike }
10、联合体
union A{ int a; int b; }; int main(){ union A a; a.a=10; a.b=20; printf("%d",sizeof(union A));//4 printf("%p %p",&a.a,&a.b);//内存一样 printf("a=%d",a.a);//20 因为共用一块内存 }
联合体union是一个能在同一个存储空间存储不同类型数据的类型
联合体所占的内存长度等于其最长成员的长度,也有叫做共用体
联合体虽然可以有多个成员,但同一时间只能存放其中一种
在上面的例子中联合体内成员a和b是共用一块地址的
11、联合体长度
union A{ unsigned char a; char b; }; int main(){ union A a; a.a=128; //a.b=20; printf("%d",sizeof(union A));//1 以联合体中长度最大的那个成员为准 printf("%p %p",&a.a,&a.b); printf("a=%d",a.a);//128 printf("b=%d",a.b);//-128 }
12、联合体中的指针成员
union A{ char a; char *b; }; int main(){ union A a; a.b=malloc(100);//b指向了一个堆的地址 a.a=10;//但是给a赋值之后b的值成了10,所以下面就free不了了 free(a.b); return 0; }
如果联合体中有指针成员,那么一定要使用完这个指针,并且free指针之后才能使用其他成员
union A{ char a; char *b; }; int main(){ union A a; a.b=malloc(100);//b指向了一个堆的地址 free(a.b); a.a=10;//但是给a赋值之后b的值成了10,所以下面就free不了了 return 0; }
13、枚举类型
enum A{ red,green,black,yellow //相当于定义了4个常量,都是int类型的 值分别为0 1 2 相当于用#define定义的常量 }; int main(){ int color=black;//但是这里不可以给black赋值 枚举是整数常量 printf("%d",red);//0 printf("%d",green);//1 printf("%d",black);//2 printf("%d",yellow);//3 printf("%d",color);//2 }
enum A{ red=5,black,yellow,green }; //现在black变成了6 yellow是7 ...
enum A{ red=5,black=2,yellow,green }; //yellow是3
14、typeof
typedef是一种高级数据特性,它能使某一类型创建自己的名字
typedef char BYTE;//定义了新的数据类型,名字叫BYTE,类型是char
#define BYTE1 char//define只是做了一个语法替换,但是typedef就是定义了一种类型,并不单单是替换 int main(){ BYTE a; BYTE1 a1; a=10; printf("%d",a); return 0; }
#define MAX 10不可以替换成typedef 10 AAA;
struct abc{ int a; char b; }; typedef struct abc A; int main(){ A a;//就不用一直写struct关键字了 }
//================上面的typedef定义的类型还有一种简化写法,而这种写法是define做不到的 typedef struct{ int a; } A2;
char *mystrcat(char *s1,char *s2){ strcat(s1,s2); return s1; } char *test( char *(*p)(char *,char *),char *s1,char *s2 ){ return p(s1,s2); } int main(){ char s1[100]="hello"; char s2[100]="world"; char *s=test(mystrcat,s1,s2); printf("%s",s);//helloworld return 0; }
上面的代码中,test函数的第一个参数是函数指针 但是这个函数的定义太复杂 所以我们一般避免这样写
char *mystrcat(char *s1,char *s2){ strcat(s1,s2); return s1; } typedef char *(*STRCAT)(char *,char *)//STRCAT可以指向两个参数是char*类型,返回值也是char*类型的函数 char *test( STRCAT p, char *s1, char *s2 ){ return p(s1,s2); } int main(){ STRCAT array[10];//定义了一个每个成员的类型都是STRCAT的数组,该数组长度是10 //如果用原始的方法定义的话可读性极差:char *(*p[10])(char *s1,char *s2); 而且在这里typedef是无法用#define替换的 char s1[100]="hello"; char s2[100]="world"; char *s=test(mystrcat,s1,s2); printf("%s",s);//helloworld return 0; }
相关文章推荐
- C语言-函数指针与函数名的区别
- C语言2——指针
- 左式堆--C语言实现
- C++ 继承体系中的名称覆盖
- 值得推荐的C/C++框架和库
- C语言数组
- c++ 特性回顾
- Sicily 2683. Alice and Bob
- 【C++】基础知识—构造函数与拷贝构造函数
- C++学习 - 泛型编程基础
- C语言指针从理解到深入(2)
- 利用C++11实现一个自动注册的工厂
- C++重载运算符
- 《The C++ Programming Language》摘抄与总结——类型与声明
- 求二进制中数1的个数
- C++实现DPM/LatentSVM 完整代码下载 --- 第四篇
- 《The C++ Programming Language》摘抄与总结——关于
- 3、进入32位模式并导入C语言
- C语言isalnum()函数:判断字符是否为英文字母或数字
- c++基本知识(1)