C语言结构体变量和结构体数组-学习笔记(十六)

一、结构体变量

1、结构体概念

将不同类型的数据组合成一个有机的整体即为结构体。结构体由许多组织在一起的数据项组成,这些数据项不需要属于同一类型。

2、结构体类型及结构体变量定义

(1)结构体类型声明
struct 结构体名
{
成员表列;
};
①–结构体类型声明形成了创建结构体变量的模板。
②–声明模板时若没有定义变量是没有开辟内存空间的。
③–结构体中的变量称为结构体元素或结构体成员。
(2)结构体类型定义及结构体变量定义
方法一:先定义结构体类型再定义变量名

struct staff   // struct staff是结构体类型符,是结构体类型名。
{
	int  num;
	char  name[20];
	char  sex;
};
struct staff staff1,staff2;
//定义staff1和sudent2为struct staff类型变量//struct staff staff1;
//struct staff staff2;
//注:在main函数中声明结构体变量, struct关键字不能丢

方法二:可用符号常量代表一个结构体类型

#define ST_STAFF struct staff
ST_STAFF 
{
	int  num;
	char name[20];
	char sex;
};
ST_STAFF staff1, staff2;

方法三:在定义类型的同时定义变量

struct staff
{
	int  num;
	char  name[20];
	char  sex;
} staff1, staff2;

方法四:使用typedef关键字

typedef struct MyStaff//数据域类型
{
	char acNo[10];//员工工号
	char acName[10];// 员工姓名
	char acPwd[8];//登录密码
	char iRole;
	char iValid;
	char remain[20];//余额
}STAFF_T;
MyStaff staff1,staff2;

3、结构体变量初始化

方式一:

struct staff
{
	long int num;
	char name[20];
	char sex;
	char addr[20];
}b={9801,"Wang hong",'W',"2 Linggong Road"};

方式二:

struct staff
{
	long int num;
	char name[20];
	char sex;
	char addr[20];
}; 
struct staff b={9801,"Wang hong",'W',"2 Linggong Road"};

int main(int argc,char const *argv[])
{
	printf("No.:%ld name:%s sex:%c address:%s\n",b.num,b.name,b.sex,b.addr);
	//No.:9801 name:Wang hong sex:W address:2 Linggong Road
	return 0;
}

在C99标准中,还支持给指定元素赋值(就像数组一样):

struct Info info = {
    .name = "Harris",
    .year = 2019
};

对于没有被初始化的成员,则数值型成员初始化为0,字符型成员初始化为’\0’。

4、结构体变量的赋值

(1)可以使用一条简单的赋值语句将一个结构体变量的值赋给另一个相同类型的结构体变量。
如:staff1 和 staff2 是同一类型的结构体变量,则staff2 = staff1
例1:

struct staff
{
	int num;
	char name[10];
	int a[3];
} stu1={1, "abcd",{1,2,3}},stu2={2, "123456",{4,5,6}};
int main(int argc,char const *argv[])
{
	int i;
	staff2=staff1;  
	printf("%d %f %s %d %d %d\n", staff2.num, staff2.name,staff2.a[0],staff2.a[1],staff2.a[2]);
	//1 99.000000 abcd 1 2 3
	return 0;
}

(2)在不能进行直接赋值的情况下,可以使用内置函数 memcpy(),该函数用于复制某内存段的值到另一内存段里。
函数原型:

char *memcpy(char *dest,const char *source,int size)
//(目标内存块首地址; 源内存块首地址; 要复制的字节数)
//返回值:成功时返回dest,否则返回NULL
//如:memcpy (&staff2, &staff1, sizeof(struct cat));

5、结构体变量引用

(1)结构体类型变量的输出和运算
①不能将一个结构体变量作为一个整体进行输入和输出;只能对其各个成员分别输入输出,
引用形式:结构体变量名.成员名。
结构体元素通过使用点运算符(.)来引用,这个运算符也称为成员运算符。

printf("%d",staff1.num); //输出 10010

②若成员本身又属一个结构体类型,只能对最低级的成员进行赋值或存取以及运算。换句话说,结构体中的成员也是一个结构体类型的变量。

struct date
{
	int month;
	int day;
	int year;
};

struct staff
{
	int num;
	char name[20];
	int age;
	struct date birthday;
}staff1,staff2;
//引用:staff1.birthday.year

③对成员变量可以像普通变量一样进行各种运算。
可以引用成员的地址,也可以引用结构体变量的地址。
如:

sumage=staff1.age+staff2.age;
scanf(%d”,& staff1.num); // 输入staff1.num的值
printf(%o”,&staff1); // 输出staff1的首地址

结构体中的成员,可以单独使用,其作用与地位相当于普通变量。
成员名可以与程序中的变量名相同,二者不代表同一对象。
例1:

#include <stdio.h>
#include <stdlib.h>//malloc的头文件
#include <string.h>

struct student
{
	int score;
	char name[128];
	int xuehao;
	void (*printfo)(int,char*,int);//定义函数指针
};

void printfoReal(int score,char *name,int xuehao)//输出结构体信息
{
	printf("name=%s,id=%d,grade=%d\n",name,xuehao,score);
}

int main(int argc,char const *argv[])
{
	struct student st1;//1.定义结构体变量
	st1.score=100;
	strcpy(st1.name,"cefeng");
	st1.xuehao=10086;
	st1.printfo=printfoReal;//2.函数指针指向函数
	st1.printfo(st1.score,st1.name,st1.xuehao);//3.调用函数指针指向的函数
	while(1);
	return 0;
}

(2)结构体变量作为参数
例1:结构体变量按值传递

struct stu_t
{
	char id[4];
	char name[21];
};
void Test(struct stu_t e)
{
	strcpy(e.id,"012");
	strcpy(e.name,"New Name");
	printf("%s,%s\n",e.id,e.name); //调用后再打印出e员工的信息
}
int main(int argc,char const *argv[])
{
	struct stu_t e={"001","Old Name"};
	printf("%s,%s\n",e.id,e.name);//打印出原来的值
	Test(e);  //将结构体变量作为参数传递
	printf("%s,%s\n",e.id,e.name); //调用后再打印出e员工的信息
	return 0;
}
//程序执行结果:
//001,Old Name
//012,New Name
//001,Old Name

(3)结构体变量作为返回值
例1:

struct stu_t
{
	char id[4];
	char name[21];
};
struct stu_t Test()
{
	struct stu_t e ={"007","Zhang San"};
	return e;
}
int main(int argc,char const *argv[])
{
	struct stu_t e=Test();
	printf("%s,%s\n",e.id,e.name);//007,Zhang San
	return 0;
}

二、结构体数组

1、概念

每个数组元素都是一个结构体类型的数据。访问数组 books 的第五个元素中的变量 author:books[4].author。

2、结构体数组的定义:

方式一:

struct staff
{
	int num;
	char name[20];
	char sex;
};
struct staff staff1[3];

方式二:

struct staff
{
	int num;}staff1[3];

3、结构体数组的初始化
结构体数组初始化通常用一对大括号将其元素值列表括起来进行,一般形式是在定义数组后面加上:={初值表列};

struct staff
{
	int num;
	char name[20];
	char sex;
}stu[3]={{111,"Li",'M'},{},{}};
//也可:
struct staff
{
	int  num;};
struct staff staff1[]={{},{},{}};

4、结构体数组应用
例1:

#include <stdio.h>
#include <stdlib.h>//malloc的头文件
#include <string.h>

struct student 
{
	int score;
	char name[128];
	int xuehao;
	void (*printfo)(int,char*,int);//定义函数指针
};

void printfoReal(int score,char *name,int xuehao)//输出结构体信息
{
	printf("姓名: %s,学号: %d,成绩: %d\n",name,xuehao,score);
}

int main(int argc,char const *argv[])
{
	int i;
	struct student stus[3];//定义结构体数组
	printf("%d,%d\n",sizeof(stus)/sizeof(struct student),sizeof(stus)/sizeof(stus[1]));
	for(i=0;i<sizeof(stus)/sizeof(struct student);i++)//逐个结构体变量赋值
	{
		printf("\n请输入第%d学生的学号:",i+1);
		scanf("%d",&stus[i].xuehao);
		printf("\n请输入第%d学生的姓名:",i+1);
		scanf("%s",&stus[i].name);
		printf("\n请输入第%d学生的成绩:",i+1);
		scanf("%d",&stus[i].score);
		stus[i].printfo=printfoReal;//结构体中函数指针指向具体函数
	}
	for(i=0;i<sizeof(stus)/sizeof(struct student);i++)//调用输出结构体信息函数
	{
		stus[i].printfo(stus[i].score,stus[i].name,stus[i].xuehao);
		//调用函数指针指向的函数
	}
	while(1);
	return 0;
}

例2:

#include <stdio.h>
#include <stdlib.h>//malloc的头文件
#include <string.h>

struct student
{
	int id;
	int score;
	char name[128];
	void (*printfo)(int,char*,int);//定义函数指针
};

void printfoReal(int id,char *name,int score)//输出结构体信息
{
	printf("我的姓名:%s,学号:%d,成绩:%d\n",name,id,score);
}

int main(int argc,char const *argv[])
{
	int i;
	int total=3;
	struct student best;
	struct student worst;
	struct student stus[10];
#if 0
	//1.要求输入总人数
	printf("please input all persons:");
	//linux下可如此写,window下变量需写在最前面
	scanf("%d",&total);
	struct student stus[total];
#endif
	//2.一个一个输入学生信息
	for(i=0;i<total;i++)
	{
		printf("请输入第%d学生的姓名: ",i+1);
		scanf("%s",stus[i].name);
		printf("请输入第%d学生的学号: ",i+1);
		scanf("%d",&stus[i].id);
		printf("请输入第%d学生的成绩: ",i+1);
		scanf("%d",&stus[i].score);
		stus[i].printfo=printfoReal;//结构体中函数指针指向具体函数
	}
	//3.查找最高分,最低分
	best=worst=stus[0];
	for(i=0;i<total;i++)
	{
		if(best.score<stus[i].score)
		{
			best=stus[i];
		}
		if(worst.score>stus[i].score)
		{
			worst=stus[i];
		}
	}
	//输出结果
	for(i=0;i<total;i++)//调用输出函数
	{
		stus[i].printfo(stus[i].id,stus[i].name,stus[i].score);
		//调用函数指针指向的函数
	}
	best.printfo(best.id,best.name,best.score);//调用函数指针指向的函数
	worst.printfo(worst.id,worst.name,worst.score);//调用函数指针指向的函数
	while(1);
	return 0;
}

三、typedef 关键字使用

1、概念

(1)定义新的数据类型名,它并没有新建数据类型,而只是为已有的类型定义新的名字(别名), 目的是为提高程序可读性。
(2)typedef用于定义各种类型名,不能用于定义变量。
(3)typedef 只是对已经存在的类型给予新的名称,不能创建新的类型。
可以定义一组类型放在一个头文件中,用#include包含进来,供所有程序使用。
(4)一般形式:typedef 数据类型名(已有定义) 新别名(习惯用大写)
如:typedef type name;

2、用法

(1)简单数据类型

typedef  float  DATA ;     
DATA a,b;//=float a,b;

(2)数组

typedef  char  STR_T[20];   定义STR_T是具有20个元素的字符数组
STR_T  s1;//=char s1[20];

(3)指针

typedef  float *PT;        定义PT是指向float型数据的指针
PT  p; //=float  *p;

typedef  char *STR_P;
STR_P  p,s[10]; 
//p是指向char型数据的指针,s是指向char型数据的指针数组。

(4)函数

typedef  char FUN( );   定义FUN是具有char型返回值的函数 
FUN  a;//=char a( );

四、结构体占用内存字节数计算

1、sizeof运算符

(1)概念
一个结构体变量所占用内存空间的字节数可以用sizeof运算符求出。
sizeof运算符是求字节运算符,用于计算运算对象在内存中所占字节的多少。
sizeof运算符是一元运算符,结合方向从右到左。
使用sizeof运算可以提高程序移植性。
(2)用法
格式:

sizeof(类型标识符)
sizeof(表达式)

例:

int a[10]; //sizeof(a)表示数组a在内存中所占的字节数
int num;  //sizeof(num)表示变量num在内存中所占的字节数
//sizeof(int)表示整型在内存中所占的字节数
//sizeof(struct cat)表示结构体在内存中所占的字节数

32位:char(1) int(4) short(2) float(4) double(8) 指针(4)
64位:char(1) int(4) short(2) float(4) double(8) 指针(8)

2、结构体所占内存字节数概念

(1)以空间最小为原则,小的数据类型放前面,节省空间,以形成一个矩形为准。
(2)结构体对齐原则
①先按数据类型自身进行对齐,再按整个结构体进行对齐,对齐值必须是2的幂,比如1,2,4,8,16;
②数据自身的对齐值通常就是数据类型所占的空间大小,比如int类型占四个字节,那么它的对齐值就是4;
③整个结构体的对齐值一般是结构体中最大数据类型所占的空间;
④如果一个类型按n字节对齐,那么该类型的变量起始地址必须是n的倍数;
⑤计算时按最大成员的大小进行顺序排列计算,不足则补位就补位;
(3)用法
例1:

#include <stdio.h>
#include <stdlib.h>//malloc的头文件
#include <string.h>

void main()
{
	struct test1
	{
		int num1;//4
		char ch;//1
		int num2;//4
	};
	struct test2
	{
		char num1;//1
		char num2;//1
		double num3;//8
	};
	struct test3
	{
		char num1;//1
		double num2; //8
		char num3;//1
	};
	struct test4
	{
		int NO[4];
		char Name[20];
	}; 
	struct test5
	{
		int NO[4];
		char Name[21];
	};
	printf("sizeof(struct test1) =%d\n",sizeof(struct test1));//=12
	printf("sizeof(struct test2) =%d\n",sizeof(struct test2));//=16
	printf("sizeof(struct test3) =%d\n",sizeof(struct test3));//=24
	printf("sizeof(struct test4) =%d\n",sizeof(struct test4));//=36
	printf("sizeof(struct test5) =%d\n",sizeof(struct test5));//=40
	typedef struct
	{
		int a;//4
		char b;//1
		char c;//1
	}STR1;
	printf("sizeof(STR1)=%d\n",sizeof(STR1));//8

	typedef struct
	{
		char a;//1
		int b;//4
		char c;//1
	}STR2;
	printf("sizeof(STR2)=%d\n",sizeof(STR2));//12
}

编辑 2020-03-07 12:30 首次编辑
增改 2020-10-15 10:05 增加结构体指针部分
增改 2021-07-19 23:10 内容结构优化
增改 2021-07-22 23:50 增加实例引用

注:本文旨于作为自己的学习笔记,不作他用。

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值