C/C++结构体是程序开发中非常常见的数据组织方式,它可以将多个不同类型的数据组合成一个完整的数据对象。在实际编码过程中,开发者经常需要访问结构体中的成员变量,而根据结构体对象的类型不同,成员访问方式也有所区别。
C/C++中结构体成员访问主要分为两种情况:通过结构体变量访问成员,以及通过结构体指针访问成员。理解这两种访问方式的区别,是掌握结构体操作、指针使用以及内存管理的重要基础。
结构体变量访问成员:使用点运算符(.)
当定义一个结构体变量后,可以直接通过点运算符(.)访问其中的成员。
例如:
#include
struct Student {
char name[20];
int age;
float score;
};
int main() {
struct Student stu;
stu.age = 18;
stu.score = 95.5;
printf("年龄:%d
", stu.age);
printf("成绩:%.1f
", stu.score);
return 0;
} 代码中定义了一个 Student 结构体,包含姓名、年龄和成绩三个成员。创建结构体变量 stu 后,可以直接通过:
stu.age
stu.score的形式访问对应成员。
这种访问方式适用于结构体对象本身存在于当前作用域中的情况,例如:
局部结构体变量;
全局结构体变量;
作为函数参数传递的结构体对象。
点运算符的本质是根据结构体变量的地址定位成员所在的内存区域,然后进行数据读取或修改。
结构体指针访问成员:使用箭头运算符(->)
当使用指针保存结构体地址时,不能直接通过点运算符访问成员,而需要使用箭头运算符(->)。
示例:
#include
struct Student {
char name[20];
int age;
float score;
};
int main() {
struct Student stu;
struct Student *p;
p = &stu;
p->age = 20;
p->score = 88.5;
printf("年龄:%d
", p->age);
printf("成绩:%.1f
", p->score);
return 0;
} 这里定义了结构体指针:
struct Student *p;然后通过取地址操作符:
p = &stu;让指针指向结构体变量 stu。
之后可以通过:
p->age
p->score访问结构体成员。
箭头运算符实际上是点运算符和指针解引用操作的组合:
p->age等价于:
(*p).age也就是说,程序会先通过 *p 获取指针指向的结构体对象,然后使用点运算符访问成员。
点运算符与箭头运算符的区别
虽然两种方式最终都是访问结构体成员,但使用场景存在明显区别。
| 访问方式 | 操作符 | 操作对象 | 示例 |
|---|---|---|---|
| 结构体变量访问 | . | 结构体对象 | stu.age |
| 结构体指针访问 | -> | 结构体地址 | p->age |
简单来说:
有结构体变量,用
.有结构体指针,用
->
这是C/C++结构体成员访问中最基本的规则。
为什么结构体指针更常用于函数传递
在实际项目开发中,经常需要将结构体传递给函数。如果直接传递结构体变量,会复制整个结构体数据。
例如:
void printStudent(struct Student stu)
{
printf("%d", stu.age);
}调用函数时:
printStudent(stu);会产生一次结构体复制。
如果结构体包含大量成员,这种复制会增加内存开销。
更常见的方式是传递结构体指针:
void printStudent(struct Student *p)
{
printf("%d", p->age);
}调用:
printStudent(&stu);这种方式只传递结构体地址,不需要复制完整数据,因此效率更高。
在大型项目、操作系统开发、嵌入式程序以及C++底层开发中,结构体指针的使用非常普遍。
动态创建结构体与成员访问
C语言中可以通过动态内存分配创建结构体对象,此时通常需要使用结构体指针访问成员。
示例:
#include
struct Student {
int age;
};
int main()
{
struct Student *p;
p = (struct Student *)malloc(sizeof(struct Student));
if (p != NULL) {
p->age = 22;
}
free(p);
return 0;
} 通过 malloc 分配内存后,返回的是一块内存地址,因此必须使用指针保存。
此时:
p->age是访问动态结构体成员的标准方式。
使用动态结构体时需要注意:
检查
malloc是否成功;使用完成后及时调用
free释放内存;避免访问已经释放的结构体指针。
C++中的结构体成员访问
C++中的 struct 与 C语言类似,也支持两种成员访问方式。
例如:
#include
using namespace std;
struct Person {
string name;
int age;
};
int main()
{
Person person;
person.name = "Tom";
person.age = 25;
Person *ptr = &person;
cout << ptr->name << endl;
cout << ptr->age << endl;
return 0;
} C++中的结构体不仅可以包含数据成员,还可以包含成员函数,因此访问方式也更加灵活。
例如:
struct Person {
int age;
void show()
{
cout << age;
}
};对象调用成员函数:
person.show();指针调用成员函数:
ptr->show();两者遵循相同的成员访问规则。
常见错误分析
错误一:使用点运算符访问结构体指针
错误代码:
struct Student *p;
p.age = 18;编译器会报错,因为 p 是地址,不是结构体对象。
正确写法:
p->age = 18;或者:
(*p).age = 18;错误二:结构体指针未初始化
错误代码:
struct Student *p;
p->age = 20;此时 p 没有指向有效内存,访问成员可能导致程序崩溃。
正确方式:
struct Student stu;
struct Student *p = &stu;
p->age = 20;或者:
struct Student *p = malloc(sizeof(struct Student));错误三:忘记释放动态内存
动态创建结构体后:
struct Student *p = malloc(sizeof(struct Student));使用结束后应该:
free(p);否则可能造成内存泄漏。
结构体成员访问的实际应用场景
结构体变量和结构体指针在不同领域都有广泛应用。
数据封装
结构体可以将多个相关数据组合,例如:
学生信息;
用户数据;
网络数据包;
配置参数。
链表实现
C语言链表节点通常通过结构体指针连接:
struct Node {
int data;
struct Node *next;