结构体之探索

版权声明:请尊重每一个人的劳动成果 https://blog.csdn.net/jamenu/article/details/84729336

经过2个月的学习,我们对C语言已经有了一定的了解。
我们都知道变量必须要有数据类型,
列出一些常见的类型

int      //整型
float    //单精度浮点型
double   //双精度浮点型
char     //字符形
unsigned //无符号整形
。。。。。。。。。。。

但是在现实生活中这些数据类型有时候不能准确的反映事物的特点和联系
打个比方
有一辆(我不是老司机)
我们如何去描述它
我们能用一个数字吗?
我们能用只用一个字符或一个字符串吗?
很显然这样不能准确的描述一辆车
假如有一辆法拉利
首先要有品牌
再是型号
还要有最高速度
百公里加速时间
车身长宽
油箱体积以及油耗
等等。。。。。。
(最后还有价格)

显然我们的上述个个数据都不能全面的描述
这时候我们就要用到我们的结构体的知识

如何定义一个结构体类型

struct 结构体名
{ 成员列表 };

成员列表也叫做域表,每一个成员是结构体中的一个域 成员命名规则和变量相同

成员的定义

类型名 成员名
下面我以上面的法拉利为例声明一个结构体

struct Ferrari
{
 char brand[];         //品牌
 
 int num;                //型号
 
 int max_speed;         //极速
 
 float add_speed_0to100;   //百公里加速时间
 
 float oil_cost;         //油耗
 
 int price;              //价格
};

下面我们要定义结构体变量,
和我们的基本数据类型一样,可以在初始化时;
也可以需要时定义如

struct Ferrari ferrari458

这个ferrari458就是一个结构体变量了
下面是给结构体变量赋值;
我们必须遵循每一个成员的数据类型

ferrari458={"Ferrari",458,320,5.7,11.3,5000000};

当然也可以对某一个成员初始化

ferrari={.num=458};注意.是结构体运算符,注意它的优先级,在以后与指针在一起时要分辨

现在写一个简单的小程序把以上的知识实践以下

#include <stdio.h>
struct Ferrari        //定义一个结构体类型
{
 char brand[30];       //规定其中的成员及类型
 
 int num;
 
 int max_speed;
 
 float add_speed_0to100;
 
 float oil_cost;
 
 int price; 
};
int main()
{
 char c;
 
 printf("请输入数据\n");
 struct Ferrari ferrari458,ferrari488;   //定义2个结构体变量
 
 scanf("%s%d%d%f%f%d",ferrari458.brand,&ferrari458.num,&ferrari458.max_speed,&ferrari458.add_speed_0to100,&ferrari458.oil_cost,&ferrari458.price);
 //为这两个结构体变量分别赋值
 c=getchar();
 
 scanf("%s%d%d%f%f%d",ferrari488.brand,&ferrari458.num,&ferrari488.max_speed,&ferrari488.add_speed_0to100,&ferrari488.oil_cost,&ferrari488.price);
 
 if(ferrari458.price>ferrari488.price)   //加以控制,判断并输出成员price大的结构体变量
 {
  printf("brand=%s\nnum=%6d\nmax_speed=%6d\nadd_speed_0to100%6f\noil_cost%5.1f\nprice=%9d",ferrari458.brand,ferrari458.num,ferrari458.max_speed,ferrari458.add_speed_0to100,ferrari458.oil_cost,ferrari458.price);
 }
 else
 {
  printf("brand=%s\nnum=%6d\nmax_speed=%6d\nadd_speed_0to100%6f\noil_cost%5.1f\nprice=%9d",ferrari488.brand,ferrari488.num,ferrari488.max_speed,ferrari488.add_speed_0to100,ferrari488.oil_cost,ferrari488.price);
 }
 
 return 0;
}

下面贴出运行截图
在这里插入图片描述

(在编译器左边的列表明确的标明了结构体类型和成员)

让我们进一步的思考,既然数据类型都可以定义数组;
那我们的结构体类型也能定义结构体数组
那我们知道一个每个数据类型都有其特定的分配的内存空间
整形是 4个字节
单精度浮点 是4个字节
双精度浮点是8个字节
字符形变量1个字节
。。。。。。。。。。
在这里插入图片描述

那数组所占的内存空间为
s i z e o f ( ) sizeof(数据类型) *数组长度

因为一个结构体变量的内存占用为其每个类型成员的类型的和,就拿上面的结构体Ferrari来说,它的一个变量的内存占用为
s i z e o f ( b r a n d [ 30 ] ) + s i z e o f ( n u m ) + s i z e o f ( m a x s p e e d ) + s i z e o f ( a d d s p e e d 0 t o 100 ) + s i z e o f ( o i l c o s t ) + s i z e o f ( p r i c e ) sizeof(brand[30])+sizeof(num)+sizeof(max speed)+sizeof(add-speed-0to100)+sizeof(oil-cost)+sizeof(price)

加起来一共50Byte
现在定义一个长度为2的结构体数组

struct Ferrari nfc[2]={{"ferrari",458,320,5.4,11.2,5000000},{"ferrari",488,350,4.6,12.5,6000000}}

这个数组的内存占用为
50 2 = 100 B y t e 50*2=100Byte

下面介绍结构体指针

和其他它类型的指针变量的定义相同
定义如下

struct Ferrari *p

这样就完成了结构体指针的定义,p就是一个指向结构体类型的指针,
它指向struct Ferrari

这个结构体指针既可以指向结构体数组和变量也可以指向结构体变量中的成员
当结构体指针指向结构体数组和变量时,对其进行地址运算时,
如p++,p将指向数组中的下一个结构体变量地址增加了一个结构体变量所占的字节数,和在其他类型数组时一样
在我们表示指针所指向的变量的值的时候
我们用表示
p *p

如果我们要引用和表达一个结构体变量中具体成员的数据,我们有以下几种方法
1. f e r r a r i 458. n u m 1.ferrari458.num
2. ( p ) . n u m 2.(*p).num
3. p &gt; n u m 3.p-&gt;num

以上三种方法等价
p->num表示p所指的结构体变量中的num成员的值
PS在用第2种方法时要注意不要用错括号,因为.作为结构体运算符的优先级要高于*

现在将上面的程序改编一下
如下

#include <stdio.h>
struct Ferrari
{
 char brand[30];
 
 int num;
 
 int max_speed;
 
 float add_speed_0to100;
 
 float oil_cost;
 
 int price; 
};
int main()
{
 char c;
 
 printf("请输入数据\n");
 struct Ferrari ferrari458,ferrari488,*p1,*p2;//多定义两个结构体指针p1和p2
 
 p1=&ferrari458;      //p1指向结构体变量458的地址
 
 p2=&ferrari488;       //p2指向结构体变量488的地址
 
 scanf("%s%d%d%f%f%d",ferrari458.brand,&ferrari458.num,&ferrari458.max_speed,&ferrari458.add_speed_0to100,&ferrari458.oil_cost,&ferrari458.price);
 
 c=getchar();
 
 scanf("%s%d%d%f%f%d",ferrari488.brand,&ferrari488.num,&ferrari488.max_speed,&ferrari488.add_speed_0to100,&ferrari488.oil_cost,&ferrari488.price);
 
 if(ferrari458.price>ferrari488.price)     //用指向运算符来引用结构体变量成员的值
 {
  printf ("brand=%s\nnum=%6d\nmax_speed=%6d\nadd_speed_0to100=%6f\noil_cost%5.1f\nprice=%9d",p1->brand,p1->num,p1->max_speed,p1->add_speed_0to100,p1->oil_cost,p1->price);
 }
 else
 {
  printf("brand=%s\nnum=%6d\nmax_speed=%6d\nadd_speed_0to100=%6f\noil_cost%5.1f\nprice=%9d",p2->brand,p2->num,p2->max_speed,p2->add_speed_0to100,p2->oil_cost,p2->price);
 }
 
 return 0;
}

下面放出运行截图
在这里插入图片描述
可以看出这个效果是和前一个程序一样的

下面提出更进一步的问题
用结构体变量和结构体变量的指针作函数参数
我在前几篇博文中也有些关于函数的内容
与其它数据类型一样
我们可以用结构体变量结构体变量的成员来做实参
在用结构体变量做实参时,形参必须是同类型的结构体变量
同样也可以分为值传递址传递的两种方式
在现在看来还是址传递的方式用结构体指针做实参
更加的方便,也可以节省出实参传入形参时所开辟的内存
节省了运行空间,提高了效率,特别是在结构体比较复杂和庞大的时候。

现在是23点半今天的工作暂时停在这里,明天继续更新
现在我将它发布,省的再花时间来审核,我打算在这里继续写到链表,会在比较闲的时候翻出来写的

今天我们继续探讨C语言关于结构体的知识;
上次说到用结构体变量和结构体指针来做函数的参数,
现在写一个程序

#include <stdio.h>
#define N 5           //定义N为5,我们输入5组数据
void searchmax(struct Student b[]);  //声明找最高分函数
struct Student
{
     int num;
 
     char name[20];
 
     float score;
};
struct Student input_dights(struct Student a[]);  //声明数据输入函数
int main()
{
    struct Student five[N];
    input_dights(five);     //调用数据输入函数,用数组名做为实参,进行地址传递
    searchmax(five);       //调用找最高分函数
    return 0;
}
void searchmax(struct Student b[])    //定义找最高分函数
{
 int i,j=0;   //定义j来做一个下标,并初始化为0
 
 for(i=0;i<N;i++)   //利用for循环将j和每一个结构体数组元素的score成员和b[j]的score成员比较,并得到score成员更大的结构体元素的下标
 {
      if(b[i].score>b[j].score)
      {
           j=i;
      }
 
 }
 
 printf("最高分是\n");      //循环结束的到分数最高的元素的下标
 
 printf("%d%s%f",b[j].num,b[j].name,b[j].score);    //并将这个元素输出
 
}
struct Student input_dights(struct Student a[])    //定义输入数据函数
{
    int i;
    float average = 0;  //初始化一个平均值
 
    printf("请输入学生的数据\n");
 
    for(i=0;i<N;i++)
    {
             scanf("%d%s%f",&a[i].num,&a[i].name,&a[i].score);
  
            average+=a[i].score;   //循环输入数据的同时进行累加
     }
 average = average/5;     //除5取平均值     
 printf("平均分为%5.1f",average);
}

下面给出程序的运行截图
在这里插入图片描述

现在我们将主函数稍加修改,请看

int main()
{
 struct Student five[N],*p1;    //我定义了一个结构体指针
 p1 = five;                 //让他指向结构体数组five
 input_dights(p1);          //现在用指针作为函数的实参
 searchmax(p1);
 return 0;
}

所实现的效果是一样的

这两种方法本质上是一样的现在回顾一下我们在学数组那一章的知识;
我们都知道在C语言中数组的长度不能动态的定义
如果一个数组要先后存放俩组数据,一组长度200,另一组的长度为10,那么我们依然需要定义一个长度为200的数组,在存放长度为10的数据时将浪费190的内存空间
而我们的链表则没有这种缺点,可以动态的定义
而我们的结构体变量是最为合适的
首先,我们必须了解链表的结构
我把书上的图搬来
在这里插入图片描述链表中每一个元素称为结点
每一个结点必须要包括2种数据
1.实际使用中种要用到的数据
2.下一个结点的地址
当结点C指向结点D时,将他的指向设为空,让它没有指向,整个链表也就结束了,这个结尾也可以称之为表尾
在结构体的成员中我们可以设置1个指针指向结构体的指针变量

#include <stdio.h>
struct Student
{
int num;

float score;

struct Student *next; //结构体指针
}

下面通过一个例子说明这样建立和输出一个链表

#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student) //定义LEN为一个结构体类型的字节长度
struct Student    //定义一个结构体作为链表的载体
{
	long number;
	
	float score;
	
	struct Student*next;
};
int n;                        //定义全局变量N,程序中的各部分和函数都能使用它
struct Student*creat(void)
{
	struct Student*head;     //定义一个头指针
	
	struct Student*p1,*p2;
	
	n=0;
	
	p1=p2=(struct Student*)malloc(LEN);  //在内存中开辟一块长度为LEN的空间,并使连个指针指向这个空间
	
	scanf("%ld,%f",&p1->number,&p1->score);  //输入第1个学生的学号和成绩
	
	head = 0;       //把头指针指向空
	
	while(p1->number!=0)    当输入的number成员的值不为0时执行循环
	{
		n = n+1;
		
		if(n==1)
		{
			head = p1;    //将头指针指向p1
		}
		else
		{
			p2->next = p1;  //再把第一个结点中的next指针变量连接上来
		}
		p2 = p1;
		
		p1 = (struct Student*)malloc(LEN);  //开辟动态存储区,把起始地址赋给p1
		
		scanf("%ld,%f",&p1->number,&p1->score);  //输入其他学生的学号和成绩
		

	}
    p2->next = 0;
		
	return(head);	
}
int main()
{
	struct Student*pt;	
	pt = creat();
	
	printf("\nnum=%ld\nscore = %f\n",pt->number,pt->score);
	
	return 0;
}

暂时先停一下,今天已经写了3个多小时了,现在可以把它发出去
现在体会到每一篇博客都像一个工程,需要日积月累

猜你喜欢

转载自blog.csdn.net/jamenu/article/details/84729336