结构

一、结构的定义

结构是有struct关键字和结构模板以及结构中的成员组成:

struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
}; 

在上面面的代码中,names叫做结构模板的名称,整个所表示的就是结构模板,ma,line,file[30],sie叫做这个结构的成员。

二、声明结构模板

1.声明结构模板一般放在主函数的外部进行定义声明,结构模板中的成员之间要用分号分隔开。

2.并且,在声明结构模板的时候,每一个成员都不能进行初始化。

3.声明结构模板不需要在模板名称后面加等于号。

重点:

在结构模板中定义声明字符串的时候,用数组形式,而不用字符串形式,原因是数组形式声明的字符串会储存在结构变量所在的内存中,而用指针形式声明的字符串不会储存在结构变量内存中,结构变量内存中储存的只是字符串的地址。

三、声明结构变量

声明结构变量的前提是要有结构模板,通过结构模板来进行结构变量的声明。

#include	<stdio.h>

struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
}; 

int	main(void)
{
	struct names hold=
	{
		3,
		2.3,
		"I love Micy very much!",
		'A',
	};
}

在上面代码中,首先在主函数外面定义声明了names结构模板,然后在主函数里面定义声明了以names为结构模板的结构变量hold,因此hold在进行初始化的时候要遵循names结构模板,并且每个成员之间用逗号隔开,这和定义声明结构模板不相同。

由于结构变量也是变量,因此结构变量也遵循前面讲的储存方式。

声明结构变量,要在结构变量后面加等于号。

四、初始化结构变量以及对结构变量赋值

初始化结构变量有三种方式:

1)对各成员都进行初始化
struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
}; 

int	main(void)
{
	struct names hold=
	{
		3,
		2.3,
		"I love Micy very much!",
		'A',
	};
}

按照个成员之间的顺序,分别对每个成员都进行初始化,并且初始化后个成员之间用逗号隔开。

2)用相同结构模板的结构变量进行初始化
struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
}; 

int	main(void)
{
	struct names hold= 
	{
		3,
		2.3,
		"I love Micy very much!",
		'A',
	};
	struct names buge=hold;

此时,结构变量buge和结构变量hold的内容就完全一样了。

3)指定初始化
//随机
#include	<stdio.h>

struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
	char games[20];
}; 

int	main(void)
{
	struct names hold=
	{
		.line=23.45,
		.file="I love Micy very much!",
		'S',
	};
	printf("print hold.line :%.2f\nprint hold.file :%s\n",hold.line,hold.file);
	printf("print hold.sie :%c\nprint hold.ma :%d\n",hold.sie,hold.ma);
	printf("print hold.games :%s\n",hold.games);
	return 0;
}

打印结果:

print hold.line :23.45
print hold.file :I love Micy very much!
print hold.sie :S
print hold.ma :0
print hold.games :

在hold的初始化中,指定了line,file这两个成员的初始化,(注意指定初始化要在指定的成员前面加上点运算符),因此在打印这两个成员的值的时候,打印出来的就是初始化的值,而在初始化中有'A'这个值,这个值会被初始化给file成员后面的成员(按顺序),未被初始化的成员会自动被初始化成0或者空字符。

4)对结构变量进行赋值
1.用相同结构模板的变量进行赋值
//用相同结构模板的结构变量进行赋值
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};

int main(void)
{
	struct names holds=
	{
		3,
		4.3,
		"I love Micy very much!",	
	};
	struct names memb=holds;
	
	printf("print ma in memb :%d\n",memb.ma);
	printf("print games in memb :%.2f\n",memb.games);
	printf("print datas in memb :%s\n",memb.datas);
	
	return 0;	
} 

打印结果:

print ma in memb :3
print games in memb :4.30
print datas in memb :I love Micy very much!

2.用结构常量进行赋值
//使用结构常量进行赋值操作
#include	<stdio.h>

struct names 
{
	int ma;
	double games;
	char datas[30];
};

int main(void)
{
	struct names hold=(struct names){
		3,
		4.5,
		"I love Micy very much!",
	};
	
	printf("print ma in hold :%d\n",hold.ma);
	printf("print games in hold :%.2f\n",hold.games);
	printf("print datas in hold :%s\n",hold.datas);
	
	return 0;	
} 

打印结果:

print ma in hold :3
print games in hold :4.50
print datas in hold :I love Micy very much!

3.指定成员进行赋值
//指定结构成员进行赋值操作
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};

int main(void)
{
	struct names hold;
	printf("enter ma in hold:\n");
	scanf("%d",&hold.ma);
	printf("print the ma in hold :%d\n",hold.ma);
	printf("print the games in hold :%.2f",hold.games);
	
	return 0; 
} 

打印结果:

enter ma in hold:
3
print the ma in hold :3
print the games in hold :0.00

可以发现,赋值操作和初始化有类似的地方,指定赋值之后,没有值的成员就会初始化成0或者空字符,指定初始化也是如此。

五、访问结构成员和指向结构变量的指针

1)指向结构变量的指针:

1.声明结构模板,系统并不会为结构模板分配内存,也就是说不占据内存。(由于结构模板相当于数据类型,因此使用sizeof()运算符能够知道这个结构模板的大小),而声明结构变量后,这个变量会占据内存,这个内存的大小就是结构模板的大小,这块内存是由各个成员的内存按顺序组成的。(可以理解为各个成员都分配在结构变量这块内存上)

2.由于这个变量拥有内存,也就拥有地址,因此可以声明一个指针,去指向这块内存(指针所储存的一般都是这块内存首字节的地址)

如果要用指针指向他的成员,就用间接成员运算符->进行连接。

//随机
#include	<stdio.h>

struct names 
{
	int ma;
	double line;
	char file[30];
	char sie;
	char games[40];
}; 

int	main(void)
{
	struct names hold=
	{
		.line=23.45,
		.file="I love Micy very much!",
		'S',
		"Love is a wonderful thing!",
	};
	struct names *ptst=&hold;
	printf("print the hold.ma:%d\nprint the hold.line:%.2f\n",ptst->ma,ptst->line);
	printf("print the hold.file:%s\nprint the hold.sie:%c\n",ptst->file,ptst->sie);
	printf("print the hold.games:%s\n",ptst->games);
	
	return 0;
}

打印结果:

print the hold.ma:0
print the hold.line:23.45
print the hold.file:I love Micy very much!
print the hold.sie:S
print the hold.games:Love is a wonderful thing!

*重点:

1.需要注意的是:ptst->ma代表的是变量名称,即结构成员ma的名称,变量名称通过相应的转换说明能直接打印出内容。

ptst->file代表的是数组名称(结构成员file[30]这个字符串数组的名称),因此ptst->file表示的是数组中首字符的地址,使用%s转换说明能够打印出整个字符串。

2.hold是变量名称,必须进行取地址操作才能够赋值给ptst。

2)访问结构成员

访问结构有两种方式,第一种是通过间接成员运算符->,这个运算符只能用在指针上面;第二种是通过点运算符,不能用在指针上面。

//
#include	<stdio.h>

struct names
{
	int ma;
	char games;
	double jir;
	char dates[30];
};
int main(void)
{
	struct names hold=
	{
		1,
		'a',
		2.3,
		"I love yoga very much!",
	};
	struct names *ptstruct=&hold;
	printf("print the  members in the name of struct:\n");
	printf("ma=%d\n",hold.ma);
	printf("games=%c\n",hold.games);
	printf("jir=%.1f\n",hold.jir);//上面这三个都表示的是结构中的变量。 
	printf("datas=%s\n\n",hold.dates);//在这里hold.datas表示的是这个结构中数组首元素的地址。
	
	printf("print the members in point:\n");
	printf("ma=%d\n",ptstruct->ma);
	printf("games=%c\n",ptstruct->games);
	printf("jir=%.1f\n",ptstruct->jir);
	printf("datas=%s\n",ptstruct->dates);
	
	return	0;
}

打印结果如下:

print the members in the name of struct:
ma=1
games=a
jir=2.3
datas=I love yoga very much!

print the members in point:
ma=1
games=a
jir=2.3
datas=I love yoga very much!

1.使用结构变量名称

使用结构变量名称必须使用点运算符,访问哪个成员,就在点后面加上哪个成员的名称。如果是数组名称,代表的是数组首元素的地址。

2.使用指向结构变量的指针

使用指向结构变量的指针必须使用间接成员运算符,访问那个成员就在->后面加上哪个成员的名称。如果是数组名称,代表数组首元素的地址。

六、结构常量

对一个结构变量进行赋值操作的时候,可以将结构常量赋值给这个变量(避免另创建新的结构变量)

#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];
};

int main(void)
{
	struct names hold;
	hold=(struct names){
		2,
		3.4,
		"I love Micy very much!",
	};
	
	printf("ma=%d\n",hold.ma);
	printf("games=%.1f\n",hold.games);
	printf("datas=%s\n",hold.datas);
	
	return 0;
}

打印结果:

ma=2
games=3.4
datas=I love Micy very much!

结构常量的写法:不需要设置出结构名称,只需要把结构模板名称加上括号即可,这样可以避免再设置一个结构变量来进行赋值。

七、结构数组

结构数组就是多个拥有同一结构模板的变量组合。

//定义声明结构数组
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};

int main(void)
{
	struct names trains[5];
	trains[0]=(struct names){
		2,
		3.4,
		"I love Micy very much!",
	};
	printf("ma=%d\n",trains[0].ma);
	printf("games=%.1f\n",trains[0].games);
	printf("datas=%s\n",trains[0].datas);
	
	return 0;
} 

打印结果如下:

ma=2
games=3.4
datas=I love Micy very much!

在上面的代码中,声明了一个具有names结构模板的结构数组,这个数组中拥有五个元素(结构变量),每个元素都是names的结构模板。

八、结构与函数之间联系方式

1)通过结构指针进行联系

把指针作为被调函数的形参,通过指针把原始结构的地址传递给被调函数。

//结构指针与被调函数
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};

void sum_123(struct names *pts);//由于参数中使用了结构模板,所以要把结构模板的声明放在上面,这是易出错的地方。 
void read_(struct names *pts);

int main(void)
{
	struct names hold;
	hold=(struct names){
		2,
		3.4,
		"I love Micy very much!",
	};
	struct names *pts=&hold;
	
	sum_123(pts);
	read_(&hold);
	
	return 0;
}

void sum_123(struct names *pts)//这里表示的是只能传递指向拥有names结构模板的变量的指针(或地址) 
{
	double sum=(pts->ma)+(double)(pts->games);
	printf("print the sum is :%.2f\n",sum);
	
	return;
}

void read_(struct names *pts)//可以传递结构变量指针,或者结构变量地址 
{
	printf("The characters is :%s\n",pts->datas);
	
	return;
}

打印结果:

print the sum is :5.40
The characters is :I love Micy very much!

被调函数中的形参接受的是结构变量的指针或者结构变量的地址,这是调用原始数据。

2)通过结构变量(常量)进行联系

除了结构变量的指针或者地址能够作为实际参数传递,结构变量本身(结构常量)也能够作为实际参数进行传递。

//结构变量(结构常量)本身作为实际参数传递
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};

void sum_(struct names data);
void read_(struct names data);

int main(void)
{
	struct names hold=
	{
		2,
		3.4,
		"I love Micy very much!",
	};
	
	sum_(hold);
	read_(hold);
	
	return 0;
} 

void sum_(struct names data)//这个参数表示接受一个以names为结构模板的结构变量作为实际参数 
{
	double sum=(double)data.ma+data.games;
	
	printf("%.2f\n",sum);
	
	return;
}
void read_(struct names data)
{
	printf("%s\n",data.datas);
	
	return;
}

打印结果如下:

5.40
I love Micy very much!

分析一下:指针作为实际参数进行传递的时候,被调函数通过这个地址直接访问原始数据,而直接将结构变量本身作为实际参数的话,会复制一个副本传递给被调函数,被调函数无法修改原始结构变量(因为被调函数中接受的只是一个副本)。

3)直接将结构成员传递给被调函数

注意:这个结构成员不能是数组,但可以是单一的数据类型,比如int、double、char或者指针或者数组中的一个元素。

//传递结构成员
#include	<stdio.h>

struct names
{
	int ma;
	double games;
	char datas[30];	
};
void sum_(int n);
void read_(double n);
void check_(char n);

int main(void)
{
	struct names hold=
	{
		2,
		3.4,
		"I love Micy very much!",
	};
	
	sum_(hold.ma);
	read_(hold.games);
	check_(hold.datas[0]);
	return 0;
} 
void sum_(int n)
{
	n++;
	printf("print the value of the member :%d\n",n);
	
	return;
}
void read_(double n)
{
	n++;
	printf("print the value of the member :%.2f\n",n);
	
	return;
}
void check_(char n)
{
	printf("print the value of the value :%c\n",n);
	
	return;	
} 

打印结果:

print the value of the member :3
print the value of the member :4.40
print the value of the value :I

如果成员作为实际参数传递的话,被调函数的形参应该符合成员的相应数据类型,并且只能传递具有单个值的数据类型。

4)总结

1.单一的传递结构成员不常用到

2.一般不直接传递结构变量本身,因为这样会创建副本,占据内存。

3.一般采用传递指针的方法,因为指针访问的是原始数据本身,如果不希望指针更改数据,最好加上const关键字,const直接放在struct关键字之前就好。

九、结构数组与函数之间的联系方式

结构数组与函数的联系方式和普通数组与函数的联系相同,都是通过指针进行联系,同样可以写成数组形式和指针形式(本质上都是指针)。

1)数组形式

//结构数组和字符串之间的联系
#include	<stdio.h>
#include	<string.h>

#define SIZE 5

struct names
{
	int ma;
	double games;
	char datas[30];	
};
struct names hold[SIZE];//在这里面,数组名称代表的就是数组首元素的地址,也就是指向结构的指针。 

void input_starray(struct names array[]);
void read_starray(struct names array[]);

int main(void)
{	
	input_starray(hold);
	read_starray(hold);
	
	return 0;	
}

void input_starray(struct names array[])//以数组形式呈现形参,这个形参表示的是接受指向names为模板的结构变量的指针。 
{
	for (int i=0;i<SIZE;i++)
	{
		puts("Please enter a int value :");
		scanf("%d",&array[i].ma);
		puts("Please enter a double value :");
		scanf("%lf",&array[i].games)//需要注意的是,使用scanf()函数输入double类型时,转换说明使用%lf
		getchar();
		puts("Please enter a characters :");
		fgets(array[i].datas,30,stdin);
		{
			int length=strlen(array[i].datas);
			if (array[i].datas[length-1] == '\n')
			{
				array[i].datas[length-1] = '\0';
			}
			else
			{
				while (getchar() != '\n')
				{
					continue;
				}
			}
		}
	}
	
	return;
}

void read_starray(struct names array[])
{
	for (int i=0;i<SIZE;i++)
	{
		printf("\nThe %d group's value:\n",i+1);
		printf("%d\n",array[i].ma);
		printf("%.2f\n",array[i].games);
		puts(array[i].datas);	
	}
	
	return;	
} 

打印结果如下:

Please enter a int value :
1
Please enter a double value :
1.1
Please enter a characters :
qqq
Please enter a int value :
2
Please enter a double value :
2.2
Please enter a characters :
www
Please enter a int value :
3
Please enter a double value :
3.3
Please enter a characters :
eee
Please enter a int value :
4
Please enter a double value :
4.4
Please enter a characters :
rrr
Please enter a int value :
5
Please enter a double value :
5.5
Please enter a characters :
ttt

The 1 group's value:
1
1.10
qqq

The 2 group's value:
2
2.20
www

The 3 group's value:
3
3.30
eee

The 4 group's value:
4
4.40
rrr

The 5 group's value:
5
5.50
ttt

2)指针形式

//结构数组指针形式参数
#include	<stdio.h>
#include	<string.h>

#define SIZE 5

struct names
{
	int ma;
	double games;
	char datas[30];	
};

struct names hold[SIZE];//在这里面,数组名称代表的就是数组首元素的地址,也就是指向结构的指针。
 
void input_starray(struct names *pts);
void read_starray(struct names *pts);

int main(void)
{
	struct names *pts=hold;//注意,在这里,hold是数组名,所以就是一个指针,储存的是首元素的地址。
	input_starray(pts);
	read_starray(pts);
	
	return 0;	
}

void input_starray(struct names *pts)//以数组形式呈现形参,这个形参表示的是接受指向names为模板的结构变量的指针。 
{
	for (int i=0;i<SIZE;i++)
	{
		puts("Please enter a int value :");
		scanf("%d",&(pts+i)->ma);//虽然用的是指针,但是指针访问成员相当于还是成为一个成员对应的变量,因此要加取地址。 
		puts("Please enter a double value :");
		scanf("%lf",&(pts+i)->games);
		getchar();
		puts("Please enter a characters :");
		fgets((pts+i)->datas,30,stdin);//在这里,指针访问datas,所以表示的是数组的数组名,也就代表着数组首元素地止。 
		{
			int length=strlen((pts+i)->datas);
			if ((pts+i)->datas[length-1] == '\n')
			{
				(pts+i)->datas[length-1] = '\0';
			}
			else
			{
				while (getchar() != '\n')
				{
					continue;
				}
			}
		}
	}
	
	return;
}

void read_starray(struct names *pts)
{
	for (int i=0;i<SIZE;i++)
	{
		printf("\nThe %d group's value:\n",i+1);
		printf("%d\n",(pts+i)->ma);
		printf("%.2f\n",(pts+i)->games);
		puts((pts+i)->datas);	
	}
	
	return;	
}  

打印结果如下:

Please enter a int value :
1
Please enter a double value :
1.1
Please enter a characters :
qqq
Please enter a int value :
2
Please enter a double value :
2.2
Please enter a characters :
www
Please enter a int value :
3
Please enter a double value :
3.3
Please enter a characters :
eee
Please enter a int value :
4
Please enter a double value :
4.4
Please enter a characters :
rrr
Please enter a int value :
5
Please enter a double value :
5.5
Please enter a characters :
ttt

The 1 group's value:
1
1.10
qqq

The 2 group's value:
2
2.20
www

The 3 group's value:
3
3.30
eee

The 4 group's value:
4
4.40
rrr

The 5 group's value:
5
5.50
ttt

3)总结

在上面的两段代码中:

1.为什么把数组定义声明放在外面:

原因是主函数在运行的时候所用的内存叫栈(zhan),栈的大小是有限制的,由于结构数组是一种比较大的数组,因此声明在外部是一种比较常见的做法,这样数组就不会占用主程序中的栈。

2.用指针访问成员:

指针是访问成员的一种方式,比如pts->names表示访问的是结构中的names成员,如果names是个变量,那么pts->names整体就表示这个变量,如果names是一个数组,那么pts->names整体就表示数组首元素的地址。

十、伸缩型数组成员

在结构中,我们有一种特殊形式可以使用,那就是伸缩型数组成员,在声明结构模板的时候可以不去声明数组的大小。

1)使用伸缩型数组成员的要求:

1.结构中只能使用一个伸缩型数组成员;

2.结构中的伸缩型数组成员必须位于结构的最末尾;

3.使用malloc()函数。

2)为什么使用伸缩型数组成员

1.首先,使用伸缩型数组成员要用malloc()函数,该函数创建的是动态内存,不在栈中,对于成员非常多的结构,我们可以使用malloc函数(不具有伸缩型数组成员的结构同样能用malloc()函数)。

2.其次:可以自定义成员数组的大小。(最重要的)

3)程序:

//伸缩型数组成员的使用
#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
 
struct names
{
	int ma;
	double games;
	char datas[];//由于要使用伸缩型结构成员,因此方括号中什么也不加。	
};

void input_st(struct names *pt,int size);
void read_st(struct names *pt);

int main(void)
{
	int size;
	puts("Please enter the array's size you want:");
	scanf("%d",&size);
	
	struct names *pts;
	pts=malloc(sizeof(struct names)+size*sizeof(char));
	//在这里,由于结构模板中的数组没有大小,所以struct names的大小不包含数组,因此再加上想要的数组大小,就是所需结构变量的大小
	//虽然使用malloc()创建的结构,pts是这个结构变量唯一的访问方式,但是这个结构和普通结构完全一样,访问方式也完全一样。
	input_st(pts,size);
	read_st(pts);
	
	free(pts);//养成好习惯,用完动态内存记得释放掉。 
	
	return 0; 
}

void input_st(struct names *pt,int size)
{
	puts("Enter the value of a int:");
	scanf("%d",&pt->ma);
	puts("Enter the value of a double:");
	scanf("%lf",&pt->games);
	getchar();
	puts("Enter a characters you want:");
	fgets(pt->datas,size,stdin);
	
	int length=strlen(pt->datas);
	if (pt->datas[length-1] == '\n')
	{
		pt->datas[length-1]='\0';
	}
	else
	{
		while (getchar() != '\n')
		{
			continue;
		}
	}
	return;
}

void read_st(struct names *pt)
{
	puts("The contents are:");
	printf("ma=%d\n",pt->ma);
	printf("games=%.2f\n",pt->games);
	puts(pt->datas);
	
	return;	
} 

打印结果:

Please enter the array's size you want:
30
Enter the value of a int:
3
Enter the value of a double:
3.3
Enter a characters you want:
I love Micy very much!
The contents are:
ma=3
games=3.30
I love Micy very much!

4)注意事项

1.使用malloc()函数来创建结构,创建出来的结构和普通结构相同,访问方式和指针访问方式相同。

2.使用完malloc()函数来分配动态内存,因此在最后需要使用free()函数释放掉动态内存。

十一、匿名结构在嵌套结构中的应用

匿名结构用在嵌套结构中比较常见,原因是可以减少结构变量的设置次数。

//匿名结构在嵌套结构中的应用
#include	<stdio.h>

struct names
{
	int ma;
	double games;
};
struct holds
{
	int diy;
	double numbers;
	char datas[30];
	struct//这里不能加names这个结构模板名称 
	{
		int ma;
		double games;
	};//避免创建变量,这样可以直接放进来。现在ma 和games就相当于直接成为holds的成员了。 
	int cch; 
};

int main(void)
{
	struct holds toys=
	{
		2,
		3.4,
		"I love Micy very much!",
		5,
		4.6,//初始化的时候只需要按着顺序初始化就可以了。
		7, 
	};
	printf("diy=%d\n",toys.diy);
	printf("ma=%d\n",toys.ma);//由于直接成为了成员,因此可以直接使用一次点运算符。 
	
	return 0; 
}

打印结果:

diy=2
ma=5

一定要注意,在嵌套的时候使用匿名,这样可以简化操作,但是需要省略嵌套部分的结构模板名称,并且嵌套部分的成员就相当于是整个结构的成员了。

上一篇:CF1110E Magic Stones


下一篇:Codeforces Round #756 (Div. 3) F 类滑动窗口的双指针 模拟 尺取法