目录
1.结构体声明
1.1结构体基础知识
我们先了解一些概念:
数组:数据的集合,但是每个数据的类型都相同
结构(结构体):也是一些数据的集合,但每个成员可以是不同类型
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1.2结构体的声明
struct是声明这圣一个结构体
tag是这个结构体的名字
member-list是结构体的成员列表
variable-list是结构体的变量列表
struct tag
{
member - list;
}variable-list;
为什么要出现结构体?
我们当前学习的类型都是内置类型
int ,char,short,int,double这些都可以用来描述一个简单的对象
但是我们如果要描述一个复杂对象,比如人,我们就需要更多的类型
人:名字(name)+性别(gender)+年龄(age)+地址(adress)+电话(phone)……
我们假如存入学生的信息,我们就可以创建一个结构体
struct stu
{
//学生的相关属性
char name[20];//名字
int age;//年龄
char gender[20];//性别
};
1.3 结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
struct B
{
char c;
int i;
};
struct stu
{
char name[20];
int age;
int arr[10];
double* pd;
struct B a;
};
1.4 结构体变量的定义和初始化
1.41结构体变量的定义
我们之前学习存入一个数据,例如:
int a = 10;
我们是先有类型,然后有名称(这时计算机会在内存中开辟这个类型大小的空间,这个空间会被命名成我们给定的名字),最后才能往空间内存入我们的数据;
int 就是类型,a就是名称,10是存入的数据。
那么结构体也是这样
我们需要先声明一个结构体类型,再创建变量最后才能存入数据
struct stu
{
char name[20];
int age;
int arr[10];
double* pd;
struct B a;
};
int main()
{//struct stu是类型
struct stu s1;//s1是变量名称
struct stu s2;//s2
return 0;
}
学生类型—>学生变量—>学生的属性信息—>存入信息
(有类型) (变量名)(结构体内的信息)(存入)
这里我们是在主函数中创建的2个变量,我们也可以在结构体外直接声明变量名称
struct stu
{
char name[20];
int age;
int arr[10];
double* pd;
struct B a;
}s3,s4;//全局变量
struct stu s5;//全局变量
//s3 s4和s5是在函数外创建的变量所以他们是全局变量
//s1和s2是函数内的变量所以说局部变量
int main()
{
struct stu s1;//局部变量
struct stu s2;//局部变量
return 0;
}
我们在函数内声明是结构体类型时,在c语言中必须加上,结构体类型和名称
int main()
{
stu s1;//错误,编译器会报错
struct stu s2;//正确写法
return 0;
}
当然我们也可以使用typedef 重新定义这个结构体类型的名字
typedef struct stu
{
char name[20];
int age;
int arr[10];
double* pd;
struct B a;
}stu;
这样这个结构体变量的类型就被称为 stu 了
我们就可以直接,类型 + 类型变量;来创建这么一个变量了
int main()
{
stu s1;
struct stu s2;
return 0;
}
当然我们还声明struct stu 也还是正确的。
1.42结构体变量的初始化
初始化和数组一样,我们都要用到大括号{ }
struct stu
{
char name[20];
int age;
char gender[23];
int* a;
};
int main()
{ // 按顺序来初始化
struct stu s1 = { "zhangsan", 17, "nan" ,NULL};
//按照指定成员来初始化
struct stu s2 = {.gender = "nv",.age = 13, .name[20] = "erya",.a = NULL};
return 0;
}
使用(.)操作符来进行指定成员的访问
struct stu
{
char name[20];
int age;
char gender[23];
int* a;
int arr[3];
};
int main()
{ // 按顺序来初始化
struct stu s1 = { "zhangsan", 17, "nan", NULL, {1,2,3} };
//按照指定成员来初始化
struct stu s2 = { .gender = "nv",.age = 13, .name[10] = "erya", .a = NULL, .arr = {1,2,3} };
return 0;
}
下面是一些初始化的例子
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
struct Point p3 = { 3, 4 };//初始化:定义变量的同时赋初值。
struct Stu
{
char name[20];
int age;
};
struct Stu s1 = { "zhangsan", 20 };//初始化
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
2.结构体访问
结构体访问用到2个操作符
(.)点操作符(->)箭头操作符
2.1(.)点操作符
(.)点操作符接收2个操作数:左边是结构体变量名字,右边是结构体内的参数名字
struct . 成员名
struct stu
{
char name[20];
int age;
}s1;
int main()
{
s1.age = 13;
return 0;
}
当我们输入结构体类型名字的时候,加上(.)自动就知道我们要访问内容了。
2.2(->)箭头操作符
struct* -> 成员名
struct stu
{
char name[20];
int age;
}s1;
int main()
{
struct stu* p = &s1;
p->name;
s1.age = 13;
return 0;
}
当我们有结构体的地址时,输入->就可以直接访问里面的成员了。
3.结构体传参
结构体传参时我们要牢记一点:我们结构体传参传过去地址。
下面我们来用一些例子解释一下。
struct stu
{
int age;
char name[20];
};
p(struct stu s)
{
printf("%d %s", s.age, s.name);
}
void set_(struct stu s)
{
s.age = 18;
//当我们想要修改一个字符串的内容时我们使用strcpy,因为字符串在内存中是常量
strcpy(s.name,"lisi" );
}
int main()
{ //初始化结构体
struct stu s = { 0, "n"};
//我们写一个函数给s存入数据
set_(s);//给age存入18,给name存入“lisi”
//我们写一个函数打印这个结构体
p(s);
return 0;
}
我们运行结果发现是
发现并未修改,这是因为:
实参传给形参,形参是实参的一份临时拷贝,对与形参的修改,不影响实参
所以我们应当传给函数相应的地址,通过地址直接修改。
struct stu
{
int age;
char name[20];
};
p(struct stu* s)//(因为我们要传的是地址,我们接收应该用指针类型来接受)
{
printf("%d %s", s->age, s->name);
}
void set_(struct stu* s)
{
s->age = 18;
//当我们想要修改一个字符串的内容时我们使用strcpy,因为字符串在内存中是常量
strcpy(s->name, "lisi");
}
int main()
{ //初始化结构体
struct stu s = { 0, "n" };
//我们写一个函数给s存入数据
set_(&s);//给age存入18,给name存入“lisi”
//这时我们传过去的是指针了
//我们写一个函数打印这个结构体
p(&s);
return 0;
}
我们来看打印结果
这样我们发现他的值确实被修改了。
此外还有要注意的点就是:
函数传参的时候,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
所以今后我们结构体传参时统一传其地址就可以了。