嵌入式C语言基础(四)

为什么要用结构体?
在实际问题中,一组数据往往具有不同的数据类型。例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值