作者 | 李肖遥

来源 | 技术让梦想更伟大(ID:gh_f7effb2fbc1c)

面试官:满头的汗的,来面试的路一定很远吧?

还好还好,骑车不到俩小时

面试官:来先喝杯水,咱们面试不急,边喝边聊

哇,谢谢您啦,咕隆咕隆喝下半杯

面试官:那咱们开始吧,看你项目做的还不少啊,不错不错,咱们随便聊聊。

哇,“这面试官还不错,感觉有戏”

面试官:说说空指针NULL是本质什么,与0一样吗

嗯嗯,这个,NULL不就是0吗?

面试官:你确定是吗,面带微笑,淡定的眼神......

嗯嗯,应该是吧,,,不是吧,汗又开始冒起了来了......

面试官:嗯嗯,了解了,不急慢慢喝水,我先上个厕所。

水的滋润与口水相互交织,骑车两小时,面试3分钟,这次我栽在这里了。

NULL 在 C/C++ 中的标准定义

NULL的标准定义

#if !defined(NULL) && defined(__NEEDS_NULL)
#ifdef _cplusplus
#define NULL 0         // 这里对应C++的情况
#else
#define NULL (void *)0 // 这里对应C语言的情况
#endif

编译器预先定义了一个宏 _cplusplus,来判断当前的编译环境是 C++ 的还是 C 语言的,在 C++ 定义为 0,在 C 语言中定义为 (void *)0。

在 C/C++ 中的区别

在 C 语言中,C 中的“标准”写法,NULL 被替换为一个 void* 类型的指针右值,值等于0;由于是 void*  类型,可以隐式转化为其它类型的指针。

在 C++ 中,void* 无法自由隐式转换为其它类型的指针,而字面量 0 可以隐式转换为指针类型。

NULL 的本质是什么

我们从指针,空指针,空指针常量以及指向的内存说起。

从指针角度来看:

我们看以下定义,p 是一个函数内的局部变量,则 p 的值是随机的,也就是说 p 是一个野指针。

int func()
{int *p; ...
}

再看以下函数,p 是一个局部变量,分配在栈上的地址,p 的值是 (void *)0,实际就是 0x00000000,意思就是指针 p 指向内存的 0x00000000 地址处。这时候p就不是野指针了。

int func()
{int *p = NULL; ...
}

什么是空指针(null pointer)?

如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)将不相等的值与指向任何对象或函数的指针进行比较。

定义 char *p=0 后,在之后 p 的任何一种赋值操作之后,p 都成为一个空指针,即 p 不指向任何实际的对象或者函数。反过来说,任何对象或者函数的地址都不可能是空指针。

什么是空指针常量(null pointer constant)?

值为 0 的整数常量表达式,或强制转换为 void * 类型的表达式,称为空指针常量。

空指针(null pointer)指向了哪里的内存?

这里标准没有定义,取决于系统的实现。我们常见的空指针一般指向 0x00000000 地址,即空指针的内部用全 0x00000000 来表示,也有一些系统用一些特殊的地址值或者特殊的方式表示空指针。

在我们实际写代码时,关键点在于判断哪个是空指针。

NULL 有什么作用?

在大部分的 CPU 中,内存的 0x00000000 地址处都不是可以随便访问的,所以野指针指向了这个区域可以保证野指针有个安家之所,否则会发生段错误。

当你尝试访问的时候会阻止你,但是有些地址不是只读的,如果一个指针指向了这个地址,你又在不经意间修改了它,可能会导致一些重要的文件被修改,所以指针初始化成 NULL 是有必要的。

注意不要混用 '\0'  和 '0' 和 0  和 NULL。

  • '\0' 是一个转义字符,他对应的 ASCII 编码值是 0,本质就是 0;常用于表示字符串的结尾标志,以判断字符串有没有到头。

  • '0' 是一个字符,他对应的 ASCII 编码值是48,本质是48。

  • 0 是一个 int 类型的数字,本质就是 0。

  • NULL 是一个表达式,是强制类型转换为 void * 类型的 0,一般用来比较指针是否是一个野指针。

NULL是不是0

NULL 就是 0?

我们先来看以下代码:

//https://tool.lu/coderunner/
#include<stdio.h>
int main()
{    int *p=NULL;    printf("%s",p);
}

结果如下:

输出 (null),在执行 int *p=NULL,打印出来空白,实际上 p 的值为 0x00000000,在 C 语言中,NULL 的本质是 0,但是这个 0 不是当一个整形数据来解析,而是当一个内存地址来解析的,代表的是内存的 0 地址。

(void *)0 这个整体表达式表示一个指针,地址在哪里取决于指针变量本身,这个指针变量指向 0 地址(实际是 0 地址开始的一段内存)。

NULL 不是 0?

如果一个指针被赋予 NULL,相当于这个指针执行了 0x00000000 这个逻辑地址,但是在 C 语言中 0x0000 这个逻辑地址用户是不能使用的,所以当你试图取一个指向了 NULL 的指针的内容时,就会提示段错误,测试一下代码如下:

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥
#include<stdio.h>
int main()
{    int *node = NULL;int p = 0;p = *node;printf("%d\n",p);return 0;
}

编译结果如下:

由于指针 node 执行的是 NULL,也就是逻辑地址 0x00000000,而这个地址不能访问,所以编译器提示段错误。

那么看到这里你觉得 NULL 还是 0 吗?根据宏定义我们知道:(void *)0 表示把数值 0 强制转换为 void * 类型,所以最后运行结果为 0。

变量在定义时,系统会给他分配内存空间,指针变量也是一样,如果指针没有指向的话,那么地址就是随机值,如果不小心用的话就会导致数据错误,从而使程序退出。

NULL 使指针 p 指向地址 0x00000000,在大多数系统中都将 0x00000000 作为不被使用的地址,所以运用 p 也不会毁坏数据。

但也有系统会使用地址 0x00000000,而将 NULL 定义为其他值,所以不要把 NULL 和 0 等同起来。

我们使用值传递的方式来看,在网上有一个面试题,这里我参考一下代码,能够帮助大家更好的理解,其代码如下:

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>void vPassByFun(int *node)
{static int N = 1024;node = &N;
}int main()
{int *node = NULL;int p = 0;vPassByFun(node);p = *node;printf("%d\n",p);return 0;
}

输出结果如下:

vPassByFun 函数是值传递,node 指针变量的值并不受影响,所以这个程序的效果和上一个程序运行结果都是段错误。

如果要让结果为 1024,应该怎样写代码呢?我们来这样写代码:

//https://tool.lu/coderunner/
//来源:技术让梦想更伟大
//作者:李肖遥
#include <stdio.h>void vPassByFun(int ** node)
{static int N = 1024;*node=&N;
}int main()
{int *node = NULL;int p = 0;vPassByFun(&node);p = *node;printf("%d\n",p);return 0;}

运行结果如下:

传递一个二级指针也就是传递 node 指针变量的指针给 vPassByFun 函数,这样的话结果就对了。

最后

编码过程中,我们需要对自己的指针负责,往往导致 bug 出现或者找不到问题所在地的就是这种细节。最后,原创不易,希望能够改正文章的错误,多提意见留言,谢谢。

更多精彩推荐
☞连按 5 次 Shift 重改 CMD 和密码并重启电脑,这个漏洞你不能不知道!
☞如何判断领导是在培养你,还是压榨你?看这4点就够了
☞微信iOS版朋友圈可删除评论;华为回应安卓系统断供;银河麒麟操作系统V10发布| 极客头条
☞万字长文总结机器学习的模型评估与调参 | 附代码下载
☞什么?一个核同时执行两个线程?
☞交易平台搅局者“Uniswap之父”,不会编程的“失业青年”,出手即巅峰
点分享点点赞点在看

空指针,段错误,这场面试我栽倒在这里了!相关推荐

  1. xxl-job 执行结果是空_空指针,段错误,这场面试我栽倒在这里了!

    空指针,段错误,这场面试我栽倒在这里了! NULL在C/C++中的标准定义 NULL的标准定义 #if !defined(NULL) && defined(__NEEDS_NULL) ...

  2. Rust语言——无虚拟机、无垃圾收集器、无运行时、无空指针/野指针/内存越界/缓冲区溢出/段错误、无数据竞争...

    2006年,编程语言工程师Graydon Hoare利用业余时间启动了Rust语言项目.该项目充分借鉴了C/C++/Java/Python等语言的经验,试图在保持良好性能的同时,克服以往编程语言所存在 ...

  3. 宋宝华:让Linux的段错误(segmentation fault)不再是一个错误

    今天周末,娃儿们配合不闹事,写一篇短小精悍的文章吧,反正文章长了大家也没时间看.今天文章的目标是,如何在进程访问空指针等情况下,产生段错误后,不再退出而是继续运行. 这件事情,对于熔断(meltdow ...

  4. 如何系统有效地准备一场面试

    如何系统有效地准备一场面试 1. 澄清阶段 澄清阶段的任务,是理清我们有什么.想要什么,明确自己的求职目标.这个阶段的工作,可以分为下面 6 部分: 明确自己的职业价值观 盘点自我价值 筛选公司 分析 ...

  5. 9个offer,12家公司,35场面试,从微软到谷歌,应届计算机毕业生的2012求职之路[对书籍加了注释]

    9个offer,12家公司,35场面试,从微软到谷歌,应届计算机毕业生的2012求职之路[对书籍加了注释] 引言:同样是求职,和前面一篇blog一样,看出了很大的差距,多花了些时间,把作者列举的书籍通 ...

  6. (转载)职场面试如何自我评价,面试回答问题技巧

    转载:http://www.201980.com/lizhi/zhichang/1541.html 职场里,职场面试是很平常不过的事了,不一定是职场新人为了找工作的面试,参加工作以后,竞聘岗位有竞聘演 ...

  7. java 主动抛出 段错误_段错误产生的原因~

    原文:https://blog.csdn.net/qq_29350001/article/details/53780697 一.什么是段错误? 一旦一个程序发生了越界访问,cpu 就会产生相应的保护, ...

  8. linux 下 C 编程和make的方法 (十、C版的try catch 捕捉段错误和异常处理)

    2019独角兽企业重金招聘Python工程师标准>>> 哇塞,C语言有try catch吗?当然没有.倒..可能有人说了,那你野鬼说没有的东西做什么. 这里需要重申一下,所谓正向设计 ...

  9. linux下gdb使用core文件调试程序,解决“段错误核心已转储“的问题

    一.core文件介绍以及用途: 1.core文件是什么? core文件包含了程序运行时的内存状态.寄存器状态.堆栈指针.内存管理信息以及各个函数使用堆栈信息等等: 当程序运行过程中出现段错误(Segm ...

最新文章

  1. 博客 rss 如何使用_如何使用RSS从您的GatsbyJS博客自动交叉发布
  2. python接口自动化测试框架链接数据库_python接口自动化测试框架实现之操作mysq数据库...
  3. 艾伟也谈项目管理,谈谈如何说“不”
  4. react取消捕获_react 异常捕获
  5. 【HDU - 2570】迷瘴 (贪心,水题,排序,卡精度有坑)
  6. springcloud项目的启动顺序_Spring Cloud微服务项目完整示例,含注册中心,网关,断路器等等...
  7. Trie树(c++实现)
  8. python函数分为哪几类_Python 69个内置函数分8类总结
  9. php面试编程题_一位资深php程序员在北京的面试30个题目
  10. OGG迁移大库注意要点之impdp导入产生大量归档
  11. AI(4)---AI+IOT=?
  12. c语言程序设计中&是什么,C语言程序设计是什么
  13. java导入hbase_如何用java导入hbase.dat文件
  14. VBScript基础
  15. 【气动学】基于龙格库塔算法实现外弹道仿真含Matlab源码
  16. prometheus中step或resolution的含义
  17. iptables限速 iptables限制流量
  18. 计算机应用物联网应用技术论文,物联网的关键技术及计算机物联网的应用研究...
  19. Unity中控制物体运动
  20. 如何把PPT连背景一起复制?

热门文章

  1. 【CVPR2020】目标检测方向论文更新
  2. html5 网页游戏 开源,HTML5 网页游戏,基于 WebGL 打造
  3. SAP WM Storage Type配置界面里的Addn to Stock
  4. 「翻译」SAP零售预测和补货–简要概述
  5. 打造标杆,中科院人工智能战队发布新一代智算平台
  6. 了解一下:机器学习性能优化的6个指标
  7. 深度学习--感知机讲解
  8. 手工计算深度学习模型中的参数数量
  9. 中国CIO最关心的八大问题(上)
  10. SAP MM ME21N 创建PO时报错 - Net price in CNY becomes too large – 之对策