您的位置:首页 > 编程语言 > C语言/C++

C语言3——结构体、联合体、枚举、宏定义

2016-01-01 12:07 597 查看
1、初始化

方式一

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: