你的C/C++程序为什么无法运行?揭秘Segmentation fault (1)
什么让你对C/C++如此恐惧?
晦涩的语法?还是优秀IDE的欠缺?
我想那都不是问题,最多的可能是一个类似这样的错误:
段错误(Segmentation fault)
这是新手无法避免的错误,也是老手极力回避也经常遇到的错误。
本篇,试图简略地剖析一段会引发这个错误的程序,带来一些启发。
先看两份代码,一份是错误的.
错误代码
#include "string.h"
#include <stdlib.h>
#include <stdio.h>
void func1(char ** dest,char * src,int n) {
(*dest) = (char*)malloc(sizeof(char)*n);
strcpy(*dest,src);
}
int main(int argc,char** args) {
char ** p = NULL;
char str[] = "foreach_break";
int len = sizeof(str);
printf("%d\n",len);
func1(p,str,len);
printf("%s\n",*p);
free(p);
p = NULL;
}
正确代码
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
void func1(char ** dest,char * src,int n) {
(*dest) = (char*)malloc(sizeof(char)*n);
strcpy(*dest,src);
}
int main(int argc,char** args) {
char * p = NULL;
char str[] = "foreach_break";
int len = sizeof(str);
printf("%d\n",len);
func1(&p,str,len);
printf("%s\n",p);
free(p);
//p = NULL;
}
代码意图来自技术问答中的一个huffman树不能运行的问题。
当然,我剥离掉了大部分关于huffman的部分,并稍加改动。
它们最大的不同:
错误代码:
char ** p = NULL;
func1(p,str,len);
正确代码:
char * p = NULL;
func1(&p,str,len);
也许你会奇怪,你看到“正确”的代码中居然注释了这行:
//p = NULL;
参数传递
同时,可能有人会觉得指针char ** p
向函数func1传递*p
,与指针char * p
向函数func1
传递&p
没什么不同啊?
嗯。这种想法也很有惯性,因为C语言没有类似这样的函数声明:
void func(int &)
同时,对char * p
作&p
操作不也得到个char **
吗?
运行程序
那么,我们看看程序自己怎么说?
如果你经常遇到段错误,希望你仔细看明白上面的图在说什么。
野指针
所谓野指针,就是很野的指针,你不知道它指向了哪个地址,也不知道对这个地址取值是否会出错,但,野指针也是指针,有一个存放它的内存地址.
正确的代码中,存放指针
p
的地址是0x7fffffffddc0
;
错误的代码中,存放指针p
的地址是0x7fffffffdd78
;
零指针
所谓零指针,就是指向了0x0的指针.对这个0x0取值是否会出错呢?你想一想.
悬浮指针
你对于在正确的程序中注释了p = NULL
而感到不解?
其实这没什么,取决于这个指针p
在后续代码中怎么使用.
free(p);
这句代码的执行,会释放掉指针p
所指向的内存地址,归还给操作系统.
当然前提是这个地址确有所指、你也有权访问它.
p = NULL;
这句代码的执行,是让指针p
指向了0x0
,变回了空指针.
虽然它指向的内存已经被释放,但是它还指向那个地址.
这就是悬浮指针,指向的地址已经不可用确还指向,就不是确有所指.
由于我们的main
函数即将执行完毕,所以在它返回后,存放空指针p
的地址会被释放.
因为指针
p
是main
函数的一个临时变量.
所以我们可以毫无顾虑的注释掉p = NULL
.
内存泄露
另外,如果free(p)
没有被执行,而先执行了p = NULL
,那么p
原来指向的内存空间可能就无法被正确释放,如果再也没有其它的引用指向了那块地址,那块地址就被遗忘在那里,同时不能被回收.
这个,叫内存泄露 (Memory Leak).
接着看程序
现在,我们来看看函数func1
的调用。
首先,是C代码:
然后是两段代码的对比:
你应该已经看出了差别。
错误的代码的dest
参数传入了0x0
.
接着是执行:
(*dest) = (char*)malloc(sizeof(char)*n);
这句代码在进行(*dest)
时就会发生段错误.
究其原因,就在于char ** p = NULL
让p
变成了零指针,*p
相当于对0x0
这个地址取值.
应用程序启动时,操作系统会建立一个进程(process),这个进程拥有自己独立的地址空间,称作虚拟地址空间(virtual memory space).
0x0
在这个空间中,不能被访问.
试图访问一个不能被访问的空间,就会段错误.
总结
段错误的一种,我们探索完毕.
现在你知道以下两种操作的含义了吗?
/* p指向的地址,对其取值,如果这个地址有东西,也有权取,没问题.*/
char ** p -> *p; (1)
/* 存放p的地址,或者指向p的引用,这个地址必然有东西,所以没问题*/
char * p -> &p (2);
本篇结束.
你的C/C++程序为什么无法运行?揭秘Segmentation fault (1)的更多相关文章
- 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2)
什么让你对C/C++如此恐惧? 本篇将继续上一篇来讨论段错误(Segmentation fault). 上一篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault(1) 追溯 ...
- 你的java/c/c++程序崩溃了?揭秘段错误(Segmentation fault)(3)
前言 接上两篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault (1) 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2) 写到这里,越跟 ...
- C#如何防止程序多次运行的技巧
一.使用互斥量Mutex弄懂了主要的实现思路之后,下面看代码实现就完全不是问题了,使用互斥量的实现就是第四点的思路的体现,我们用为该程序进程创建一个互斥量Mutex对象变量,当运行该程序时,该程序进程 ...
- iOS开发小技巧--iOS程序进入后台运行的实现
iOS程序进入后台运行的实现 视频中看到老师用的iOS7,代码中有开启timer,无限请求数据的功能,但是切换到后台,代码就不打印了 自己用的iOS9,进入后台还是可以打印的,再次进入前台也可以正常运 ...
- 配置ASP.NET Web应用程序, 使之运行在medium trust
这文章会向你展示, 怎么配置ASP.NET Web应用程序, 使之运行在medium trust. 如果你的服务器有多个应用程序, 你可以使用code access security和medium ...
- WPF 设置程序开机自动运行(+注册表项)
#region 设置程序开机自动运行(+注册表项) RegistryKey rgkRun = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Micr ...
- 【转】delphi程序只允许运行一个实例的三种方法:
一. 创建互斥对象 在工程project1.dpr中创建互斥对象 Program project1 Uses Windows,Form, FrmMain in 'FrmMain.pas' ...
- inno安装卸载时检测程序是否正在运行卸载完成后自动打开网页-代码无效
inno安装卸载时检测程序是否正在运行卸载完成后自动打开网页-代码无效 inno setup 安装卸载时检测程序是佛正在运行卸载完成后自动打开网页-代码无效 --------------------- ...
- .NET概念:.NET程序编译和运行
.NET概念:.NET程序编译和运行 分类: c#程序设计 2012-02-29 15:46 3001人阅读 评论(2) 收藏 举报 .net编译器语言microsoftassemblyvb.net ...
随机推荐
- 01.Web基础和HTML初始
1.1 上网就是请求数据 我们先不直接解决这个问题,我们做一个小实验.我们每个人的电脑里面,都有一个神秘的文件夹: C:\Users\Weiheng\AppData\Local\Microsoft\W ...
- MySQL触发器Trigger实例篇
定义: 何为MySQL触发器? 在MySQL Server里面也就是对某一个表的一定的操作,触发某种条件(Insert,Update,Delete 等),从而自动执行的一段程序.从这种意义上讲触发器是 ...
- IE源代码摘抄,基于泄漏的IE5.0(持续更新)
下载了一份很久以前泄漏的IE5.0的源代码,虽然已经是很古远的版本了.但是通过调试现有版本浏览器与查看源代码,发现关键部分的差距并不是很大,代码很有参考意义.这里把重要的函数.数据结构摘抄出来以备参考 ...
- 一步一步学习IdentityServer3 (9)
idr添加验证码,授权方法中获取不到session,而且没有login页面的post方法,只有一个视图,而且是先加载视图,生成不了验证码 我的解决方法是将验证写一个自定义mvc控件 利用 viewd ...
- 按书上学写测试pytest
慢慢的,这块知识也补好吧. 系统的学习框架,具体的细节,可以边百度边实现. test_three.py '''Test the Task data type.''' from collections ...
- #JS Regex正则表达式的使用
字符串带的正则表达式相关的方法 1.search() 搜索符合指定正则表达式在目标字符串中的位置 str.search('hello'); //str字符串中查找search字符串,成功返回位置,否则 ...
- mongo oplog 整理
首先需要介绍一下mongodb local库的作用 local库是MongoDB的系统库,记录着时间戳和索引和复制集等信息 test:PRIMARY> use local switched to ...
- day1作业二:多级菜单操作(函数实现)
作业二:多级菜单 (1)三级菜单 (2)可以次选择进入各子菜单 (3)所需新知识点:列表.字典 要求:输入back返回上一层,输入quit退出整个程序 本示例的三级菜单是一个yaml文件格式,格式如下 ...
- day1作业:登录接口
作业一:编写登陆接口 1.输入用户名和密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 思路: (1)用户输入用户名: (2)去锁定文件中验证用户名是否锁定: (3)去当前用户验证用户是否存在: ...
- Ubuntu16.04下Hive的安装与配置
一.系统环境 os : Ubuntu 16.04 LTS 64bit jdk : 1.8.0_161 hadoop : 2.6.4mysql : 5.7.21 hive : 2.1.0 在配置hive ...