指针究竟指向何方?

99ANYc3cd6
预计阅读时长 14 分钟
位置: 首页 C语言 正文

这两个运算符都与结构体联合体有关,它们的作用是访问其成员,它们的主要区别在于操作的对象不同。

c语言指针-
(图片来源网络,侵删)

结构体 和 联合体 回顾

我们需要知道 -> 和 是用来做什么的,它们是用来访问结构体联合体内部变量的。

// 定义一个结构体类型 Person
struct Person {
    char name[50];
    int age;
    float height;
};
int main() {
    // 声明一个结构体变量
    struct Person p1;
    // 使用点运算符 . 来访问和赋值成员
    strcpy(p1.name, "Alice"); // 注意:不能直接用 p1.name = "Alice";
    p1.age = 30;
    p1.height = 1.68;
    printf("Name: %s, Age: %d, Height: %.2f\n", p1.name, p1.age, p1.height);
    return 0;
}

在上面的例子中,p1 是一个结构体变量,我们使用 运算符来直接访问它的成员 name, age, height


点运算符

用途:通过结构体变量本身来访问其成员。

语法结构体变量名.成员名

c语言指针-
(图片来源网络,侵删)

核心: 运算符作用于一个实实在在的变量上。

示例

struct Point {
    int x;
    int y;
};
int main() {
    struct Point p1; // p1 是一个 Point 类型的变量
    p1.x = 10;      // 通过变量 p1 访问成员 x
    p1.y = 20;      // 通过变量 p1 访问成员 y
    printf("Point is at (%d, %d)\n", p1.x, p1.y);
    return 0;
}

p1.x 的意思是:“取 p1 这个变量的 x 成员”。


-> 箭头运算符

用途:通过指向结构体的指针来访问其成员。

语法结构体指针名->成员名

核心-> 运算符作用于一个指向结构体的指针上,它是 (*指针名).成员名 的简写形式。

为什么需要 ->

假设我们有一个结构体指针,如果用 运算符会发生什么?

struct Point {
    int x;
    int y;
};
int main() {
    struct Point p1 = {10, 20};
    struct Point *ptr = &p1; // ptr 指向 p1
    // 错误用法!
    // ptr.x;  // 编译错误!ptr 是一个指针,它没有 x 成员。
              // *ptr 才是那个结构体变量。
}

指针 ptr 本身存储的是内存地址,它不包含 xy 成员,它所指向的 *ptr 才包含。

正确的访问方式有两种:

解引用 + 点运算符

// (*ptr).x
// 1. *ptr: 解引用 ptr,得到它指向的结构体变量(也就是 p1)
// 2. .x:   然后对这个结构体变量使用点运算符访问 x 成员
(*ptr).x = 30;
(*ptr).y = 40;

这种方式语法上有点别扭,因为括号和星号混在一起,可读性不强。

箭头运算符(推荐)

C 语言为了解决上述问题,提供了 -> 运算符,它是 (*ptr). 的完美简写。

// ptr->x
// 1. ptr->: 等价于 (*ptr).
// 2. x:     然后访问 x 成员
ptr->x = 30;
ptr->y = 40;

这种方式更简洁、更直观,清晰地表达了“通过这个指针去访问它的成员”的意图。


核心区别与总结

特性 (点运算符) -> (箭头运算符)
操作对象 结构体变量 指向结构体的指针
语法 变量名.成员名 指针名->成员名
含义 访问变量本身的成员 通过指针访问其指向的变量的成员
本质 直接访问 间接访问(先解引用,再访问)
等价形式 - (*指针名).成员名

一个完整的对比示例

这个例子将清晰地展示 和 -> 在不同场景下的用法。

#include <stdio.h>
#include <string.h>
// 定义一个学生结构体
struct Student {
    int id;
    char name[50];
    float score;
};
int main() {
    // 场景一:使用结构体变量
    struct Student student1;
    student1.id = 101;
    strcpy(student1.name, "Bob");
    student1.score = 95.5;
    printf("--- Using Dot Operator (.) ---\n");
    printf("ID: %d, Name: %s, Score: %.1f\n", student1.id, student1.name, student1.score);
    // 场景二:使用指向结构体的指针
    struct Student *ptr_student = &student1; // ptr_student 指向 student1
    // 修改 student1 的数据,通过指针
    ptr_student->id = 102; // 使用 -> 访问
    strcpy(ptr_student->name, "Charlie");
    ptr_student->score = 88.0;
    printf("\n--- Using Arrow Operator (->) ---\n");
    printf("ID: %d, Name: %s, Score: %.1f\n", ptr_student->id, ptr_student->name, ptr_student->score);
    // 场景三:验证 -> 和 (*ptr). 的等价性
    printf("\n--- Verifying Equivalence: ptr->id vs (*ptr).id ---\n");
    printf("ptr->id is: %d\n", ptr_student->id);
    printf("(*ptr_student).id is: %d\n", (*ptr_student).id);
    // 这两个输出结果完全相同
    return 0;
}

程序输出:

--- Using Dot Operator (.) ---
ID: 101, Name: Bob, Score: 95.5
--- Using Arrow Operator (->) ---
ID: 102, Name: Charlie, Score: 88.0
--- Verifying Equivalence: ptr->id vs (*ptr).id ---
ptr->id is: 102
(*ptr_student).id is: 102

记忆技巧

一个非常简单粗暴的记忆方法:

  • 有名字(变量名)用 student1.id (student1 是个名字)
  • 有箭头(->)用 ->ptr->id (ptr 看起来像一支箭)

希望这个详细的解释能帮助你彻底理解 C 语言中这两个至关重要的运算符!

-- 展开阅读全文 --
头像
C语言如何实现pingpong游戏?
« 上一篇 04-13
同一空间,如何织就两个不同的梦?
下一篇 » 04-13

相关文章

取消
微信二维码
支付宝二维码

目录[+]