指向结构体的指针【结合例子】

1代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 用于strcpy函数

// 定义Person结构体
struct Person {
    char name[50]; // 姓名
    int age;       // 年龄
    float height;  // 身高(以米为单位)
};

int main() {
    // 声明两个Person指针
    struct Person *p1;
    struct Person *p2;
    struct Person *p3;
    
    
    //此时指向结构体的指针p1 p2 p3还未初始化,是野指针,它可能指向任何地址 
    printf("Name1: %p\n", p1);
    printf("Name2: %p\n", p2);
    printf("Name3: %p\n", p3);
    //输出指针变量本身的存储地址 
    printf("Name1: %p\n", &p1);
    printf("Name2: %p\n", &p2);
    printf("Name3: %p\n", &p3);
    
    printf("\n");
    // 分配内存给两个Person结构体,此时结构体指针就指向结构体变量实体了,就不是野指针了! 
    p1 = (struct Person *)malloc(sizeof(struct Person));
    p2 = (struct Person *)malloc(sizeof(struct Person));

    // 检查内存分配是否成功
    if (p1 == NULL || p2 == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    // 给p1指向的结构体赋值
    strcpy(p1->name, "Alice");
    p1->age = 30;
    p1->height = 1.75;

    // 给p2指向的结构体赋值
    strcpy(p2->name, "Bob");
    p2->age = 25;
    p2->height = 1.80;

    // 使用指针打印信息
    printf("Name: %s, Age: %d, Height: %.2f meters\n", p1->name, p1->age, p1->height);
    printf("Name: %s, Age: %d, Height: %.2f meters\n", p2->name, p2->age, p2->height);
    printf("\n");
    
    //检查分配内存后,结构体指针指向的地址 
    printf("Name1: %p\n", p1);
    printf("Name2: %p\n", p2);
    printf("Name3: %p\n", p3);
    
    printf("Name1: %p\n", &p1);
    printf("Name2: %p\n", &p2);
    printf("Name3: %p\n", &p3);
    printf("\n");
    // 释放内存
    free(p1);
    free(p2);
    //检查释放内存后,结构体指针指向的地址 
    printf("Name1: %p\n", p1);
    printf("Name2: %p\n", p2);
    printf("Name3: %p\n", p3);
    
    printf("Name1: %p\n", &p1);
    printf("Name2: %p\n", &p2);
    printf("Name3: %p\n", &p3);

    return 0;
}


运行结果:

Name1: 0000000000000001
Name2: 0000000000000000
Name3: 0000000000000020
Name1: 000000000062FE18
Name2: 000000000062FE10
Name3: 000000000062FE08

Name: Alice, Age: 30, Height: 1.75 meters
Name: Bob, Age: 25, Height: 1.80 meters

Name1: 0000000000A27550
Name2: 0000000000A275A0
Name3: 0000000000000020
Name1: 000000000062FE18
Name2: 000000000062FE10
Name3: 000000000062FE08

Name1: 0000000000A27550
Name2: 0000000000A275A0
Name3: 0000000000000020
Name1: 000000000062FE18
Name2: 000000000062FE10
Name3: 000000000062FE08

2问题

2.1对于任何变量,只要是定义了两个不同名称的指针,那么这两个指针的地址是不一样的,对吗?不过这两个指针应该指向同一个变量呀?

不,**两个不同名称的指针变量本身就会占用不同的内存地址。**每个指针变量都是一个独立的变量,它有自己的地址,这个地址是存储指针值的地方。当你定义两个指针变量时,它们默认不会指向同一个变量,除非你显式地将它们指向同一个地址。
这里有一个例子来说明:

int main() {
    int var = 10;       // 定义一个整型变量var
    int *ptr1;          // 定义一个整型指针ptr1
    int *ptr2;          // 定义另一个整型指针ptr2
    ptr1 = &var;        // 将ptr1指向var的地址
    ptr2 = ptr1;        // 将ptr2也指向ptr1所指向的地址,即var的地址
    printf("Address of var: %p\n", &var);
    printf("Address stored in ptr1: %p\n", ptr1);
    printf("Address stored in ptr2: %p\n", ptr2);
    printf("Address of ptr1: %p\n", &ptr1);
    printf("Address of ptr2: %p\n", &ptr2);
    return 0;
}

输出将类似于:

Address of var: 0x7ff7bfeff8ac
Address stored in ptr1: 0x7ff7bfeff8ac
Address stored in ptr2: 0x7ff7bfeff8ac
Address of ptr1: 0x7ff7bfeff8a0
Address of ptr2: 0x7ff7bfeff898

在这个例子中,ptr1ptr2 都指向了同一个变量 var,但是它们自己的地址(即存储指针值的地方)是不同的。这是因为 ptr1ptr2 是两个不同的指针变量,它们各自在内存中有自己的位置。

2.2未初始化的指针“野指针”问题

struct Person {
    char name[50]; // 姓名
    int age;       // 年龄
    float height;  // 身高(以米为单位)
};struct Person *p1; printf("Name1: %p\n", p1);
/*这段代码,其实指针p1内部存的地址是随机的,因为还没有产生结构体变量实体,还没有给结构体分配地址,对吗?*/

是的,这个理解是对的。在提供的代码片段中:

struct Person *p1;
printf("Name1: %p\n", p1);

您定义了一个指向 Person 结构体的指针 p1,但是在您打印 p1 的值之前,您没有让它指向任何有效的 Person 结构体实例。因此,p1 内部存储的地址是未初始化的,它可能包含一个随机值,这个值取决于 p1 变量所在内存区域之前的状态。
在C语言中,未初始化的指针被称为“野指针”,试图访问野指针所指向的内存可能会导致未定义的行为,包括程序崩溃。为了避免这种情况,您应该在尝试访问指针所指向的内存之前确保指针已经被正确地初始化。
例如,您可以这样做:

struct Person *p1 = malloc(sizeof(struct Person)); // 动态分配内存
if (p1 != NULL) {
    printf("Name1: %p\n", p1);
    // 使用p1指向的结构体实例...
    free(p1); // 使用完毕后释放内存
} else {
    printf("Memory allocation failed\n");
}

在这个例子中,我使用了 malloc 函数来动态分配一个 Person 结构体大小的内存块,并将返回的指针赋值给 p1。然后,我检查了 malloc 是否成功分配了内存(即 p1 不是 NULL)。如果分配成功,我就可以安全地使用 p1 指向的结构体实例。最后,使用完毕后,我调用 free 函数来释放分配的内存。

2.3未初始化的指针的值是随机的吗?

是的,未初始化的指针的值是随机的。在C语言中,当你声明一个指针变量但未将其初始化为一个特定的地址时,它将包含一个不确定的值,这个值是它所指向的内存地址的随机值。这个值可能是有效的内存地址,也可能是无效的,甚至可能是操作系统不允许你的程序访问的地址。
例如:

int *p; // 声明一个整型指针,但未初始化

在这个例子中,p 的值是未定义的,它可能指向任何地方。尝试访问 p 所指向的内存(例如,通过 *p)会导致未定义的行为,可能会导致程序崩溃或其他意外结果。
因此,在使用指针之前,你应该总是初始化它,要么让它指向一个具体的变量,要么将它设置为 NULL。将指针设置为 NULL 是一个良好的编程习惯,因为它可以帮助你避免访问未初始化的内存。在后续的代码中,你可以检查指针是否为 NULL,以确定它是否已经被正确地初始化。

int *p = NULL; // 初始化指针为NULL

现在,p 是一个空指针,它不指向任何有效的内存地址。在尝试解引用 p 之前,你可以检查它是否为 NULL,这样可以避免空指针解引用错误。

2.4为什么释放内存之后,p1、p2、p3这三个指针还是指向分配内存时的地址?

在C语言中,使用 malloc 分配的内存会在调用 free 之后被释放,但是 free 函数并不会改变指针本身的内容。换句话说,free 不会将指针设置为 NULL 或其他值,它只是释放指针所指向的内存,并使得这块内存可以被操作系统重新分配给其他用途。
在你提供的代码中,p1p2 指针在调用 free 之后仍然包含它们之前所指向的地址。这个地址现在可能不再有效,因为那块内存已经被释放了。这是一个常见的问题,因为持有已经被释放的内存地址的指针被称为“悬垂指针”,试图访问悬垂指针所指向的内存会导致未定义的行为。
下面是代码中的一些关键点:

  1. 在调用 free 之后,p1p2 仍然指向它们之前所分配的内存地址。这是因为在 free 之后,指针的内容没有被改变。
  2. p3 指针在代码中从未被分配内存,因此它仍然是一个野指针,其值是未定义的。
  3. 输出指针 p1p2p3 本身的地址(&p1&p2&p3)会显示这些指针变量在栈上的地址,这些地址在程序的生命周期内是不会改变的。
    为了避免悬垂指针的问题,一个好的做法是在释放内存后将指针设置为 NULL
free(p1);
p1 = NULL; // 将p1设置为NULL,避免悬垂指针
free(p2);
p2 = NULL; // 将p2设置为NULL,避免悬垂指针

这样,即使指针在释放后仍然存储着之前的地址,你也知道这个地址不再有效,因为指针的值为 NULL。在后续的代码中,你可以检查指针是否为 NULL 来避免访问已经释放的内存。

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九层指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值