七、结构体与C++引用
七、结构体与C++引用
1. 结构体的定义、初始化、结构体数组
有时候需要将不同类型的数据组合为一个整体,以便于引用。例如, 一名学生有学号、姓名、性别、年龄、地址等属性,如果针对学生的学号、姓名、年龄等都单独定义一个变量,那么在有多名学生时,变量就难以分清。为此,C语言提供结构体来管理不同类型的数据组合。
声明一个结构体类型的一般形式为
struct 结构体名
{成员表列};
struct student
{
int num;char name[20];char sex;
int age;float score;char addr[30];
};
先声明结构体类型, 再定义变量名。
struct student student1, student2;
【例】结构体的 scanf读取和输出。
#include <stdio.h>
struct student{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};//结构体类型声明,注意最后一定要加分号
int main() {
struct student s={1001,"lele",'M',20,85.4,"Shenzhen"};
struct student sarr[3];//定义一个结构体数组变量
int i;
//结构体输出必须单独去访问内部的每个成员
s.num=1003;
printf("%d %s %c %d %f %s\n",s.num,s.name,s.sex,s.age,s.score,s.addr);
printf("--------------------------------------\n");
// scanf("%d%s %c%d%f%s",&s.num,s.name,&s.sex,&s.age,&s.score,s.addr);
for(i=0;i<3;i++)
{
scanf("%d%s %c%d%f%s",&sarr[i].num,sarr[i].name,&sarr[i].sex,&sarr[i].age,&sarr[i].score,sarr[i].addr);
}
for(i=0;i<3;i++)//结构体数组的输出
{
printf("%d %s %c %d %f %s\n",sarr[i].num,sarr[i].name,sarr[i].sex,sarr[i].age,sarr[i].score,sarr[i].addr);
}
return 0;
}
结构体类型声明要放在main函数之前,这样main函数中才可以使用这个结构体,工作中往往把结构体声明放在头文件中,注意,结构体类型声明最后一定要加分号, 否则会编译不通。另外, 定义结构体变量时, 使用 struct student来定义, 不能只有struct或student, 否则也会编译不通,sarr是结构体数组变量. 结构体的初始化只能在一开始定义,如果 struct students={1001,”lele”,’M’,20,85.4,”Shenzhen”}已经执行, 即 struct student s已经定义, 就不能再执行s={1001,”lele”,’M’,20,85.4,”Shenzhen”}. 如果结构体变量已经定义, 那么只能对它的每个成员单独赋值, 如 s. num=1003.
采用“结构体变量名.成员名”的形式来访问结构体成员,例如用s.num访问学号。在进行打印输出时,必须访问到成员,而且printf中的%类型要与各成员匹配,使用scanf读取标准输入时,也必须是各成员取地址,然后进行存储,不可以写成&s,即不可以直接对结构体变量取地址.整型数据(%d)、浮点型数据(%f) 、字符串型数据(%s)都会忽略空格,但是字符型数据(%c)不会忽略空格,所以如果要读取字符型数据,那么就要在待读取的字符数据与其他数据之间加入空格.
例中代码的运行结果如下
1003 lele M 20 85.400002 Shenzhen
--------------------------------------
1001 Lele M 20 85.4 Shenzhen
1005 leli M 21 86.4 Shenzhen
1002 lile M 25 81.4 Shenzhen
1001 Lele M 20 85.400002 Shenzhen
1005 leli M 21 86.400002 Shenzhen
1002 lile M 25 81.400002 Shenzhen
2. 结构体对齐
结构体本身的对齐规则有好几条,考研初试只需要记住一条,结构体的大小必须是其最大成员的整数倍!
#include <stdio.h>
struct student_type1{
double score;//double是一种浮点类型,8个字节,浮点分为float和double,记住有这两种即可
short age;//short 是整型,占2个字节
};
struct student_type2{
double score;
int height;//如果两个小存储之和是小于最大长度8,那么它们就结合在一起
short age;
};
struct student_type3{
int height;
char sex;
short age;
};
//结构体对齐
int main() {
struct student_type1 s1;
struct student_type2 s2={1,2,3};
struct student_type3 s3;
printf("s1 size=%d\n",sizeof(s1));
printf("s2 size=%d\n",sizeof(s2));
printf("s3 size=%d\n",sizeof(s3));
return 0;
}
s1 size=16
s2 size=16
s3 size=8
3. 结构体指针
一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设置一个指针变量,用它指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。指针变量也可以用来指向结构体数组中的元素, 从而能够通过结构体指针快速访问结构体内的每个成员。
【例】结构体指针的使用。
#include <stdio.h>
struct student{
int num;
char name[20];
char sex;
};
//结构体指针的练习
int main() {
struct student s={1001,"wangle",'M'};
struct student sarr[3]={1001,"lilei",'M',1005,"zhangsan",'M',1007,"lili",'F'};
struct student *p;//定义了一个结构体指针变量
p=&s;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1访问通过结构体指针去访问成员
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2访问通过结构体指针去访问成员,用这种
p=sarr;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1访问通过结构体指针去访问成员
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2访问通过结构体指针去访问成员,用这种
printf("------------------------------\n");
p=p+1;
printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1访问通过结构体指针去访问成员
printf("%d %s %c\n",p->num,p->name,p->sex);//方式2访问通过结构体指针去访问成员,用这种
return 0;
}
1001 wangle M
1001 wangle M
1001 lilei M
1001 lilei M
------------------------------
1005 zhangsan M
1005 zhangsan M
p就是一个结构体指针, 可以对结构体s取地址并赋给 p,这样借助成员选择操作符,就可以通过 p访问结构体的每个成员,然后进行打印。我们知道数组名中存储的是数据的首地址,所以可以将sarr赋给p,这样就可以通过两种方式访问对应的成员。
使用(*p). num访问成员为什么要加括号呢? 原因是“.”成员选择的优先级高于“*”(即取值) 运算符, 所以必须加括号, 通过*p 得到 sarr[0], 然后获取对应的成员。
4. typedef 的使用
前面定义结构体变量时使用的语句是 struct students,有些麻烦,可以选择使用 typedef 声明新的类型名来代替已有的类型名
例。
#include <stdio.h>
//stu 等价于 struct student,pstu等价于struct student*
typedef struct student{
int num;
char name[20];
char sex;
}stu,*pstu;
typedef int INGETER;//特定地方使用
//typedef的使用,typedef起别名
int main() {
stu s={1001,"wangle",'M'};
stu *p=&s;//定义了一个结构体指针变量
pstu p1=&s;//定义了一个结构体指针变量
INGETER num=10;
printf("num=%d,p->num=%d\n",num,p->num);
return 0;
}
num=10,p->num=1001
使用 stu定义结构体变量和使用 struct student定义结构体变量是等价的; 使用 INTEGER定义变量i和使用 int定义变量i是等价的; pstu等价于 struct student*, 所以p 是结构体指针变量.
5. C++的引用
对于 C++, 首先新建源文件时, 名字需要叫main. cpp,以cpp后缀结尾
使用了引用后,在子函数内的操作和函数外操作手法一致,这样编程效率较高,对于初学者理解也非常方便, 王道数据结构书籍中均采用了这种手法。
那这种C++的写法, 和C语言的代码又是如何对应的呢? 下面我们来看一下代码对应关系。
【例】在子函数内修改主函数的普通变量
#include
//当你在子函数中要修改主函数中变量的值,就用引用,不需要修改,就不用
void modify_num(int &b)//形参中写&,要称为引用
{
b=b+1;
}
//C++的引用的讲解
//在子函数内修改主函数的普通变量的值
int main() {
int a=10;
modify_num(a);
printf("after modify_num a=%d\n",a);
return 0;
}
改为纯c
#include <stdio.h>
void modify_num(int *b)
{
*b=*b+1;
}
int main(){
int a=10;
modify_num(&a);
printf("after modify_num a=%d\n",a);
return 0;
}
【例】子函数内修改主函数的一级指针变量(重要!)
#include
void modify_pointer(int *&p,int *q)//引用必须和变量名紧邻
{
p=q;
}
//子函数内修改主函数的一级指针变量
int main() {
int *p=NULL;
int i=10;
int *q=&i;
modify_pointer(p,q);
printf("after modify_pointer *p=%d\n",*p);
return 0;//进程已结束,退出代码为 -1073741819 ,不为0,那么代表进程异常结束
}
after modify_pointer *p=10
改为纯c,就需要使用到二级指针(可以不理解)
#include <stdio.h>
void modify_pointer(int **p, int *q)//相对于 C++这里是 int **p;
{
*p=q;//这里的写法和例中的是非常类似的
}
int main(){
int*p=NULL;
int i=10;
int*q=&i;
modify_pointer(&p,q);//相对于 C++这里是&p
printf("after modify_pointer *p=%d\n",*p);
return 0;
}
6. C++的布尔类型
布尔类型在C语言没有, 是C++的, 有 true和 false
【例】 布尔类型也是有值的
#include <stdio.h>
int main() {
bool a= true;
bool b= false;
printf("a=%d,b=%d\n",a,b);
return 0;
}
a=1,b=0
7.例题
使用C++的引用,注意提交时把代码选为C++;在主函数定义字符指针 char *p,然后在子函数内malloc申请空间(大小为100个字节),通过fgets读取字符串,然后在主函数中进行输出;要求子函数使用C++的引用,注意在C++中从标准输入读取字符串,需要使用fgets(p,100,stdin)
Sample Input 1 | Sample Output |
---|---|
I love C language | I love C language |
how are you | how are you |
#include <stdio.h>
#include <stdlib.h>
//当子函数要修改主函数中的p,那么就加引用。引用如何实现的,完全不需要去关心
void modify_pointer(char *&p)
{
p=(char*)malloc(100);//申请100个字节大小的空间
fgets(p,100,stdin);//stdin代表标准输入,fgets是安全的
}
//课时8作业2 练习C++的引用的使用
int main() {
char *p=NULL;
modify_pointer(p);
puts(p);
free(p);//申请的空间不使用后,记得free,避免扣分
return 0;
}