基础8:结构体

1. 结构体是什么?

1.1 为什么需要结构体

为了表示一些复杂的事物,而普通的基本类型无法满足实际要求;

(1)数据封装(面向对象):当您需要将相关的数据组合在一起时,可以使用结构体来创建一个新的数据类型。这有助于组织和管理数据,并提高代码的可读性和可维护性。
(2)性能考虑:对于需要频繁访问大量数据的应用程序,结构体可能比类更有效率,因为它们可以直接存储数据,而不需要额外的函数调用开销(构造,析构函数)。
(3)简单数据结构:在某些情况下,结构体可以用来表示简单的数据结构,如链表、树等。这些数据结构通常不需要复杂的操作,因此结构体可以提供一种轻量级的解决方案。

总之,结构体是一种灵活且强大的数据类型,它可以根据具体的应用场景和需求来使用。在设计系统时,应该根据性能要求、可靠性要求、以及系统复杂性等因素综合考虑。

1.2 什么叫结构体

把一些基本数据类型组合在一起形成的一个新的复合数据类型叫做结构体

2. 结构体怎么用?

2.1 定义结构体

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

有三种方式:

		   第一种方式:   //这只是定义了一个新的数据类型,并没有定义变量
					struct Student 
		        {              
		        	int age;     
		        	float score; 
		        	char sex;    
		        };                        //最好也最常用的是第一种方式
		    第二种方式:
		    		struct Student2
		        {             
		         	int age;     
		         	float score; 
		         	char sex;    
		        } 	st2;            
		 	第三种方式:
		 			struct 
		        {               
		          	int age;      
		          	float score;  
		          	char sex;     
		        } 	st3;          
    

2.2 定义结构体变量

  1. 赋值和初始化
    (1)定义的同时可以整体赋初值
    (2)如果定义完之后,则只能单个的赋初值
    (3)部分初始化;
			#include <stdio.h>
			struct Student 
		    {              
		        int age;     
		        float score; 
		        char sex;    
		   	};
		    int main(void)                         
			{                                      
				struct Student st = {80, 66.6, 'F'}; //初始化,定义的同时赋初值
				struct Student st2;
				st2.age = 10;
				st2.score = 99;       
				st2.sex = 'F';       //像这样,定义完之后,就只能单个的赋初值
 
				printf("%d %f %c\n",st.age,st.score,st.sex);
				printf("%d %f %c\n",st2.age,st2.score,st2.sex);
                                      
				return 0;                            
			}
//没有初始化的变量为任意值
struct Point3D{
    int x;
    int y;
    int z;  
};
struct Point3D p = {.x=10,.z=20}; 

2.3 如何取出结构体变量中的每一个成员

(1)结构体变量名.成员名
(2) 指针变量->成员名 这种方式更常用
在计算机内部会被转化成 (*指针变量).变量名 的方式来执行

   		#include <stdio.h>
   		struct Student            
   	    {              									
   	        int age;     								
   	        float score;======= 定义的结构体部分  	
   	        char sex;    								
   	   	};			  //分号千万不能省			  
   	    int main(void)                         
   		{                                      
   			struct Student st = {80, 66.6F, 'F'}; //初始化,定义的同时赋初值,st是结构体struct Student定义的变量
   			struct Student *pst = &st; //&st不能改成st  
   									//可以把 struct Student * 类似于是指针变量名左边的 int * 
   			pst->age = 99; //指针的这种方式,也是第二种方式
   			st.score = 66.6f; //直接赋值这种方式,是第一种方式  
   							//66.6在c语言中默认是double,如果希望默认一个实数是float类型,则必须在末尾加f或者F
   							//因此66.6是double,66.6f或66.6F是float类型

   			printf("%d %f\n",st.age,pst->score);
   			return 0;                            
   		}

解析:
1.pst->age 在计算机内部会被转化成(*pst).age 这是一种硬性的规定
2.pst->age 等价于 (*pst).age 也等价于 st.age
3.所以pst->age = 99;就相当于 st.age = 99;
4.pst->age 的含义:
pas所指向的是struct Student结构体变量中的age这个成员

2.4 .结构体变量的运算,赋值

结构体变量不能相加,不能相减,也不能相互乘除
但结构体变量可以相互赋值:数组不能直接赋值;
--------------------------
struct Student st1,st2;
st1 + st2 st1 * st2 st1 / st2 都是错误的
st1 = st2 或者 st2 = st1 都是正确的

struct Student student1;
struct Student student2;

strcpy(student1.name,"张三"); 
student1.age = 19;
student1.score = 90.5;

student2 = student1;

printf("姓名\t年龄\t分数\n");
printf("%s\t%d\t%.1f\n",student1.name,student1.age,student1.score);
printf("%s\t%d\t%.1f\n",student2.name,student2.age,student2.score);

2.5 .结构体操作

2.5.1 取地址

结构体名不是结构体变量的地址,必须使用&获取地址。
数组名即地址。

#include <stdio.h>
#include <string.h>
struct Point3D{
    int x;
    int y;
    int z;  
};
void main()
{
	struct Point3D p = {1,2,3};
	printf("&p = %p\n",&p);
	printf("&(p.x) = %p\n",&p.x);
	printf("&(p.y) = %p\n",&p.y);
	printf("&(p.z) = %p\n",&p.z);
}
2.5.2 传参

整个结构体作为参数的值传入函数。这时候在函数内新建一个结构体变量并复制值。结构体可以作为返回值,也是结构体整体复制。推荐使用结构体指针作为函数参数来传递;

void Print(struct Point3D p){
    printf("(%d,%d,%d)",p.x,p.y,p.z);
}

2.6 .结构体指针

(1)通过修改结构体指针q指向的成员,也会改变结构体变量p成员的值。

struct Point3D p = {1,2,3};
struct Point3D* q = &p;
printf("(%d,%d,%d)",q->x,q->y,q->z); // 等同于printf("(%d,%d,%d)",(*q).x,(*q).y,(*q).z);

(2)结构体指针作为参数
在C语言中,通常会将结构体指针作为参数传入函数,尤其是当传递的参数类型比地址大的时候,可以使用这种方式既能传递较少的字节数。

void Print(struct Point3D* p){
    printf("(%d,%d,%d)",p->x,p->y,p->z);// 等同于printf("(%d,%d,%d)",(*q).x,(*q).y,(*q).z);
}

完整代码

#include <stdio.h>
#include <string.h>
			struct Student            
		    {              									
		        int age;     								
		        char sex;    	
		        char name[100];    								
		   	};			  //分号千万不能省
		   	void InputStudent(struct Student *);   //InputStudent函数的声明
		   	void OutputStudent(struct Student *);  //OutputStudent函数的声明
		    int main(void)                         
			{                                      
				struct Student st;    //第12行
				
				InputStudent(&st); //对结构体变量输入 必须发送st的地址
			//	printf("%d %c %s\n",st.age, st.sex, st.name);
				OutputStudent(&st);  //对结构体变量输出  可以发送st的弟子也可以直接发送st的内容
									//但为了减少内存的耗费,也为了提高执行速度,推荐发送地址
				return 0;                            
			}
			void InputStudent(struct Student *pstu)    //pstu只占4个字节
			{
				(*pstu).age = 10;
				pstu->sex = 'F';
				strcpy(pstu->name,"张三");   //或者可以写成 strcpy((*pstu).name ="张三");
			}
			void OutputStudent(struct Student *ss)
			{
				printf("%d %c %s\n",ss->age, ss->sex, ss->name);
			}


 
 
			/*本函数无法修改主函数第12行st的值,所以本函数是错误的
			void InputStudent(struct Student stu)
			{
				stu.age =10;
				strcpy(stu.name,"张三");  //不能写成stu.name = "张三",必须通过<string.h>的头文件来实现
				stu.sex = 'F';
			}
			*/

2.7 .结构体数组

struct Point3D ps[] = {{1,2,3},{1,1,1},{0,0,0}};
for(int i=0;i<3;++i){
    printf("(%d,%d,%d)\n",ps[i].x,ps[i].y,ps[i].z);
}

完整代码:

动态构造存放学生信息的结构体数组
动态构造一个数组,存放学生的信息
然后按分数排序输出
------------------------------------------------------------------------

#include <stdio.h>
#include <malloc.h>
			struct Student
			{
			        int age;
			        float score;
			        char name[100];
			};
			int main(void)
			{
			        int len;
			        struct Student *prr;
			        int i,j;
			        struct Student t;
  //动态的构造一维数组
			        printf("请输入学生的个数:\n");
			        printf("len = ");
			        scanf("%d",&len);
			        prr = (struct Student *)malloc(len*sizeof(struct Student));   //为输入的学生数量动态分配内存空间
			        				//一个结构体是struct Student,代表一个学生,所有需要(申请学生数量 * 请求单个学生内存)
 
			        //学生信息录入
			        for(i=0;i<len;++i)
			        {
			                printf("请输入第%d个学生的信息:\n",i+1);
			                printf("Student age = ");
			                scanf("%d",&prr[i].age);
 
			                printf("Student score = ");
			                scanf("%f",&prr[i].score);
 
			                printf("Student name = ");
			                scanf("%s",prr[i].name);  //name是数组名,本身就已经是数组首元素的地址,所以不用再前面加取地址符&
			        }
			        //对录入的学生成绩进行升排序
			        for (i=0;i<len-1;++i)
			        {
			                for(j=0;j<len-1-i;++j)
			                {
			                        if(prr[j].score >prr[j+1].score)    //大于 > 表示升序,小于 < 表示降序
			                        {
			                                t=prr[j];                      //默认互换的是整体
			                                prr[j]=prr[j+1];
			                                prr[j+1]=t;
			                        }
			                }
 
			        }
			        //输出
			        printf("\n\n学生的信息是:\n");
			        for(i=0;i<len;++i)
			        {
			                printf("第%d个学生的信息是:\n",i+1);
			                printf("Student age = %d\n",prr[i].age);
			                printf("Student score = %f\n",prr[i].score);
			                printf("Student name = %s\n",prr[i].name);
			        }
			        return 0;
			}
			程序运行的结果为:
			-------------------------------------------------------------------------------------------------------------------
			请输入学生的个数:
			len = 3
			请输入第1个学生的信息:
			Student age = 10
			Student score = 20
			Student name = zhangsan
			请输入第2个学生的信息:
			Student age = 20
			Student score = 50
			Student name = lisi
			请输入第3个学生的信息:
			Student age = 30
			Student score = 10
			Student name = wangwu


			学生的信息是:
			第1个学生的信息是:
			Student age = 30
			Student score = 10.000000
			Student name = wangwu
			第2个学生的信息是:
			Student age = 10
			Student score = 20.000000
			Student name = zhangsan
			第3个学生的信息是:
			Student age = 20
			Student score = 50.000000
			Student name = lisi
			-------------------------------------------------------

以上就定义了一个Student类型的结构体变量student1,这个变量就可以代表一个学生,他拥有姓名、年龄、成绩这三个成员。

2.8 .结构体嵌套

(1)结构体嵌套可以看做路径,结构体相当于目录/文件夹,基础变量相当于文件。之间的.或者->相当于分割符\
(2)小结构体:内层的需要先定义;否则出现: error: field ‘course’ has incomplete type
struct Course course; // 结构体Student嵌套结构体Course
example:

struct Line{
    struct Point3D start;
    struct Point3D end;
};
struct Line line = {{1,1,1},{0,0,0}};
// 使用
printf("(%d,%d,%d)~(%d,%d,%d)",
line.start.x,line.start.y,line.start.z,
line.end.x,line.end.y,line.end.z);

struct Line* p = &line;
printf("(%d,%d,%d)~(%d,%d,%d)",
p->start.x,p->start.y,p->start.z,
p->end.x,p->end.y,p->end.z);

(2)结构体含有结构体数组

struct Triangle{
    struct Point3D p[3];
};

struct Triangle t = {{{1,2,3},{1,1,1},{0,0,0}}};

***example

#include <stdio.h> 
struct Course {
    char code[10];
    float score;
};

// 定义结构体Student和Course
struct Student {
    int id;
    char name[50];
    struct Course course; // 结构体Student嵌套结构体Course
};
 

 
int main() {
    // 创建Student结构体实例
    struct Student stu;
    stu.id = 1;
    snprintf(stu.name, sizeof(stu.name), "张三");
    snprintf(stu.course.code, sizeof(stu.course.code), "CS101");
    stu.course.score = 90.5;
 
    // 打印结构体信息
    printf("ID: %d\n", stu.id);
    printf("Name: %s\n", stu.name);
    printf("Course Code: %s\n", stu.course.code);
    printf("Course Score: %.1f\n", stu.course.score);
 
    return 0;
}

2.9 .结构体大小

结构体大小:结构体成员按照类型进行4字节补齐,如果超过4字节,不用补齐;
空结构体为1;

3 .使用结构体获取时间

在C语言标准库time.h中,有一个tm结构体用来获取时间。

struct tm {
    int tm_sec; /* 秒 – 取值区间为[0,59] */
    int tm_min; /* 分 - 取值区间为[0,59] */
    int tm_hour; /* 时 - 取值区间为[0,23] */
    int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */
    int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */
    int tm_year; /* 年份,其值等于实际年份减去1900 */
    int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */
    int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */
    int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的时候,tm_isdst为0;不了解情况时,tm_isdst()为负。*/
};

ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。

time():获得的日历时间time_t,从公元1970年1月1日0时0分0 秒算起至今的UTC时间所经过的秒数。
gmtime():将日历时间time_t转化为UTC时间(世界标准时间,即格林尼治时间)tm结构体。
localtime():将日历时间time_t转化为本地(当前时区)时间tm结构体。

获取当前时间

#include <stdio.h>
#include <time.h>

int main(){
    time_t now = time(NULL); // a,需要先获取当前时间;秒数
    // 获取UTC时间
    struct tm* utc = gmtime(&now);
    printf("UTC:%d-%d-%d %d:%d:%d\n",utc->tm_year+1900,utc->tm_mon+1,utc->tm_mday,utc->tm_hour,utc->tm_min,utc->tm_sec);
    // 获取本地时间
    struct tm* local = localtime(&now);
    printf("Local:%d-%d-%d %d:%d:%d\n",local->tm_year+1900,local->tm_mon+1,local->tm_mday,local->tm_hour,local->tm_min,local->tm_sec);
}

两个常用的时间函数:

时间结构体转字符串strftime()
字符串解析成时间结构体strptime()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值