前言:
结构体是很重要的C语言内容,打好结构体的基础是很有必要的!另外,再把程序员必备的硬技能---调试技巧掌握好,可以让我们高效的找出bug的地方。调试就是在寻找使程序出现不符合预期的错误逻辑代码。
一.结构体
结构体是用来描述有多种参数特征的复杂对象,比如描述一本书,有书名、有价格、作者、书号等。但是如果只说书名,是描述不清楚的具体是哪一本书的---(也就是用一个字符数组)。用一个书的价格更不可能描述清楚~。
结构体使用的关键字是struct,和枚举类型(enum)一样,都是使用关键字创建一个新的数据类型;结构体成员可以是不同的数据类型。
#include <stdio.h> struct Book { char author[7]; char BookName[20]; float Price; int ed; }b2;//参数列表 在结构体后面可以创建该结构体类型的全局变量 int main() { struct Book b1 = {"张三", "我的牢底生活", "55.5", 2}; }
使用struct创建一个名为Book的结构体类型,在C++中,创建b1结构体变量的时候,不需要加struct,在C语言中struct不能省略。在C语言想要这么做的话,可以typedef重命名一下,一般都会这么做,因为每次都要多写一个struct,不仅显的长,还麻烦。
#include <stdio.h> typedef struct Book { //结构体成员 --- 成员列表 char author[7]; char BookName[20]; float Price; int ed; }Book;//Book和struct Book等效 //重命名和参数列表是不能一起表示的 int main() { struct Book b1 = {"张三", "我的牢底生活", "55.5", 2}; }
1.1结构体的初始化和访问
结构体的初始化和数组一样使用列表{}初始化,注意不能在创建结构体类型的时候给结构体成员初始化,因为它只是一个类型,是一张蓝图,并没有实际的空间。只有在实例化(使用结构体类型创建结构体变量)才有分配空间。
结构体初始化有两种方式:第一种就是按照成员列表依次初始化;第二种就是不根据列表的顺序初始化,但第二种需要加上一些指定性的东西才能实现,请看代码:
在打印的时候,点操作符(.)是访问结构体成员的一种方式,使用结构体变量名.成员变量的方式访问。所以在使用第二种方式初始化可以这样理解,.height其实就是在访问s2里的height成员,因为这是在初始化s2。
补充:不要第一种方式和第二种方式混合使用。
结构体访问的两种方法:
第一种是前面提到的结构体变量.成员
第二种是结构体指针->成员
第一种方法和第二种方法都行,第三种其实就是指针解引用拿到结构体变量,再使用第一种方法,duck不必~
二.结构体传参
传值方式和传址方式:
#include <stdio.h> struct Stu { char number[12]; char name[10]; int age; char address[50]; }; void Print1(struct Stu s1) { printf("%s %s %d %s\n", s1.number, s1.name, s1.age, s1.address); s1.age = 70; } void Print2(struct Stu* ps) { printf("%s %s %d %s\n", ps->number, ps->name, ps->age, ps->address); (*ps).age = 80; } int main() { struct Stu s1 = {"1111", "张三", 23, "阿拉斯加"}; Print1(s1); printf("%d\n", s1.age); Print2(&s1); printf("%d\n", s1.age); return 0; }
Print1是把s1这一整个变量传给形参,相比与传址而言,传值浪费空间严重,因为结构体本身是很大的,形参是实参的一份临时拷贝,传值会在栈区上开辟一模一样结构体s1。而地址的大小无非就是4个或者8个字节。
第二就是传地址可以在函数内改变函数外部的变量,因为使用的是s1本身。传值改变不了的原因是,形参是一块新的空间,改变不了原来的。
总结:对于结构体,传地址可以节省空间,可以改变外部变量,一般情况下传的是结构体的地址。