基于前提 int* p = nullptr; int a = 8;(nullptr 是 C++ 中更安全的空指针,指向 0 地址,程序无权访问)分析几种赋值情况,用结构化表格拆解每一种写法的核心信息,包括类型匹配、编译/运行状态、错误原因/执行结果,一眼分清合法/非法场景:
| 写法 | 等号两侧类型 | 编译状态 | 运行状态 | 核心原因/执行结果 | 关键结论 |
|---|---|---|---|---|---|
p = a | 左:int*(p)右: int(a=8) | 警告(非报错) | 无直接崩溃(但p变野指针) | 把数值8赋值给指针p,p的地址变为8(非法内存地址),p从“空指针”变成“野指针”; 仅赋值不解引用时程序不崩溃,但后续解引用必崩溃 | 类型不匹配,非法赋值 |
p = 9 | 左:int*(p)右: int(9) | 警告(非报错) | 无直接崩溃(但p变野指针) | 把数值9赋值给指针p,p的地址变为9(非法内存地址),p成野指针; 仅赋值不崩溃,后续解引用必崩溃 | 类型不匹配,非法赋值 |
p = &a | 左:int*(p)右: int*(&a) | 完全通过(无警告/报错) | 正常(无崩溃) | 把变量a的合法内存地址赋值给p,p从“空指针”变为“指向a的合法指针”; 此时p的地址是a的真实地址(如0x7ffeefbff5c4) | 类型匹配,合法赋值(推荐) |
*p = &a | 左:int(*p)右: int*(&a) | 直接报错(无法编译) | 无法运行 | *p 是“p指向的int类型数值”,只能接收int值;&a 是指针类型(地址),类型完全不兼容,编译器直接拦截 | 类型不匹配,编译错误(必改) |
*p = a | 左:int(*p)右: int(a=8) | 完全通过(无警告/报错) | 立即崩溃 | 类型匹配,但p是nullptr(指向0地址,系统保留);解引用空指针往0地址写数值8,触发“内存非法访问” | 类型匹配但指向非法,运行崩溃 |
*p = 9 | 左:int(*p)右: int(9) | 完全通过(无警告/报错) | 立即崩溃 | 类型匹配,但p是nullptr;解引用空指针往0地址写数值9,触发“内存非法访问” | 类型匹配但指向非法,运行崩溃 |
补充关键细节(新手必懂)
-
关于“编译警告 vs 编译报错”:
p=a/p=9是“类型不匹配但编译器允许隐式转换”,所以仅出警告(如GCC的-Wint-conversion),但依然是非法写法;*p=&a是“类型完全不兼容(int无法接收int*)”,编译器直接报错,程序连编译都通不过。
-
关于“空指针(nullptr)的解引用”:
nullptr是专门标记“指针无合法指向”的关键字,解引用*p(无论赋值什么)都会崩溃——因为它指向的0地址是操作系统内核保留的,普通程序无权读写。 -
合法场景的延伸(基于
p=&a): 若先执行p=&a(让p指向合法地址),再执行*p=a/*p=9,则完全合法:int* p = nullptr; int a = 8; p = &a; // 先让p指向a的合法地址 *p = a; // ✅ 合法:a的值保持8 *p = 9; // ✅ 合法:a的值被改为9
核心规则总结(覆盖所有场景)
-
赋值给指针变量
p(p=xxx):- 只有
xxx是「int*类型的合法地址」(如&a、malloc返回值),才合法; - 赋值普通int值(如
a、9),类型不匹配,p变野指针。
- 只有
-
赋值给解引用指针
*p(*p=xxx):- 第一步:检查类型——只有
xxx是int类型(如a、9),编译才通过; - 第二步:检查指向——只有
p指向合法地址(如p=&a),运行才不崩溃; - 若p是
nullptr/野指针,即使类型匹配,运行必崩溃。
- 第一步:检查类型——只有
-
绝对禁忌: 永远不要写
*p=&a(类型不匹配,编译报错); 永远不要解引用nullptr/野指针(运行必崩溃)。