为什么要用结构体?
在实际问题中,一组数据往往具有不同的数据类型。例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,C语言中给出了另一种构造数据类型——“结构(structure)”或叫“结构体”。 它相当于其它高级语言中的记录。“结构”是一种构造类型,它是由若干“成员”组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。结构既是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。让编程序的人自定义一个数据类型。
结构体的定义:
struct Datas //结构体的定义用关键字struct,模板的作用
{
int a;
char c;
float f;
char*p;
int array[2];
};
结构体的使用一:
#include <stdio.h>
#include <stdlib.h>
struct Student //结构体的定义用关键字struct,模板的作用,相当于自定义的类型
{
int score; //特征:分数
char name[128];//特征;名字,这个是定义了可以存储128个字节大小的数组,空格占一个字节
void (*pintroduce)(char* pname);//这里是函数指针
};
int main()
{
//结构体的使用:
//类型 变量名 初始值
struct Student stu1 ={98,"feng nan nan"};
//如何访问结构体:
printf("结构体中的score=%d\n",stu1.score);
printf("结构体中的name=%s\n",stu1.name);
struct Student stu2;
stu2.score=99;
/*
stu2.name="冯楠楠" 在给结构体中的字符串赋值时,C++、java可以这样写
C语言必须用字符串拷贝函数strcpy进行赋值
*/
char* str="冯楠楠";
strcpy(stu2.name,str);
printf("结构体中的score=%d\n",stu2.score);
printf("结构体中的name=%s\n",stu2.name);
system("pause");
return 0;
}
结构体使用二:
#include <stdio.h>
#include <stdlib.h>
void func(int data)
{
printf("函数:data=%d\n",data);
}
struct Datas
{
char*p1;//这里p1是指针
int a;
char c;
float f;
double d;
char str[128];
void (*p)(int a);
};
int main()
{
/*char *str="hello";这样写可以
char str[];
str="hello!";
这样写就不行了
要写为strcpy(str,"hello!");
还可以这样写:
char *str=NULl;//定义指针时不往指针里写东西就不用=NULL
//如果要是写还要malloc并且memset
str="hello!";//可以直接赋值,但最好都加
*/
char* str1=NULL;//定义一个指针没有malloc不能往里面写东西
str1=(char*)malloc(128);
memset(str1,'\0',128);
strcpy(str1,"hello!");
char str[]="你很帅!";
struct Datas d1;
d1.a=100;
d1.c='d';
d1.f=3.14;
d1.d=123.2323;
strcpy(d1.str,str);
d1.p=func;
d1.p1=(char*)malloc(128);//p1在结构体中没有开辟空间是个野指针,不能指直接写入,需要开辟空间
memset(d1.p1,'\0',128);
strcpy(d1.p1,"注意指针这是否开辟空间");
printf("结构体输出:%d\n",d1.a);
printf("结构体输出:%c\n",d1.c);
printf("结构体输出:%f\n",d1.f);
printf("结构体输出:%lf\n",d1.d);
puts(d1.str);
puts(d1.p1);
d1.p(10);//结构体函数调用
system("pause");
return 0;
}
结构体数组:
#include <stdio.h>
#include <stdlib.h>
struct Student
{
int score;
char *name;//这里尽量用指针,减小结构体的大小
//结构体太大,传参数占空间
};
int main()
{
int i;
struct Student stu[3];
struct Student maxStudent;//找最高分最低分找的是人,也就是详细的信息,用结构体
struct Student minStudent;
for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){
printf("请输入第%d个学生的名字:\n",i+1);
stu[i].name=(char*)malloc(128);
scanf("%s",stu[i].name);//注意结构体内的指针使用前要开辟空间
printf("请输入第%d个学生的分数:\n",i+1);
scanf("%d",&stu[i].score);
}
for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){
printf("%s:%d\n",stu[i].name,stu[i].score);
}
//maxStudent.score=stu[0].score;
//minStudent.score=stu[0].score;
minStudent=maxStudent=stu[0];//相同类型的结构体可以对等
for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){
if(maxStudent.score<stu[i].score)
maxStudent=stu[i];
if(minStudent.score>stu[i].score)
minStudent=stu[i];
}
printf("最高分是:%s:%d\n最低分是:%s:%d\n",maxStudent.name,maxStudent.score,minStudent.name,minStudent.score);
system("pause");
return 0;
}
结构体指针:
#include <stdio.h>
#include <stdlib.h>
//1、如果用结构体指针,就不能用点运算符访问结构体中的变量应该用->
//2、注意结构体指针是否是野指针或者NULL,若是则会出现段错误
struct Student
{
int score;
char name[128];
};
int main()
{
struct Student stu1;
stu1.score=100;
strcpy(stu1.name,"冯楠楠");
printf("姓名:%s\n分数:%d\n",stu1.name,stu1.score);
int* a;//整型定义指针
struct Student *p=NULL;//同样的道理也可以定义结构体指针
//这里这样定义属于野指针,对野指针进行写操作要出现段错误
p=(struct Student*)malloc(sizeof(struct Student));//不管是野指针还是等于NULL,
//都不能对这个非法的内存访问,否则出现段错误
p->score=150;//如果用结构体指针,就不能用点运算符访问结构体中的变量,要用->
strcpy(p->name,"冯楠楠");
printf("姓名:%s\n分数:%d\n",p->name,p->score);
free(p);//空间不用了就free掉防止内存泄漏
p=&stu1;//指针是存放地址的变量,之前指向malloc那片空间,现在存放的是stu1的地址
printf("姓名:%s\n分数:%d\n",p->name,p->score);
printf("地址是;%p\n",p++);//因为结构体大小为128+4=132,所以指针++偏移132个字节(十进制),结果:0060FE68
printf("加加后地址是;%p\n",p);//结果:0060FEEC
/*
总结来说指针++,要看指针指向的对象是谁,并不是+1
*/
system("pause");
return 0;
}
结构体指针操作学生成绩表:
#include <stdio.h>
#include <stdlib.h>
/*
结构体指针访问结构体内部元素方法:
结构体指针 ->成员名;如addr->country;
(*结构体指针).成员名;(*addr).country;//很少去进行使用,注意必须去使用(),,因为.优先级大于*
*/
struct Student
{
int score;
char*name;//4,linux 8
};
int main()
{
int i;
int len=2;
struct Student stu[2];
struct Student *p=stu;//结构体指针指向数组的头,其实和整型数是一样的
for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){
printf("请输入名字;\n");
p->name=(struct Student*)malloc(128);
scanf("%s",p->name);
printf("请输入分数:\n");
scanf("%d",&(p->score));
p++;
}
p=stu;
for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){
printf("姓名:%s 分数::%d\n",p->name,p->score);
p++;
}
struct Student *p2=(struct Student *)malloc(len*sizeof(struct Student));
//上一行代码是直接定义了结构体指针,并给他开辟5*sizeof(struct Student)怎么大的空间
//相当于有5个结构体数组,可以存储5个人的信息
for(i=0;i<len;i++){
printf("请输入名字;\n");
p2->name=(struct Student*)malloc(128);
scanf("%s",p2->name);
printf("请输入分数:\n");
scanf("%d",&(p2->score));
p2++;
}
p2-=len;
for(i=0;i<len;i++){
printf("姓名:%s 分数::%d\n",p2->name,p2->score);
p2++;
}
system("pause");
return 0;
}
结构体指针函数综合处理学生成绩:
#include <stdio.h>
#include <stdlib.h>
//malloc在堆上面开辟空间,函数调用结束后空间不会被释放
struct Student
{
int score;
char* name;
};
struct Student* initStuScores(int* len)//初始化函数,获取用户输入完成初始化
{
int i;
printf("请输入总人数:\n");
scanf("%d",len);
struct Student *p2=(struct Student *)malloc((*len)*sizeof(struct Student));
//上一行代码是直接定义了结构体指针,并给他开辟5*sizeof(struct Student)怎么大的空间
//相当于有5个结构体数组,可以存储5个人的信息
//malloc开辟的空间不会消失,在函数内部定义的指针变量,不会对main函数中的指针有影响
//p2是局部变量,返回的是p2的内容,不是p2
for(i=0;i<(*len);i++){
printf("请输入名字;\n");
p2->name=(struct Student*)malloc(128);
scanf("%s",p2->name);
printf("请输入分数:\n");
scanf("%d",&(p2->score));
p2++;
}
return p2-*len;
}
void printMes(struct Student* p2,int len)
{
int i;
for(i=0;i<len;i++){
printf("姓名:%s 分数::%d\n",p2->name,p2->score);
p2++;
}
//p2=p2-len;
}
struct Student* findMaxStu(struct Student* p,int len)
{
int i=0;
struct Student* maxStudent;
maxStudent=p;
for(i;i<len;i++){
if((p->score)>(maxStudent->score)){
maxStudent=p;
}
p++;
}
return maxStudent;
}
struct Student* findMinStu(struct Student* p,int len)
{
int i=0;
struct Student* minStudent;
minStudent=p;
for(i;i<len;i++){
if((p->score)<(minStudent->score)){
minStudent=p;
}
p++;
}
return minStudent;
}
float getAverage(struct Student*p,int len)
{
int i;
float toal=0;
for(i=0;i<len;i++){
toal=toal+p->score;
p++;
}
return (float)toal/len;
}
int findSome(struct Student*p,int len,char*name)
{
int i;
for(i=0;i<len;i++){
if(strcmp(name,p->name)==0){
return 1;
}
p++;
}
return -1;
}
int main()
{
int len;
struct Student *pstus=initStuScores(&len);
printMes(pstus,len);
struct Student* max=NULL;
struct Student* min=NULL;
max=findMaxStu(pstus,len);
min=findMinStu(pstus,len);//函数传参其实就是将地址值拷贝一份给函数
//函数内的指针变量不会对main函数中的指针有影响
//除非用二级指针
printf("最高分:%d,姓名:%s\n",max->score,max->name);
printf("最低分:%d,姓名:%s\n",min->score,min->name);
printf("平均分是:%f\n",getAverage(pstus,len));
if(findSome(pstus,len,"冯楠")==1){
printf("找到此人\n");
}else{
printf("没有此人\n");
}
system("pause");
return 0;
}
结构体大小如何计算:
#include <stdio.h>
#include <stdlib.h>
/*由于存储变量地址对齐的问题,结构体大小计算必须满足两条原则:
一、结构体成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
二、结构体大小必须是所有成员(数组和结构体除外)大小的整数倍
三、对齐方式确实很浪费空间,可是按照计算机的访问方式,这种对齐方式提高了效率*/
//简单结构体
struct s1
{
char ch1;//1 ch1相对于整个结构体的偏移量就是0,因为他是结构体的第一项
char ch2;//1 ch2相对于整个结构体的偏移量就是1,因为结构体第一项是1个字节
int i;//4 i相对于整个结构体的偏移量就是2,2不是4的倍数,逻辑偏移2,
//实际按照对齐规则,要偏移4个字节,这样ch2和i之间就右空余了两个字节,
//所以一共是8个字节
};
//简单结构体
struct s2{
char ch1;//1 ch1偏移量是0,
int i;//4 i的逻辑偏移值是1,要满足第一条规则,所以偏移4
//这样ch1和i之间就有3个字节
char ch2;//1 逻辑偏移量是8满足条件一,但是结构体总大小为九
//不满足条件二,所以ch2要向后偏移3个字节,所以总大小是12
};
//成员包含数组的结构体
struct s3{
char ch;//偏移值1
int i;// 逻辑偏移值是1,实际偏移值4,ch和i之间有三个字节
char str[10];//逻辑偏移值8,实际偏移值10,所以总大小是20
//这个char类型的数组,只需要把它看做十个char连在一起即可
};
//成员包含结构体的结构体,若结构体内的结构体仅仅是声明不占空间则可忽略
struct s4{
char ch;//偏移量是1
int i;//实际偏移量是1+3+4=8
struct s{
char ch1;
int j;
};//这个结构体大小是8,但是没有定义所以忽略
float f;//逻辑偏移量是8,实际偏移量是8,所以整个结构体大小为8+4=12
//满足条件一二
};
//成员包含结构体的结构体,若结构体内的结构体有定义,则占空间要计算
struct s5{
char ch;//偏移量是1
int i;//实际偏移量是1+3+4=8
struct ss{
char ch1;
int j;
}stemp;//这个结构体大小是8,满足条件一
float f;//逻辑偏移量是16,实际偏移量是16,所以整个结构体大小为16+4=20
//满足条件一二
};
//成员包含联合体的结构体
struct s6{
char ch;
int i;
union{//联合体按照最大的计算就是4
char ch1;
int j;
};
};
//指定对齐值:对齐值小于最大类型成员值
//如果最大成员超过了pack的要求,就按pack来对齐
//如果最大成员没有超过pack,结构体总大小按最大成员开对齐
#pragma pack(4) //指定向4对齐 最大是8
struct s7{
char ch;
int i;
float f;
double d;
};
//对齐值大于最大类型成员值,当指定对齐值大于自身对齐值时,向自身对其值对齐,大小是24.
#pragma pack(10)
struct s8{
char ch;
int i;
float f;
double d;
};
int main()
{
printf("char:%d\n",sizeof(char));//1
printf("float:%d\n",sizeof(float));//4
printf("int:%d\n",sizeof(int));//4
printf("double:%d\n",sizeof(double));//8
printf("s1:%d\n",sizeof(struct s1));//8
printf("s2:%d\n",sizeof(struct s2));//12
printf("s3:%d\n",sizeof(struct s3));//20
printf("s4:%d\n",sizeof(struct s4));//12
printf("s5:%d\n",sizeof(struct s5));//20
printf("s6:%d\n",sizeof(struct s6));//12
printf("s7:%d\n",sizeof(struct s7));//20
printf("s8:%d\n",sizeof(struct s8));//24
system("pause");
return 0;
}
typedef关键字
#include <stdio.h>
#include <stdlib.h>
/*
typedef
typedef关键字,
作用是为一种数据类型定义一个新的名字
这里的数据类型包括(int、char等等)和自定义的数据类型(struct等)
*/
/*
在单片机开发中,寄存器有8位 16位 32位
int data=0x1234;int是4个字节32位,如果是8位单片机的话可能装不下
所以有了 char data=0x11这样的定义,因为数据类型的表示范围都是有
重合的地方的,比如0~128 int float double char 这四个数据类型都
表示
但是char表示数字就有了以下的写法
typedef unsigned char u_int8;表示(0~255这个区间的数)
typedef unsigned short int u_int16;
typedef unsigned short int u_int32;
这样定以后就可以用u_int8来代替unsigned char
u_int16 nsigned short
u_int32 unsigned short
然后就可以:u_int8 data=10;
u_int16 data2=20;
u_int32 data3=30;这种定义方式了
*/
/*
typedef struct Student
{
int score;
char* name;
void (*p)(struct Student stu1);//定义一个函数指针要求参数类型是结构体类型的
//可以用struct Student但是不可以用STU stu1
//因为STU定义在结构体外边
}STU,*PSTU;//通常使用typedef重命名结构体时,会命名名称和指针
*/
/*
typedef struct Student STU,*PSTU;也可以这样重命名结构体
*/
typedef struct//也可以将Student去掉,直接给结构体命名
{
int score;
char* name;
void (*p)(struct stu);
}STU,*PSTU;//通常使用typedef重命名结构体时,会命名名称和指针
int main()
{
STU stu1;//直接可以用命名后的STU代替struct Student
stu1.score=100;
printf("%d\n",stu1.score);
PSTU stu2;//这个stu2就代表结构体指针,这行代码等同于struct Student* stu2
//PSTU就等同于struct Student*
stu2=(PSTU)malloc(sizeof(STU));
stu2->score=99;
printf("%d\n",stu2->score);
system("pause");
return 0;
}