LV015-结构体指针
一、结构体指针简介
1. 什么是结构体指针
我们可以设定一个指针变量用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址,该指针称为结构体指针。
结构体指针与前面的各种指针变量在特性和方法上是相同的。与前述相同,在程序中结构体指针也是通过访问目标运算 * 访问它的对象。
2. 怎么定义?
结构体指针的一般声明形式为:
struct 结构体名 *结构体指名;其中的结构体名必须是已经定义过的结构体类型。例如:
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
/* 定义结构体数组 */
struct Student *pStu;pStu 是指向 struct Student 结构体类型的指针。结构体指针的说明规定了它的数据特性,并为结构体指针本身分配了一定的内存空间。但是指针的内容尚未确定,即它指向随机的对象。
3. 怎么访问成员
3.1 *和.
可以使用解引用运算符*和点运算符.:
(*结构体指针名).成员名1
(*结构体指针名).成员名2
...
(*结构体指针名).成员名N例如:
struct student {
int num;
char name[20];
char sex;
} stu1 = {1001, "Tom", 'M'}, *pstu = &stu1;
(*pstu).num;3.2 箭头运算符->
可以使用箭头运算符来访问成员:
结构体指针名->成员名1
结构体指针名->成员名2
...
结构体指针名->成员名N确保是一个结构体指针的情况下才可以使用 -> 来获取成员。下面是一个简单示例,
struct student {
int num;
char name[20];
char sex;
} stu1 = {1001, "Tom", 'M'}, *pstu = &stu1;
pstu->num;4. 赋值与初始化
与之前的指针变量格式一样。,赋值时要给的是一个地址。
4.1 赋值方式一
- 先定义结构体指针,再赋值
#include <stdio.h>
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
struct Student stu_v = {"fanhua", 'g', 18, "0000000003", 97.5};
struct Student *pStu;
pStu = &stu_v;
printf("%s %c %d %s %.2f\n", pStu->name, pStu->gender, pStu->age, pStu->id, pStu->score);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
fanhua g 18 0000000003 97.504.2 赋值方式二
- 在定义结构体指针的时候直接进行赋值(这种赋值方式也被称为初始化)
/* 语法格式 */
struct 结构体名
{
数据类型 成员名1;
数据类型 成员名2;
...
数据类型 成员名N;
};
struct 结构体名 *指针变量名 = address;示例如下:
#include <stdio.h>
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
struct Student stu_v = {"fanhua", 'g', 18, "0000000003", 97.5};
struct Student *pStu = &stu_v;
printf("%s %c %d %s %.2f\n", pStu->name, pStu->gender, pStu->age, pStu->id, pStu->score);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
fanhua g 18 0000000003 97.50二、含有指针?
当结构体成员中含有指针的时候该怎么访问?又怎么获取地址?
1. 访问结构体中的指针
1.1 结构体变量示例
#include <stdio.h>
#include <stdlib.h>
// 定义结构体
struct MyStruct {
int *p; // 结构体成员是一个指针
};
int main(int argc, char *argv[]) {
int target_data = 100;
struct MyStruct obj1;
obj1.p = &target_data; // 让结构体成员 p 指向 target_data 的地址
// 访问 p 指向的数据:
// 1. obj1.p 先取出指针成员 p
// 2. *(obj1.p) 对该指针进行解引用,得到指向的值
// 注意:因为 . 的优先级高于 *,所以括号可以省略,写成 *obj1.p
printf("target_data=%d, &target_data=%p\n", target_data, &target_data);
printf("*obj1.p=%d, &obj1.p=%p, obj1.p=%p\n", *obj1.p, &obj1.p, obj1.p);
return 0;
}将会看到如下打印信息:
sumu@virtual-machine:~/hk/alpha$ gcc main.c -Wall
sumu@virtual-machine:~/hk/alpha$ ./a.out
target_data=100, &target_data=0x7ffc9acc49bc
*obj1.p=100, &obj1.p=0x7ffc9acc49c0, obj1.p=0x7ffc9acc49bc1.2 结构体指针变量示例
#include <stdio.h>
#include <stdlib.h>
// 定义结构体
struct MyStruct {
int *p; // 结构体成员是一个指针
};
int main(int argc, char *argv[]) {
int target_data = 100;
struct MyStruct obj1;
struct MyStruct *p_obj1 = &obj1;
p_obj1->p = &target_data; // 让结构体成员 p 指向 target_data 的地址
// *p_obj1->p 等价于 *(p_obj1->p)
printf("p 指向的值是: %d\n", *p_obj1->p);
printf("target_data=%d, &target_data=%p\n", target_data, &target_data);
printf("*p_obj1->p=%d, &p_obj1->p=%p, p_obj1->p=%p\n", *p_obj1->p, &p_obj1->p, p_obj1->p);
return 0;
}将会看到如下打印信息:
sumu@virtual-machine:~/hk/alpha$ gcc main.c -Wall
sumu@virtual-machine:~/hk/alpha$ ./a.out
p 指向的值是: 100
target_data=100, &target_data=0x7fff3eaa2a24
*p_obj1->p=100, &p_obj1->p=0x7fff3eaa2a28, p_obj1->p=0x7fff3eaa2a242. 嵌套的指针?
像下面的这种结构体,该怎么访问内部的指针?
// 定义结构体
struct MyStruct {
int *p; // 结构体成员是一个指针
};
// 定义结构体
struct MyStructObj {
struct MyStruct a;
};2.1 结构体变量访问嵌套结构体中的指针
当使用结构体变量时,需要通过多个.运算符来访问嵌套的指针成员:
#include <stdio.h>
#include <stdlib.h>
// 定义结构体
struct MyStruct {
int *p; // 结构体成员是一个指针
};
// 定义嵌套结构体
struct MyStructObj {
struct MyStruct a;
};
int main(int argc, char *argv[]) {
int target_data = 200;
struct MyStructObj obj1;
// 访问嵌套结构体中的指针成员
obj1.a.p = &target_data;
// 访问嵌套指针指向的值
// 1. obj1.a.p 先取出嵌套结构体中的指针成员 p
// 2. *(obj1.a.p) 对该指针进行解引用,得到指向的值
printf("target_data=%d, &target_data=%p\n", target_data, &target_data);
printf("*obj1.a.p=%d, &obj1.a.p=%p, obj1.a.p=%p\n", *obj1.a.p, &obj1.a.p, obj1.a.p);
return 0;
}将会看到如下打印信息:
sumu@virtual-machine:~/hk/alpha$ gcc main.c -Wall
sumu@virtual-machine:~/hk/alpha$ ./a.out
target_data=200, &target_data=0x7ffc9acc49bc
*obj1.a.p=200, &obj1.a.p=0x7ffc9acc49c0, obj1.a.p=0x7ffc9acc49bc2.2 结构体指针访问嵌套结构体中的指针
当使用结构体指针时,需要结合->和.运算符来访问嵌套的指针成员:
#include <stdio.h>
#include <stdlib.h>
// 定义结构体
struct MyStruct {
int *p; // 结构体成员是一个指针
};
// 定义嵌套结构体
struct MyStructObj {
struct MyStruct a;
};
int main(int argc, char *argv[]) {
int target_data = 200;
struct MyStructObj obj1;
struct MyStructObj *p_obj1 = &obj1;
// 访问嵌套结构体中的指针成员
p_obj1->a.p = &target_data;
// 访问嵌套指针指向的值
// *p_obj1->a.p 等价于 *(p_obj1->a.p)
printf("p 指向的值是: %d\n", *p_obj1->a.p);
printf("target_data=%d, &target_data=%p\n", target_data, &target_data);
printf("*p_obj1->a.p=%d, &p_obj1->a.p=%p, p_obj1->a.p=%p\n", *p_obj1->a.p, &p_obj1->a.p, p_obj1->a.p);
return 0;
}将会看到如下打印信息:
sumu@virtual-machine:~/hk/alpha$ gcc main.c -Wall
sumu@virtual-machine:~/hk/alpha$ ./a.out
p 指向的值是: 200
target_data=200, &target_data=0x7fff3eaa2a24
*p_obj1->a.p=200, &p_obj1->a.p=0x7fff3eaa2a28, p_obj1->a.p=0x7fff3eaa2a242.3 访问规则总结
访问嵌套结构体中的指针成员时,遵循以下规则:
| 情况 | 访问方式 | 示例 |
|---|---|---|
| 结构体变量 | 使用.运算符 | obj1.a.p |
| 结构体指针 | 使用->运算符 | p_obj1->a.p |
| 解引用指针值 | 在指针前加* | *obj1.a.p 或 *p_obj1->a.p |
| 获取指针地址 | 使用&运算符 | &obj1.a.p 或 &p_obj1->a.p |
重要提示:
->运算符只能用于指针类型,不能用于结构体变量.运算符只能用于结构体变量,不能用于指针(除非先解引用)- 访问嵌套成员时,从外到内逐层访问
三、结构体指针与数组
结构体指针变量自然也可以指向数组,帮助我们访问数组中的元素。
1. 基本用法
#include <stdio.h>
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
struct Student stu[3] = {{"fanhua", 'g', 18, "0000000003", 97.5},
{"qidaink", 'm', 19, "0000000001", 95.67},
{"a", 'm', 16, "0000000002", 87.5}};
struct Student *pStu = stu;
int len = sizeof(stu) / sizeof(struct Student);
printf("Name\tGender\tAge\t ID\t\tScore\t\n");
for(pStu = stu; pStu < stu + len; pStu++)
{
printf("%s\t %c\t%d\t%s\t%.2f\n", pStu->name, pStu->gender, pStu->age, pStu->id, pStu->score);
}
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
Name Gender Age ID Score
fanhua g 18 0000000003 97.50
qidaink m 19 0000000001 95.67
a m 16 0000000002 87.502. 作为数组名使用
当一个结构体指针指向数组后,它可以作为数组名使用,但是无法再像结构体指针那样可以使用 -> 来取相应元素的成员,但是可以使用 . 来获取相应元素的成员。
#include <stdio.h>
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
struct Student stu[3] = {{"fanhua", 'g', 18, "0000000003", 97.5},
{"qidaink", 'm', 19, "0000000001", 95.67},
{"a", 'm', 16, "0000000002", 87.5}};
struct Student *pStu = stu;
printf("%s\t %c\t%d\t%s\t%.2f\n", pStu[0]->name, pStu[0]->gender, pStu[0]->age, pStu[0]->id, pStu[0]->score);
return 0;
}一般来说应该会报这样的一个错误,而且是有一个报一个😂,能给报一堆:
test.c: In function ‘main’:
test.c:20:47: error: invalid type argument of ‘->’ (have ‘struct Student’)
printf("%s\t %c\t%d\t%s\t%.2f\n", pStu[0]->name, pStu[0]->gender, pStu[0]->age, pStu[0]->id, pStu[0]->score);四、结构体指针与函数
结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针。
如果结构体成员较多,尤其是成员为数组时,传送的时间和空间开销会很大,影响程序的运行效率。所以最好的办法就是使用结构体指针,这时由实参传向形参的只是一个地址,速度将会有所提升。
#include <stdio.h>
/* 定义结构体数据类型 */
struct Student
{
char *name; /* 姓名 */
char gender; /* 性别 */
int age; /* 年龄 */
char id[11]; /* 学号 */
float score; /* 成绩 */
}; /* ; 不可缺少 */
void average(struct Student *ps, int len);
int main(int argc, char *argv[])
{
struct Student stu[3] = {{"fanhua", 'g', 18, "0000000003", 97.5},
{"qidaink", 'm', 19, "0000000001", 95.67},
{"a", 'm', 16, "0000000002", 87.5}};
struct Student *pStu = stu;
int len = sizeof(stu) / sizeof(struct Student);
printf("Name\tGender\tAge\t ID\t\tScore\t\n");
for(pStu = stu; pStu < stu + len; pStu++)
{
printf("%s\t %c\t%d\t%s\t%.2f\n", pStu->name, pStu->gender, pStu->age, pStu->id, pStu->score);
}
average(stu, len);
return 0;
}
void average(struct Student *ps, int len)
{
int i = 0;
float average, sum = 0;
for(i = 0; i < len; i++)
{
sum += (ps + i) -> score;
}
average = sum / len;
printf("average = %.2f\n", average);
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
Name Gender Age ID Score
fanhua g 18 0000000003 97.50
qidaink m 19 0000000001 95.67
a m 16 0000000002 87.50
average = 93.56