晦涩的语法?还是优秀IDE的欠缺?
我想那都不是问题,最多的多是1个类似这样的毛病:
这是新手没法避免的毛病,也是老手极力躲避也常常遇到的毛病。
本篇,试图简略地剖析1段会引发这个毛病的程序,带来1些启发。
先看两份代码,1份是毛病的.
#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
",len);
func1(p,str,len);
printf("%s
",*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
",len);
func1(&p,str,len);
printf("%s
",p);
free(p);
//p = NULL;
}
代码意图来自技术问答中的1个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 **
吗?
那末,我们看看程序自己怎样说?
如果你常常遇到段毛病,希望你仔细看明白上面的图在说甚么。
所谓野指针,就是很野的指针,你不知道它指向了哪一个地址,也不知道对这个地址取值是不是会出错,但,野指针也是指针,有1个寄存它的内存地址.
正确的代码中,寄存指针
p
的地址是0x7fffffffddc0
;
毛病的代码中,寄存指针p
的地址是0x7fffffffdd78
;
所谓零指针,就是指向了0x0的指针.对这个0x0取值是不是会出错呢?你想想.
你对在正确的程序中注释了p = NULL
而感到不解?
其实这没甚么,取决于这个指针p
在后续代码中怎样使用.
free(p);
这句代码的履行,会释放掉指针p
所指向的内存地址,归还给操作系统.
固然条件是这个地址确有所指、你也有权访问它.
p = NULL;
这句代码的履行,是让指针p
指向了0x0
,变回了空指针.
虽然它指向的内存已被释放,但是它还指向那个地址.
这就是悬浮指针,指向的地址已不可用确还指向,就不是确有所指.
由于我们的main
函数行将履行终了,所以在它返回后,寄存空指针p
的地址会被释放.
由于指针
p
是main
函数的1个临时变量.
所以我们可以毫无顾虑的注释掉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
这个地址取值.
利用程序启动时,操作系统会建立1个进程(process),这个进程具有自己独立的地址空间,称作虚拟地址空间(virtual memory space).
0x0
在这个空间中,不能被访问.
试图访问1个不能被访问的空间,就会段毛病.
段毛病的1种,我们探索终了.
现在你知道以下两种操作的含义了吗?
/* p指向的地址,对其取值,如果这个地址有东西,也有权取,没问题.*/
char ** p -> *p; (1)
/* 寄存p的地址,或指向p的援用,这个地址必定有东西,所以没问题*/
char * p -> &p (2);
本篇结束.