目录
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
在这个例子中,ptr1
和 ptr2
都指向了同一个变量 var
,但是它们自己的地址(即存储指针值的地方)是不同的。这是因为 ptr1
和 ptr2
是两个不同的指针变量,它们各自在内存中有自己的位置。
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
或其他值,它只是释放指针所指向的内存,并使得这块内存可以被操作系统重新分配给其他用途。
在你提供的代码中,p1
和 p2
指针在调用 free
之后仍然包含它们之前所指向的地址。这个地址现在可能不再有效,因为那块内存已经被释放了。这是一个常见的问题,因为持有已经被释放的内存地址的指针被称为“悬垂指针”,试图访问悬垂指针所指向的内存会导致未定义的行为。
下面是代码中的一些关键点:
- 在调用
free
之后,p1
和p2
仍然指向它们之前所分配的内存地址。这是因为在free
之后,指针的内容没有被改变。 p3
指针在代码中从未被分配内存,因此它仍然是一个野指针,其值是未定义的。- 输出指针
p1
、p2
和p3
本身的地址(&p1
、&p2
、&p3
)会显示这些指针变量在栈上的地址,这些地址在程序的生命周期内是不会改变的。
为了避免悬垂指针的问题,一个好的做法是在释放内存后将指针设置为NULL
:
free(p1);
p1 = NULL; // 将p1设置为NULL,避免悬垂指针
free(p2);
p2 = NULL; // 将p2设置为NULL,避免悬垂指针
这样,即使指针在释放后仍然存储着之前的地址,你也知道这个地址不再有效,因为指针的值为 NULL
。在后续的代码中,你可以检查指针是否为 NULL
来避免访问已经释放的内存。