结论
只有通过指针存取内存时,才会出现运行时报错。
实验
offsetof()函数
struct A {
int x;
char y;
long z;
};
int main() {
int offset = ((struct A*)NULL)->y; // 即 offsetof()的实现方法
printf("offset of y: %d\n", offset); // 打印 4,不会报错
}
以上就是c语言的宏函数offsetof
的原理,其定义如下:
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
大家可以将其展开看看,和我的例子是一样的。
这里可以看到,编译器不会报错,因为这里没有对NULL指针进行取值。
进行内存存取操作时,运行时报错
struct Node {
int val;
Node* next;
Node() : val(0), next(NULL) {}
};
int main() {
Node* head = NULL;
head->next->next; // 这里不会报错,因为没有任何内存存取操作
Node* ptr = head->next; // 这里报错,因为我们尝试读取NULL指向的内存
head->next = NULL; // 这里报错,因为我们尝试对NULL指针指向的内存赋值
}
NULL指针原理猜测
这里只是笔者的猜测,不一定正确。
我们知道NULL的值就是0,其定义如下:
#define NULL 0
那么,为什么0
可以用来检测内存地址是否有效呢?
c语言的内存模型如下图所示:
从上图来看,低地址区域存放的是代码段(text),也就是说,内存的起始位置(0
地址处),不应该有数据,那么0
地址当然可以用来判断内存地址的有效性了。