C中野指针、空指针、万能指针问题
目录:
一.内存空间
1.1 代码区(text segment)
1.2 静态全局区
1.3 堆区
1.4 栈区
二.变量在内存中的作用域和未初始值
2.1 变量的作用域:变量起作用的范围
2.2 不同变量的作用域
三. 指针的步长
3.1 为什么要定义指针的步长这一概念?
3.2 什么是指针的步长?
四.野指针
4.1什么是野指针?
4.2 为什么说野指针的指向是随机的,不可操作的?
4.3 如何避免野指针?
4.4 如果我们定义了一个野指针,肯定是不能正常使用的,那么在定义后系统编译是否会报错呢?
4.5 野指针的典型案例
五.空指针(NULL指针)
5.1 什么是空指针?
5.2 空指针的作用是什么?
六.万能指针((void*)类型指针)
6.1 什么是万能指针?
6.2 为什么要使用万能指针?
6.3 使用万能指针的例子
一.内存空间
程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的。程序运行期间不能改变。然后运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区和堆区。
1.1 代码区(text segment)
加载的是可执行文件代码段,所有的可执行代码都加载代码区,这块内存在运行期间不可修改。
1.2 静态全局区
静态全局区就是存放那些静态和全局变量的。这些变量都是在执行main函数前就已经开辟了空间,只有在程序结束后才会释放空间。除了存放静态和全局变量以外,这个区还会存放文字常量。
我们在上面的示意图中可以看到,这个区分为两个不同的小区:
1>未初始化数据区(BSS):
加载的是可执行文件BSS段,位置可以分开也可以紧靠数据段,存放的是全局未初始化、静态未初始化的数据。
2>全局初始化数据区/静态数据区(data segment)
加载的是可执行文件数据段,存放的是全局、静态已初始化的数据。
此外,除了存储全局、静态已初始化数据,它还存储文字常量(只读)。
1.3 堆区
堆是一个大容器,它的容量很大,远远大于栈,但没有栈那样的先进后出顺序。这个空间一般用于动态内存分配(通过malloc()函数实现)。在内存中,堆则位于BSS区和栈区之间。一般由程序员分配和释放。如果程序员不释放的,程序结束后就会由操作系统回收。
1.4 栈区
栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放。所以,局部变量的生存周期为申请到释放该段的栈空间。
二.变量在内存中的作用域和未初始值
2.1 变量的作用域:变量起作用的范围
·代码块作用域(代码块是{}之间的一段代码)
·函数作用域
·文件作用域
2.2 不同变量的作用域
我们提到作用域,其实就是为了讨论变量的生命周期,所谓生命周期就是从开辟空间到释放空间这个过程。
我们一般在定义变量会同时初始化变量,以保证变量的有效性。那如果没有初始化怎么样?都会为0还是随机值?可否正常使用?
我们这里通过代码验证总结了四种不同的变量的作用域和定义时未初始化时这个变量内存空间所存放的数值:
1.局部变量 (auto自动变量)
在{ }范围之内定义的变量,并且仅在{ }内起作用
auto int a;//auto可省略
· 作用域:在定义变量的{ }之内有效
· 生命周期:程序运行变量定义处开辟空间,所在函数结束之后(或{ })释放空间
· 未初始化的值:随机
2. 静态(static)局部变量
在{ }范围之内定义的变量,在前面加上了static修饰的变量。
static int a;
· 作用域:在定义变量的{ }之内有效
· 生命周期:和程序运行周期相同,因为静态变量存放在静态全局区,在执行main函数之前就已经开辟了空间,程序结束之后才释放空间。
· 未初始化的值:0(注:静态局部变量只初始化一次)
3.全局变量
在函数之外定义的变量
· 作用域:整个工程,所有文件
· 生命周期:和程序运行周期相同,因为全局变量存放在静态全局区,在执行main函数之前就已经开辟了空间,程序结束之后才释放空间
· 未初始化的值: 0
4.静态全局变量
在函数之外定义的变量,加上static修饰的变量
static int a
· 作用域: 当前文件
· 生命周期: 和程序运行周期相同,因为静态全局变量存放在静态全局区,在执行main函数之前就已经开辟了空间,程序结束之后才释放空间
· 未初始化的值: 0
再进一步总结一下:
作用域:局部变量和静态局部变量的作用域在{ }内,全局变量在整个工程,局部变量在当前文件
生命周期:除了局部变量为从变量定义时开辟,函数结束后释放空间。其他都为在执行main函数之前就已开辟空间,程序结束后才释放空间。(即除了局部变量,其他的生命周期都为整个程序)
未初始化的值:除了局部变量为未初始化的值为随机值,其他都为0
三. 指针的步长
3.1 为什么要定义指针的步长这一概念?
我们知道,同一编译器下的内存大小都是一定的,而所谓地址也就是内存的编号,也就是说地址的范围也是固定的。(地址=指针=内存的编号)。既然指针变量存放的都是地址,也就是说,不管什么类型的指针变量,它的大小都是相同的。换个角度说,不管什么类型的指针,大小只与系统内存有关,只与编译器有关。
那么,在32位编译器中,指针变量的大小为4字节;而在64位编译器中,指针变量的大小则为8字节。
那么我们就会疑惑了。既然指针变量的大小都是一样的,那为什么我们还要定义不同类型的指针变量?
这是因为,虽然指针变量存放的都是地址,指针变量的大小都相同,但是不同类型的指针变量,取指针指向的内容的宽度是与类型有关系的。如果是char*类型的指针,在取内容时他只能取出一个字节的内容,而如果是int*类型的指针,在取内容时他只能取出4个字节的内容。
我们发现,不同指针类型的指针宽度是不同的。于是乎,就有了步长的概念。
3.2 什么是指针的步长?
所谓步长,就是不同类型的指针变量,取指针指向的内容宽度。
在代码中,步长就是指针变量+1所跨过的字节大小。
步长的大小往往等于该指针变量所存储的类型的地址对应的那个类型的大小。
四.野指针
4.1什么是野指针?
野指针,就是没有初始化的指针。野指针的指向是随机的、不可操作的。
4.2 为什么说野指针的指向是随机的,不可操作的?
首先,如果我们在栈区申请了一个局部变量指针,由于局部变量未初始化的值是随机的,那么指针在定义后就会随机保存一个地址,而这个地址可能指向内存中任何一个可能的地方,包括代码区、文字常量区。 而为了避免这样的情况发生,操作系统不允许操作一个指向区域是未知的指针所指向的内存空间,说的有点绕,就是操作系统不允许操作野指针指向的内存区域,因为它是未知的,不可操作的。
而如果在静态全局区申请了一个指针,如果没有初始化,则系统默认执行0x0000 0000,也就是NULL,这个地址存放的也是代码区,而代码区是用来加载可执行代码的。在运行期间这块内存不能修改。也就是说野指针的指向还是不可操作的。
总结一下,也就是说:
我们使用指针的原则:指针保存的地址一定是定义过的。
4.3 如何避免野指针?
使用野指针,也就是操作一个未初始化的指针所指向的空间怕是我们最容易犯的一个错误。
如果我们保证使用指针的原则,指针保存的地址一定是定义过的,就可以避免野指针的存在。我们要避免野指针其实就是要避免指针乱指,指向了不可操作的内存区,比如文字常量区和代码区等。
我们一般避免的方式有两种:
1.在定义后就让该指针指向一个已定义过的变量所处的内存空间
2.在堆区申请一块内存,让该指针直接指向这个在堆区的地址。因为堆区就是提供给用户动态申请和释放内存的大容器,是可操作的。
4.4 如果我们定义了一个野指针,肯定是不能正常使用的,那么在定义后系统编译是否会报错呢?
答案是不会。前面我们提到过,操作系统不允许操作一个指向区域是未知的指针所指向的内存空间,也就是说操作系统不允许操作一个野指针。但是,野指针不会直接引发错误,因为野指针就是一个指针变量,是变量就可以像整型等其他变量一样可以任意赋值,只需保证不越界即可。
这里我们总结一下:
野指针不会直接引发错误,操作野指针指向的内存空间才会出问题。
4.5 野指针的典型案例
int a = 100;
int *p;
p= a; //将a的值赋值给指针变量p,p为野指针,不会有问题,但没有意义
p= 0x12345678;//给指针变量p赋值,p为野指针,不会有问题,但没有意义
*p = 1000;//野指针指向未知区域,内存出问题,err
五.空指针(NULL指针)
5.1 什么是空指针?
我们前面提到过,NULL也就0x0000 0000,空指针就是指向NULL这个地址的指针。
例:int *p =NULL;
5.2 空指针的作用是什么?
我们前面提到过0x0000 0000是处在代码区的,那么这个空指针是不可操作的。那为什么我们要专门让指向NULL这个空间的指针定义为空指针呢?
这里我们可以结合NULL这个地址在运行过程中是不允许被使用的特性来思考:如果我们操作空指针,就是非法操作。而很多时候,比如完成一个初始化的代码块时,我们希望某些指针只被使用一次,那我们在使用时只需要判断指针是否为NULL就可以知道这个指针是否被使用过。
除此之外,空指针还可以有什么作用呢?空指针也可以标志为没有任何指向任何变量的指针,也就是说这个空指针没有指向任何变量,也就说,这个指针是空闲可用的。
总结下,程序员使用这个地址的主要作用是:
1.如果使用完指针后,便将指针赋值为NULL,在使用时只需要判断指针是否为NULL就可以知道是否被使用。
2.标志着这个指针没有指向任何变量,是空闲可用的。
六.万能指针
6.1 什么是万能指针?
所谓万能指针,就是可以保存任意类型变量的地址。我们编写代码时,就将(void *)类型的指针称为万能指针。
例:(void *) p=NULL;
6.2 为什么要使用万能指针?
我们前面提到了指针步长的概念,那么我们知道如int、float、char等基本类型的变量的地址只能存放在int*、float* 、char*类型的指针变量中,我们在操作时才能正常引用。
可是如果我们需要保存很多不同类型的或者不确定类型变量的地址怎么办?如果保存很多不同已知类型的变量我们尚且还可以分别定义对应不同类型变量的不同类型的指针变量。那如果需要让它保存任意类型的未知变量的地址怎么办?
我们前面提过,指针变量的大小都是一定的,那么,(void *)类型的指针变量就和其他基本类型的指针变量一样,大小是固定的,可以存放任意一个地址。而系统是也可以顺利申请已知内存大小的变量。
我们前面还提到过,不同类型的指针变量的步长是不一样的。但是void并不是一个基本类型,系统根本不知道void这个类型所占的空间大小,也就是说,(void *)类型的指针变量根本就没有步长。那么,如果我们需要取内容怎么办?很简单,在取内容的时候再强制转换一下就可以了。
这里我们总结一下:
我们可以用万能指针保存任意类型变量的地址。
但如果我们要取内容,需要强制转换为对应类型的指针。
6.3 使用万能指针的例子
int a =10;
void * p =(void *)&a;
printf("%d\n",*(int *)p);
结束语:
文章整理不容易,麻烦尊重知识尊重劳动,请勿抄袭剽窃。
如果本文章对你有帮助的话希望多多支持嘿
C中野指针、空指针、万能指针问题相关推荐
- 野指针 空指针 悬空指针 智能指针
C语言中"野指针"."悬空指针"是什么? C语言中"野指针"."悬空指针"是什么?_不脱发的程序猿的博客-CSDN博客_ ...
- 黑马程序员(57.指针-指针所占内存空间-58.指针-空指针-59.指针-野指针)
黑马程序员(57.指针-指针所占内存空间-58.指针-空指针-59.指针-野指针) 一,57.指针-指针所占内存空间 二,58.指针-空指针 三, 59.指针-野指针 来自黑马程序员 一,57.指针- ...
- c语言怎么让写的函数兼容int型和char型_既然C语言void指针是“万能指针”,那么malloc还需类型转换吗?...
在C语言程序开发中,动态内存分配是不可避免的,而调用 malloc()/free() 库函数实现这一过程是方便的,事实上,在很多C语言程序中,malloc/free 库函数的使用相当频繁,它俩的C语言 ...
- c语言 万能函数指针,C/C++编程笔记:C语言精华解析之void *指针,指针中的万能指针...
什么是万能指针? 万能指针其实就是void *类型的指针,而void *指针一般被称为通用指针或叫泛指针.它是C语言关于纯粹地址的一种约定.当某个指针是void型指针时,所指向的对象不属于任何类型. ...
- 关于空指针(指针指向为NULL)和void类型的指针的理解
正在学C,书上老说空指针,或者说void指针,对于我这样的生手来说,理解非常容易造成混淆,因为void这个单词的意思也是空,到底空指针的意思是指指向地址为空的类型呢,还是指void类型的指针呢 (1) ...
- 指针||指针和数组||指针和函数||指针、数组、函数 案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序
指针 指针的基本概念 指针的作用: 可以通过指针间接访问内存 内存编号是从0开始记录的,一般用十六进制数字表示 可以利用指针变量保存地址 指针变量的定义和使用 指针变量定义语法: 数据类型 * 变量名 ...
- 物联网学习入门:C/C++函数指针与特殊指针
函数指针:存放函数首地址的变量 函数与数组类似,函数名代表函数首地址,且存放函数地址的指针为函数指针. 如下: void prin() {printf("hello\n"); }i ...
- C++中野指针和空指针和无类型指针
C++中野指针和空指针和无类型指针https://blog.csdn.net/chenguolinblog/article/details/27054267 一. 野指针 所谓的野指针指的是一个指针变 ...
- c/c++教程 - 1.9 指针 空指针 野指针 const修饰指针 指针常量 常量指针 指针和数组 指针和函数
十一.指针 (1)指针的定义和使用 指针的作用:可以通过指针间接访问内存. 参考视频:https://www.bilibili.com/video/BV1et411b73Z?from=search&a ...
最新文章
- Perl新接触的小命令
- where嵌套select_Select子查询:Select Zoo
- VS2022+.NET6+C#10,.NET开发起飞
- 日常问题——初始化Hive仓库报错com.google.common.base.Preconditions.checkArgument
- 恋舞服务器维修,2144《梦幻恋舞》关闭充值及关服公告
- xp系统更改计算机名BIOS设置,XP系统如何设置Netbios名|XP系统设置Netbios名的方法...
- 解决U盘无法格式化的问题
- 京瓷1020怎么打印自检页_惠普打印机怎样打印测试页
- 计算机经常断开网络,当笔记本电脑经常自动断开网络连接时如何解决问题
- 计算机网络安全及故障谢辞,计算机网络安全初探.pdf
- android 闪屏动态界面,Android开发 关于避免切换主题时闪屏的几种方式
- 16张图详解计算机网络协议(万字)
- 8Manage:大宗商品采购,专注构建企业采购信息化!
- 遥控车钥匙算法之KeeLoq
- mysql被删库如何恢复_mysql整个数据库被删除了怎么恢复
- SLAM十四讲-(3)三维空间刚体运动
- 2021年中级Java面试题
- TCP/IP协议学习( 三 ) ---- ping原理 和 ICMP
- C Primer Plus 练习 P85
- 黑群晖二合一安装不了套件_【优选产品】Si1133/53光学传感器多功能评估套件