什么让你对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的地址会被释放.

因为指针pmain函数的一个临时变量.

所以我们可以毫无顾虑的注释掉p = NULL.

内存泄露

另外,如果free(p)没有被执行,而先执行了p = NULL,那么p原来指向的内存空间可能就无法被正确释放,如果再也没有其它的引用指向了那块地址,那块地址就被遗忘在那里,同时不能被回收.

这个,叫内存泄露 (Memory Leak).

接着看程序

现在,我们来看看函数func1的调用。

首先,是C代码:

然后是两段代码的对比:

你应该已经看出了差别。

错误的代码的dest参数传入了0x0.

接着是执行:

(*dest) = (char*)malloc(sizeof(char)*n);

这句代码在进行(*dest)时就会发生段错误.

究其原因,就在于char ** p = NULLp变成了零指针,*p相当于对0x0这个地址取值.

应用程序启动时,操作系统会建立一个进程(process),这个进程拥有自己独立的地址空间,称作虚拟地址空间(virtual memory space).

0x0在这个空间中,不能被访问.

试图访问一个不能被访问的空间,就会段错误.

总结

段错误的一种,我们探索完毕.

现在你知道以下两种操作的含义了吗?

/* p指向的地址,对其取值,如果这个地址有东西,也有权取,没问题.*/
char ** p -> *p; (1)
/* 存放p的地址,或者指向p的引用,这个地址必然有东西,所以没问题*/
char * p -> &p (2);

本篇结束.

你的C/C++程序为什么无法运行?揭秘Segmentation fault (1)的更多相关文章

  1. 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2)

    什么让你对C/C++如此恐惧? 本篇将继续上一篇来讨论段错误(Segmentation fault). 上一篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault(1) 追溯 ...

  2. 你的java/c/c++程序崩溃了?揭秘段错误(Segmentation fault)(3)

    前言 接上两篇: 你的C/C++程序为什么无法运行?揭秘Segmentation fault (1) 你的C/C++程序为什么无法运行?揭秘Segmentation fault (2) 写到这里,越跟 ...

  3. C#如何防止程序多次运行的技巧

    一.使用互斥量Mutex弄懂了主要的实现思路之后,下面看代码实现就完全不是问题了,使用互斥量的实现就是第四点的思路的体现,我们用为该程序进程创建一个互斥量Mutex对象变量,当运行该程序时,该程序进程 ...

  4. iOS开发小技巧--iOS程序进入后台运行的实现

    iOS程序进入后台运行的实现 视频中看到老师用的iOS7,代码中有开启timer,无限请求数据的功能,但是切换到后台,代码就不打印了 自己用的iOS9,进入后台还是可以打印的,再次进入前台也可以正常运 ...

  5. 配置ASP.NET Web应用程序, 使之运行在medium trust

    这文章会向你展示, 怎么配置ASP.NET Web应用程序, 使之运行在medium trust.   如果你的服务器有多个应用程序, 你可以使用code access security和medium ...

  6. WPF 设置程序开机自动运行(+注册表项)

    #region 设置程序开机自动运行(+注册表项) RegistryKey rgkRun = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Micr ...

  7. 【转】delphi程序只允许运行一个实例的三种方法:

    一.        创建互斥对象 在工程project1.dpr中创建互斥对象 Program project1 Uses Windows,Form, FrmMain in 'FrmMain.pas' ...

  8. inno安装卸载时检测程序是否正在运行卸载完成后自动打开网页-代码无效

    inno安装卸载时检测程序是否正在运行卸载完成后自动打开网页-代码无效 inno setup 安装卸载时检测程序是佛正在运行卸载完成后自动打开网页-代码无效 --------------------- ...

  9. .NET概念:.NET程序编译和运行

    .NET概念:.NET程序编译和运行 分类: c#程序设计 2012-02-29 15:46 3001人阅读 评论(2) 收藏 举报 .net编译器语言microsoftassemblyvb.net ...

随机推荐

  1. Git如何设置多个用户

    前言 由于我们在使用GitHub时,通常不希望带有公司信息,所以需要独立的Git账户来提交练习代码,本文记录一下如何配置多个Git账户并创建公钥 正文 1.首先进入~/.ssh文件夹 2.然后创建一个 ...

  2. HTML5 localStorage、sessionStorage 作用域

    一.localStorage localStorage有效期:永不失效,除非web应用主动删除. localStorage作用域:localStorage的作用域是限定在文档源级别的.文档源通过协议. ...

  3. 表格中border-collapse属性

    页面制作中最头痛的,表格的边框算是其一了.一不小心就会出现双重线 border-collapse属性 很好的解决了纠结了很久的问题 .table{border: 1px solid #ccc;bord ...

  4. 经典面试题:js继承方式上

    js不是传统的面向对象语言,那么他是怎么实现继承的呢?由于js是基于原型链实现的面向对象,所以js主要通过原型链查找来实现继承,主要有两大类实现方式,分为基于构造函数的继承,以及非构造函数的继承. 由 ...

  5. 洛谷P1286 两数之和

    这个题.. 刚开始没看见输入若干行,所以有的点就.. 令 m = n * (n - 1) / 2 已知 s = {s (1), s(2), ..., s(m)}, s(i) <= s(i+1) ...

  6. (五)动态SQL

    第一节:if条件 第二节:choose,when和otherwise条件 第三节:where条件 1.自动加上where: 2.如果where子句以and或者or开头,则自动删除第一个and或者or: ...

  7. SSIS 学习之旅 FTP文件传输-脚本任务

    这一章主要讲解一下用脚本怎么把CSV文件抛送到FTP服务器上 设计:   通过Demon库的Users表数据生成CSV文件.   生成后的CSV文件抛送到FTP指定目录下. 控件的使用这里就不做详细讲 ...

  8. OneNote无法同时设置中英文字体设置解决办法

    如果你是一位OneNote老用户,无论是2003.2007还是2010或者最新的2013版本,都一直存在一个Bug,就是无法同时设置中英文字体(比如在Word中就可以分别设置不同的).我搜了一下,在微 ...

  9. 配置tomcat报错: Unknown version of Tomcat was specified.

    报错原因:路劲没选择对,应选择bin文件夹的上一层目录,也不能选择bin目录

  10. hdoj2955 Robberies(01背包)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2955 题意 有n家银行,每家银行有两个属性:钱数m,概率p,p表示抢这家银行被逮着的概率.有一个人想抢 ...