计算机系统应用教程网站

网站首页 > 技术文章 正文

空指针,段错误,这场面试我栽倒在这里了

btikc 2024-09-12 12:08:27 技术文章 22 ℃ 0 评论

作者 | 李肖遥

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

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

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

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

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

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

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

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

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

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

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

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

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

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

的标准定义

#if !defined && defined(__NEEDS_)

#ifdef _cplusplus

#define 0 // 这里对应C++的情况

#else

#define (void *)0 // 这里对应C语言的情况

#endif

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

在 C/C++ 中的区别

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

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

的本质是什么

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

从指针角度来看:

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

int func

{

int *p;

...

}

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

int func

{

int *p = ;

...

}

什么是空指针( pointer)?

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

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

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

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

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

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

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

有什么作用?

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

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

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

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

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

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

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

是不是0

就是 0?

我们先来看以下代码:

//https://tool.lu/coderunner/

#include<stdio.h>

int main

{

int *p=;

printf("%s",p);

}

结果如下:

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

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

不是 0?

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

//https://tool.lu/coderunner/

//来源:技术让梦想更伟大

//作者:李肖遥

#include<stdio.h>

int main

{

int *node = ;

int p = 0;

p = *node;

printf("%d\n",p);

return 0;

}

编译结果如下:

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

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

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

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

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

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

//https://tool.lu/coderunner/

//来源:技术让梦想更伟大

//作者:李肖遥

#include <stdio.h>

void vPassByFun(int *node)

{

static int N = 1024;

node = &N;

}

int main

{

int *node = ;

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 = ;

int p = 0;

vPassByFun(&node);

p = *node;

printf("%d\n",p);

return 0;

}

运行结果如下:

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

最后

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

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表