一、指针案例
案例一
#include <stdio.h>
int main() {
// a是整形变量
int a = 10;
// 声明一个指向整型的指针pa,把变量a的地址赋予它
int* pa = &a;
// 输出指针存储的地址值
printf("%p", pa);
return 0;
}
- 声明并初始化变量:创建一个整型变量
a
,并初始化为 10。 - 声明并初始化指针:创建一个指向整型的指针
pa
,并将变量a
的内存地址赋给它。 - 输出指针值:使用
printf
函数和%p
格式说明符,以十六进制形式输出指针pa
存储的地址值。
案例二:
代码声明了四种不同数据类型的指针(char*
、short*
、int*
、double*
),并通过 sizeof
操作符分别计算它们的大小。在大多数系统中,所有指针类型占用的内存空间是相同的,因为它们存储的是内存地址,而地址的位数取决于系统架构(32 位或 64 位)。
#include <stdio.h>
int main() {
char* pc = NULL;
short* ps = NULL;
int* pi = NULL;
double* pd = NULL;
// sizeof 返回的值的类型是无符号类型,也就是 unsigned int
printf("%d\n",sizeof(pc));
printf("%d\n",sizeof(ps));
printf("%d\n",sizeof(pi));
printf("%d\n",sizeof(pd));
}
- 指针大小与类型无关:
无论指针指向何种数据类型(char
、short
、int
、double
等),其占用的内存空间只取决于系统架构。- 32 位系统:地址总线为 32 位,指针占用 4 字节(32 位)。
- 64 位系统:地址总线为 64 位,指针占用 8 字节(64 位)。
sizeof
的返回值类型:
sizeof
返回的是size_t
类型,它是无符号整数类型(通常定义在<stddef.h>
中)。在 64 位系统中,size_t
可能是unsigned long
或unsigned long long
,因此使用%d
(有符号整数)打印可能会导致输出异常。建议使用%zu
来正确打印size_t
类型的值:
printf("%zu\n", sizeof(pc)); // 使用%zu格式说明符
- 空指针(NULL)的大小:
所有类型的空指针(如NULL
)在内存中占用的空间与普通指针相同,因为NULL
本质上是一个特殊的地址值(通常为 0)。
二、指针类型的定义
指针就是内存地址,我们所说的指针变量也就是存放内存地址的变量,变量有int,float等等类型,那我们的指针变量也需要不同的类型来存放内存地址变量。
#include <stdio.h>
int main() {
int a = 0x11223344;
int* pa = &a;
// 打印指针pa的值,即变量 a 的地址。
printf("%p\n", pa);
// 打印变量 a 的当前值(以十六进制形式显示)。
printf("修改前a的值(十六进制): 0x%x\n", a);
// 通过指针 pa 修改变量 a 的值。
// *pa 表示通过指针 pa 解引用,即访问 pa 所指向的变量(这里是 a),并将其值设置为 0。
*pa = 0;
printf("修改后a的值(十六进制): 0x%x\n", a);
return 0;
}
C 语言中指针的基本使用
#include <stdio.h>
int main() {
// 定义一个int类型的变量,值为42
int num = 42;
// 定义一个int类型的指针变量,并初始化为变量num的地址
int* ptr = #
// 输出指针ptr中存储的值(即ptr所指向的地址,也就是num的地址)
printf("指针ptr中存储的地址: %p\n", ptr);
// 直接输出变量num的地址(与ptr中存储的地址相同)
printf("变量num的地址: %p\n", &num);
// 对指针ptr进行解引用操作,输出ptr所指向的内存位置存储的值(即num的值)
printf("ptr所指向的值: %d\n", *ptr);
return 0;
}
各 printf
语句输出内容解释:
printf("指针ptr的地址: %p\n", ptr);
- 输出的是指针
ptr
中存储的地址值,即变量num
的地址(例如:0x7ffd8a3c6a5c
)。
- 输出的是指针
printf("变量num的地址: %p\n", &num);
- 输出的是变量
num
本身的地址,与ptr
中存储的地址完全相同(例如:0x7ffd8a3c6a5c
)。
- 输出的是变量
printf("ptr所指向的值: %d\n", *ptr);
- 输出的是指针
ptr
所指向的内存位置存储的值,即num
的值42
。
- 输出的是指针
关键概念总结:
- 指针变量
ptr
:存储的是变量num
的地址。 &num
:取变量num
的地址。*ptr
:对指针ptr
进行解引用,访问其指向的内存位置存储的值。
更多情况展示
#include <stdio.h>
int main() {
int num = 42;
int* ptr = #
printf("错误(未定义行为): %p\n", *ptr); // 错误示例
printf("ptr存储的地址: %p\n", ptr); // 正确:ptr的值
printf("num的地址: %p\n", &num); // 正确:&num的值
printf("ptr和&num是否相同: %d\n", ptr == &num); // 输出1(真)
printf("ptr自己的地址: %p\n", &ptr); // ptr变量本身的地址
printf("ptr指向的值: %d\n", *ptr); // 42
return 0;
}
输出
错误(未定义行为): 000000000000002A
ptr存储的地址: 000000000062FE1C
num的地址: 000000000062FE1C
ptr和&num是否相同: 1
ptr自己的地址: 000000000062FE10
ptr指向的值: 42
总结
- 使用
%p
时:必须传递指针(地址),而不是整数。 - 区分:
ptr
(地址值) vs*ptr
(地址指向的值)。ptr
(num
的地址) vs&ptr
(指针自己的地址)。
扩充
在 C 语言中,指针必须存储内存地址,而不是普通整数值。
int num = 42;
int* s = num; // 错误!
正确写法
如果想让指针 s
指向 num
,必须用 &
取地址符:
int num = 42;
int* s = # // 正确!s存储num的地址
三、直接访问内存实例
以下是一个在 C 语言中直接访问内存地址的示例。需要注意,这种操作非常危险,可能导致程序崩溃或安全漏洞
#include <stdio.h>
int main() {
int num = 42; // 定义一个变量,存储在某个内存地址中
// 获取变量num的地址
int* ptr = #
printf("num的地址: %p\n", (void*)ptr);
// 通过指针直接修改内存中的值
*ptr = 99;
printf("修改后num的值: %d\n", num); // 输出: 99
// 定义一个新的指针,指向固定地址(仅作示例,不要在实际代码中这样做)
// 注意:这个地址可能不存在或不可写,会导致段错误!
int* dangerous_ptr = (int*)0x12345678; // 假设的固定地址
// *dangerous_ptr = 100; // 危险操作!可能导致程序崩溃
return 0;
}
四、解引用操作符
在 C 语言中,*
是解引用操作符,用于访问指针所指向的内存地址中的值。
一、何时需要使用 \*
打印值?
当你需要访问指针指向的变量的值时,使用 *
。
int num = 42;
int* ptr = # // ptr存储num的地址
printf("%d\n", *ptr); // 输出:42(访问ptr指向的值)
示例 2:通过指针修改值后打印
int num = 42;
int* ptr = #
*ptr = 99; // 修改ptr指向的值(即num的值)
printf("%d\n", *ptr); // 输出:99
printf("%d\n", num); // 输出:99(num已被修改)
二、何时不使用 \*
?
当你需要打印指针本身存储的地址值时,直接使用指针变量名(无需 *
)。
示例 3:打印指针存储的地址
int num = 42;
int* ptr = #
printf("%p\n", ptr); // 输出:num的地址(如0x7ffe536b8aec)
printf("%p\n", &num); // 同上:直接取num的地址
三、常见混淆场景
1. 指针变量 vs 指针指向的值
int num = 42;
int* ptr = #
// 混淆点:区分以下两个操作
printf("%p\n", ptr); // 打印ptr存储的地址(0x7ffe...)
printf("%d\n", *ptr); // 打印ptr指向的值(42)
2. 多级指针(如 int\**
)
int num = 42;
int* ptr = #
int** pptr = &ptr; // 二级指针,存储ptr的地址
printf("%p\n", pptr); // 输出:ptr的地址
printf("%p\n", *pptr); // 输出:num的地址(即ptr的值)
printf("%d\n", **pptr); // 输出:42(先解引用得到ptr,再解引用得到num)
四、判断口诀
- 看需求:
- 若需访问指针指向的值 → 使用
*
。 - 若需查看指针存储的地址 → 直接用指针变量名。
- 若需访问指针指向的值 → 使用
- 结合
%p
和%d
:%p
(打印地址) → 直接用指针(如ptr
、&num
)。%d
(打印整数) → 用解引用(如*ptr
)。
五、示例
#include <stdio.h>
int main() {
int num = 42;
int* ptr = #
printf("num的值: %d\n", num); // 42
printf("ptr指向的值: %d\n", *ptr); // 42
printf("ptr存储的地址: %p\n", ptr); // 0x7ffe...
printf("ptr自己的地址: %p\n", &ptr); // 另一个地址
return 0;
}
六、注意事项
空指针解引用:
int* ptr = NULL;
printf("%d\n", *ptr); // 错误!解引用空指针会导致崩溃
未初始化的指针:
int* ptr; // 未初始化,可能指向随机地址
*ptr = 10; // 危险!可能覆盖重要内存
数组名作为指针:
int arr[3] = {1, 2, 3};
printf("%d\n", *arr); // 输出:1(数组名隐式转换为指向首元素的指针)
评论区