一、结构的定义
结构是有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 :
tttThe 1 group's value:
1
1.10
qqqThe 2 group's value:
2
2.20
wwwThe 3 group's value:
3
3.30
eeeThe 4 group's value:
4
4.40
rrrThe 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 :
tttThe 1 group's value:
1
1.10
qqqThe 2 group's value:
2
2.20
wwwThe 3 group's value:
3
3.30
eeeThe 4 group's value:
4
4.40
rrrThe 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
一定要注意,在嵌套的时候使用匿名,这样可以简化操作,但是需要省略嵌套部分的结构模板名称,并且嵌套部分的成员就相当于是整个结构的成员了。