结构体结构体,到底什么是结构体?顾名思义,它是一个结构,换句话说,是一个框架,里面存放着各种各样不同类型的变量,而这些变量又存在着某种内在联系,合在一起反映了一个结构的某种性质。
例如:
现在有三个橱柜:(把我自己说饿了)
第一个是装水果的橱柜,里面放着一个苹果,两个橘子,三个香蕉。
第二个是装蔬菜的橱柜,里面放着一个白菜,两根葱,三根茄子,四个番茄。
第三个是装肉的橱柜,里面放着一块猪肉,两块牛肉,三块鸡肉
其中,橱柜——结构体,存的东西——变量。而每一个橱柜里的东西都有着各自的内在联系,合在一起就反应了一个橱柜的特性。
说的官方一点,创建一个结构体,就是把多个数据按照需求进行封装,其中类型和大小都可以由用户指定。
我们现在知道了结构体到底是什么,接下来就需要来使用它。很多教材都会用 “包含一个学生的信息的结构体” 举例子,为了普适性,我们也创建一个包含了学生信息的结构体。
1. 声明一个结构体类型一般来说,会在所有函数前声明结构体的类型。
struct STUDENT//声明了一个STUDENT的结构体类型,注意:为了规范,要用大写字母。
{char name[20];
int age;
char num[10];//学生的学号
} ; //分号一定不能省略
到此为止,我们只是声明了一个结构体类型,并没有声明结构体类型变量!此时系统还没有为他分配内存空间!!即,此时还不能进行初始化(落脚的地方都还没有),类似于int ,而不是int a.
2. 声明结构体变量第一步我们声明了一个STUDENT类型的结构体,但是只是创建了一个类型,没有为这个类型开辟一个内存空间。
前面提到过,一般会在所有函数前声明结构体的类型,再在某个函数中声明他的结构体变量。
#includestruct STUDENT
{char name[20];
int age;
char num[10];
};
int main()
{struct STUDENT stu1;//定义了一个STUDENT类型的结构体变量stu1,stu1具有STUDENT的结构
}
#includestruct STUDENT
{char name[20];
int age;
char num[10];
} stu1;//声明一个STUDENT类型的结构体变量stu1,注意!!!分号不要忘了!
3. 结构体类型的初始化注意:
初始化的方式:
scanf(“%s,%d,%s”,stu1.name,&stu1.age,stu1.num);
int main()
{struct STUDENT stu1 = {"小明",12,k12345};
}
int main()
{struct STUDENT stu1;
strcpy(stu1.name,"小明");//如果是中文字符就必须要用strcpy!
stu1.age = 12;
stu1.num = "k12345";
}
4. 结构体的调用首先引进一个概念,“成员运算符”,即目的是调用某结构体中的成员。一般用到的是 " . “和” ->",他们的优先级非常高,高于其他所有的运算符。
当我们要调用一个结构体里的某一个成员,可以这样做
第一种方式:
stu1.name;
第二种方式
struct STUDENT *p;
p = &stu1;
(*p).name;
第三种方式
struct STUDENT *p;
p = &stu1;
p->name;
后两种方式需要用到指向结构体的指针的知识,后面会有详细的介绍,这里了解即可。然而!只有当使用指向结构体的指针p时,才可以使用->运算符!!
下面是一个完整的例子,包含两个结构体的使用
#includestruct AGE
{int year;
int month;
int day;
};
struct STUDENT
{char name[20];
int age;
char num[10];
struct AGE birthday;//结构体嵌套结构体
//birthday是STUDENT类型的一个成员,同时是AGE类型的结构体变量,因此,STUDENT类型包含了AGE类型的所有成员。
};
int main()
{struct STUDENT stu1={"小明",12,"k12345",{2010,6,25}};
printf("%s今年%d岁,学号是%s,生日是%d.%d.%d\n",stu1.name,stu1.age,stu1.num,stu1.birthday.year,stu1.birthday.month,stu1.birthday.day);//结构体的调用,需要用成员运算符“.”(他的优先级最高)调用某一结构体的内部成员。
return 0;
}
(二)结构体字节对齐”结构体字节对齐“也可以称为“内存对齐”,它不仅仅存在于结构体当中,更是计算机系统本身所具备的一种能力,存在于方方面面。
在这里,可以用来解释不同的结构体类型所占的字节数。由于一个结构体里面有不同类型的成员,那么一个结构体类型究竟占多少字节呢?
根据字节对齐理论,我们以占字节最长(n)的类型为准分配一个长为n的内存空间。
例如
struct this
{//先观察全局,发现int占4个字节,char占1个字节,int所占字节数最多,于是系统便分配了一个长度为4个字节的内存空间。
char a;
char b;
char c;
char d;
char e;//填充后如果后面还有空位就空着,下一个int另起一行
int f;//分配一个四个字节的长方形
}
为了清楚地说明,建立了以下图表表示内存空间——
a | b | c | d |
---|---|---|---|
e | |||
f | f | f | f |
abcd一个一个往后填充,等 e 填充完后,由于 f 是int类型,需要占据4个字节,因此需要另起一行,那么e后面没有能够填充的元素,便需要空出来,为 f 另起一行。
通过数格子数目,不难看出,以上定义的this类型的结构体所占字节为12。
那么,内存对齐的目的是什么?
既然要节约内存,为什么不依次按顺序地把变量放进内存空间,而要在遇到占更多字节数的变量时,要另起一行呢?是为了便于计算机寻址,提高运行速度。
注意:
基本结构如下:
struct STUDENT stu[10];
表示:定义了一个STUDENT类型的结构体数组stu,stu中有10 个元素,每一个元素都是STUDENT类型的结构体。
结构体数组和普通数组其实差不多,只不过结构体数组里面的元素是结构体,普通数组里面的元素是数。
(四)结构体指针之前提到过,定义一个指针p,他指向某个结构体变量,这个指针p就叫做结构体指针。
基本结构如下:
struct STUDENT *p;
表示:定义了一个指向STUDENT结构体类型的指针。注意!这里只声明了p指向STUDENT类型,但它并没有明确指向哪一个变量!
于是需要对其进行初始化
struct STUDENT *p = NULL;
调用(三个等价)
(*p).name;
p->name;//非常重要!!!
stu1.name;
如果要调用结构体内部成员,以下三种方式等价
注意!第三种方式只能有指针变量能用!!!!
(->是成员运算符,优先级和.相同)
前面对结构体数组、结构体指针有所了解之后,现引出一个概念—— “指向结构体数组的指针”
现有一结构体数组
struct STUDENT stu[10];(有10个元素,每一个元素都是一个STUDENT类型的结构体)
创建一个指向该STUDENT类型结构体的指针
struct STUDENT *p=NULL;
p = stu;//(实际上指向的是该结构体数组的第一个元素,指向数组中的第一个结构体)
调用该指针
P是数组中第一个结构体的首地址。
现在调用第一个结构体中的第一个成员。
方式一:(*p).name;
方式二:p->name;
现在依次调用该数组中的每一个元素的第一个成员,直到stu+n截止(*p++).name;
(可以写作:p++
指针的加减,即地址位置的前后变化
答:
对于输入函数:
如果要对传入函数的值进行修改,就一定要传入它的地址,从它的根本上去修改它。
如果只是要使用它的值,就只需要传入它的值就行,相当于复制了一个值,对原来的值没有任何影响。
对于输出函数
只需要对传入的值进行输出,那么直接调用它的值就行。
然而,从节约内存的角度分析,如果传入的是一个指针变量,形参就只占据了4个字节,如果传入的是一个结构体变量的值,那么该结构体占多少字节,形参就占多少字节,这可能会使用更多的内存空间。
然而
当你只是想调用变量的值,却传入的是变量的地址,你所定义的函数就能对该变量进行修改了!!这无疑是非常危险的,也会成为你的程序当中的一个漏洞。
因此
从综合角度来分析,当要对一个值进行修改,传入变量的地址。当只是对一个变量的值进行调用,或者单纯地想要输出它,也传入变量的地址,但是,在前面加上一个const,这样一来,就无法修改它的值了。
举一个小栗子来结束这篇文章
#include#includestruct STUDENT
{char name[20];
int age;
char num[10];
};
void inputstudent ( struct STUDENT *p)//定义了一个需要你输入一个指向结构体的指针的函数,即,输入一个地址,该函数对输入的地址上的数据进行操作。
{(*p).num=k123456;
strcpy((*p).name,"小明");
(*p).age=12;
return;//如果没有返回值可以就写作return;
}
void outputstudent(struct STUDENT *p)//作为输出,其实传递值也是可以的,但是为了节省内存空间,常常写作只有4个字节的指针
{printf("名字是%s,年龄是%d,学号是%s",(*p).name,(*p).age,(*p).num);
return;
}
int main()
{struct STUDENT stu1;
inputstudent(&stu1);
outputstudent(&stu1);
return 0;
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧