您当前的位置:首页 > 养生 > 内容

c语言内存错误的原因及解决办法是什么(C语言内存错误的原因及解决办法)

c语言内存错误的原因及解决办法是什么(C语言内存错误的原因及解决办法)

一、指针未指向合法内存。

定义了指针变量,但是没有为指针分配内存,也就是说,指针没有指向合法的内存。我赢了我不会给你显而易见的例子,但这里有一些更微妙的例子。

1、结构成员指针未初始化

结构学生

{

char * name

int分数;

}stu、* pstu

intmain()

{

strcpy(stu.name,“欧兢兢”);

stu.score=99

返回0;

}

许多初学者犯了这样的错误,但仍然不明白。我不知道这是什么。这里定义了结构变量stu,但他没有没想到这个结构里面的成员char *name在定义结构变量stu的时候只给指针变量名本身分配了4个字节。名字指针并没有指向一个合法的地址,此时里面存储的只是一些乱码。因此,当调用strcpy函数时,字符串欧兢兢会被复制到乱码指向的内存,而这个内存名指针根本没有访问权,导致错误。解决方案是为名称指针malloc提供一个空间。

同样,有些人犯了以下错误:

intmain()

{

pstu=(结构学生*)malloc(sizeof(结构学生));

strcpy(pstu-》名字,“欧兢兢”);

pstu-》评分=99;

免费(pstu);

返回0;

}

为指针变量pstu分配了内存,但没有为名称指针分配内存。错误和上面第一种情况一样,解决方法也一样。这里使用了一个malloc,给人一种内存也被分配给名字指针的错觉。

2、没有足够的内存分配给结构指针。

intmain()

{

pstu=(struct student *)malloc(size of(struct student *));

strcpy(pstu-》名字,“欧兢兢”);

pstu-》评分=99;

免费(pstu);

返回0;

}

为pstu分配内存时,分配的内存大小不合适。这里sizeof(struct student)错写成sizeof(struct student*)。当然,名字指针也是不分配内存的。解决方法同上。

3、功能输入检查

每当我们使用指针时,我们必须确保它是有效的。

使用assert(NULL!=p)检查参数。使用if(NULL!=p)来检查。但是有一个要求,p在定义的同时初始化为NULL。例如,在上面的例子中,如果(NULL!=p)支票没有也不行,因为名字指针没有初始化为null,其内部是非NULL的乱码。

Assert是一个宏,不是函数,包含在assert.h头文件中。如果它后面括号中的值为false,程序将停止运行并提示错误;如果括号中的值为真,继续运行下面的代码。这个宏只在调试版本上起作用,但是在发布版本中被编译器完全优化了,所以不会影响代码的性能。

可能有人会问,既然发布版是完全由编译器优化的,那么发布版是不是根本没有这个参数入口检查呢?在这种情况下,isn 这和不用它不一样吗?

是的,使用assert宏的地方在发布版本中没有这些检查。但是,要知道assert宏只是用来帮助我们调试代码的。它所有的功能都是让我们在调试功能的时候尽量消除错误,而不是等到发布之后。它本身没有调试功能。还有一点就是参数错误不是函数问题,而是调用者传递的实参。Assert宏可以帮助我们定位错误,而不是消除错误。

二、内存分配成功,但未初始化。

经常犯这个错误是因为没有初始化的概念或者是因为内存的值在分配后自然是0。未初始化的指针变量可能看起来不那么严重,但它确实是一个非常严重的问题,而且往往很难找到这个错误的原因。

曾经有个学生写windows程序的时候想调用字体库里的一个字体。而调用这个字体需要填充一个结构。他很自然的定义了一个结构变量,然后把他想要的字体代码赋给相关变量。然而,问题出现了。无论怎么调试,他需要的字体效果永远也不会出来。我查了他的代码,没发现什么问题,就一步一步调试。在观察这个结构变量的记忆时,发现有几个成员的值是乱码。它这只是一个乱码!因为系统会根据这个结构中某些特定成员的值在字体库中寻找匹配的字体,当这些值与字体库中某个字体的某些项匹配时,就会调用这个字体。但遗憾的是,正是因为这些乱码,找不到匹配的字体!因为系统可以无法区分哪些数据是乱码,哪些数据是有效的。只要有数据,系统就想当然地认为是有效的。

或许这种严重的问题并不多见,但绝不能掉以轻心。所以定义变量的时候,第一件事就是初始化。您可以将其初始化为有效值,例如:

int i=10

char * p=(char *)malloc(sizeof(char));

但是往往这个时候我们并不确定这个变量的初始值,所以可以初始化为0或者NULL。

int I=0;

char * p=NULL

如果定义一个数组,可以像这样初始化它:

int a[10]={ 0 };

或者使用memset函数初始化为0:

memset(a,0,sizeof(a));

Memset函数有三个参数,第一个是要设置的内存起始地址;第二个参数是要设置的值;第三个参数是要设置的内存大小,单位是字节。我不这里不想过多讨论memset函数的用法。如果想了解更多,请查阅相关资料。

至于指针变量,如果没有初始化,if语句或assert宏检查就会失败。这个上面已经分析过了。

三、分配给指针的内存太小。

为指针分配了内存,但内存大小不够,导致越界错误。

char * p1=" abcdefg

char * p2=(char *)malloc(sizeof(char)* strlen(P1));

strcpy(p2,P1);

P1是一个长度为7个字符的字符串常量,但它的内存大小是8个字节。初学者经常忘记结束标志\ 0 字符串常量。这将导致最后一个空字符\ 0 p1中的字符串没有被复制到p2。解决方案是添加以下字符串结束标识符:

char * p2=(char *)malloc(sizeof(char)* strlen(P1)1 * sizeof(char));

这里应该注意,只有字符串常量有结束标识符。例如,在下面的书写中没有结束标记:

char a[7]={'a '' b '' c '' d '' e '' f '' g ' };

此外,唐不要因为char类型大小是1个字节就忽略sizof(char)的写入。这只会降低代码的可移植性。

四、内存超出界限

内存分配成功并已初始化,但操作越过了内存边界。这个错误通常是由1 more 或者少当操作数组或指针时。例如:

int a[10]={ 0 };

for(I=0;我“=10;我)

{

a[I]=I;

}

所以for循环的循环变量必须使用半开半闭区间,如果不是特殊情况,循环变量尽量从0开始。

如果内存越界,越界代码中可能没有内存错误。在下一次或者几次使用这个指针的时候可能会出现错误,甚至在下一次或者几次申请内存的时候会出现段错误。

示例:

这是我检查过的内存段错误bug。代码类似于以下内容:

int set_value(char *psz_dst,char * PSZ _ src){ int I _ len=0;如果(!psz_src)返回0;I _ len=strlen(PSZ _ src);PSZ _ dst=(char *)malloc(I _ len 1);返回0;}

malloc中有一个段错误。检查参数i_len是63,没问题。

以为系统内存耗尽,free发现系统还剩几百M内存。

不得不牺牲广发的方案。调试时,运行第7行之前,pstren (PSZ _ src),输出62;

然后到第八行,p strlen(psz_src),结果是63。奇怪!

我只能怀疑psz_src有问题。

我找到了psz_src申请内存的地方。果然malloc丢了61个字节,然后strcpy丢了63个字节。内存超出界限。

五、内存泄漏

内存泄露几乎是不可避免的,无论是老手还是新手都存在。即使是windows、Linux等软件,也或多或少存在内存泄漏。也许对于一般的应用软件来说,这个问题并不不会显得那么突出,重启也不会不要造成太大的损失。但是如果你是开发嵌入式系统软件呢?比如汽车制动系统、心脏起搏器等对安全性要求非常高的系统。你可以不要让起搏器重新启动。严师傅很好客。

会泄漏的内存是堆上的内存(这里不讨论资源或句柄的泄漏),也就是malloc系列函数或new运算符分配的内存。如果你不使用后不要及时释放或删除,这种记忆会直到整个程序被终止。

1、退休归乡沃土

如何理解这个内存分配和释放的过程?先看下面这段对话:

主万岁:艾青,你为我立下了大功。你想要什么报酬?

一个英雄:万岁,金银,我视之如粪土。我老了,想退休回老家。我乞求千亩良田,为后人遮荫,别无所求。

主万岁:艾青,你辛苦了,却只想要这么点小小的报酬。今天我会按你的愿望去做。刘侍郎,查一下湖广一带还有没有几千亩良田。

刘部长助理:长沙还有5万多亩一级良田没有得到奖励。

主万岁:在长沙拨一千亩良田给艾青。爱情,千亩良田,你想要什么?

英雄:谢万岁。长沙地区适合种植水稻。我想用它来种水稻。种植水稻需要将田地分成一英亩,以便于耕种。

2、如何使用malloc函数

唐不要莫名其妙,其实上面的小对话就是使用malloc的过程。Malloc是一个专用于从堆中分配内存的函数。使用malloc函数有几个要求:

内存分配给谁了?这是分配给英雄的肥沃土地。

分配了多少内存?这里是1000亩的分配。

内存分配足够吗?是的,这里有足够肥沃的土地可供分配。

内存会用来存储什么格式的数据,也就是说,内存会用来做什么?

这里用来种水稻,田地需要分亩。分配的内存在哪里?这是在长沙。

如果确认了这五点,那么就可以分配内存了。让让我们先看看malloc函数的原型:

(void *)malloc(int size)

malloc函数的返回值是void类型的指针,参数是int类型的数据,即申请分配的内存大小,单位是字节。内存分配成功后,malloc函数返回该内存的第一个地址。你需要一个指针来接收这个地址。但是由于函数的返回值是void *类型,所以必须将其强制转换为您收到的类型。也就是这个内存会用来存储什么样的数据。例如:

char * p=(char *)malloc(100);

在堆上分配了100字节的内存,并返回该内存的第一个地址。该地址被强制转换为char *类型,并赋给char *类型的指针变量P。同时告诉我们这个内存会用来存储char数据。也就是说你只能通过指针变量p来操作这个内存,这个内存本身是没有名字的,对它的访问是匿名的。

以上是使用malloc函数成功分配一块内存的过程。但是,你能每次都分发成功吗?

不一定。在上面的对话中,皇帝让户部侍郎询问是否有足够的良田没有被分配。使用malloc函数的时候也要注意这一点:如果请求的内存块大于当前堆上剩余的内存块(整个块),内存分配就会失败,函数会返回NULL。请注意堆上剩余的内存块这里提到的并不是所有剩余内存块的总和,因为malloc函数适用于一个连续的mem块

由于malloc函数有内存申请不成功的可能,所以必须使用if(NULL!=p)语句来验证内存分配确实是成功的。

3、使用malloc功能申请0字节内存

另一个问题:用malloc函数申请0字节内存会返回空指针吗?

可以测试一下,也可以找关于malloc函数的文档。申请0字节内存,函数不返回NULL,而是一个正常的内存地址。但是你可以不要使用大小为0的内存。好的尺子上一定的刻度,刻度本身没有长度,只有两个刻度放在一起才能测出长度。这个要小心,因为这个时候如果(NULL!=p)语句检查不起作用。

4、内存释放

既然有分配,就要有释放。否则,有限的内存总是会被用完,而未释放的内存则会闲置。与malloc对应的是free函数。free函数只有一个参数,就是要释放的内存块的第一个地址。例如,上面的例子:

免费(p);

free函数看起来很难,但是它是做什么的呢?其实它做了一件事:切断了指针变量和这个内存的关系。比如上面的例子,我们可以说malloc函数分配的内存块是属于P的,因为我们都需要通过P来访问这个内存,free函数就是切断这个内存和P的所有关系,从此P和那个内存就没有任何关系了。至于指针变量P本身保存的地址,并没有改变,只是对这个地址的内存没有所有权。存储在释放的内存中的值没有改变,但不能再使用。

这就是自由函数的作用。根据上面的分析,如果连续两次以上对p使用free函数,肯定会出错。因为第一次使用free函数时p所属的内存已经被释放,第二次使用时就没有内存可以释放了。对此,我在课堂上让学生记住的是,一定要一夫一妻制,不然肯定会出事。

Malloc将只泄漏一次内存,免费两次;Malloc肯定会免费犯错一次两次。也就是说,malloc在程序中的使用次数必须和free一样多,否则就会出错。这种错误主要发生在malloc函数被回收的时候,malloc和free times经常被弄错。这里这是一个练习:

写两个函数,一个生成链表,一个释放链表。两个函数的参数只使用一个头指针。

5、释放内存后

由于指针变量p本身保存的地址在使用free函数后没有变化,我们需要再次将p的值改为NULL:

p=NULL

这就是我们所说的系上无效狗的锁链之前。如果你不别纠结了,迟早会出事的。例如:

在free(p)之后,使用if(NULL!=p)这样的check语句还能用吗?例如:

char * p=(char *)malloc(100);

strcpy(p,“你好”);

免费(p);

if (NULL!=p)

{

strcpy(p,“世界”);

}

在内存块被释放后,指针不被设置为空,这个指针变成一个野生指针。也有书叫悬挂指针。这是很危险的,也是经常出错的地方。所以一定要记住一点:free之后,一定要把指针设置为NULL。

同时留下一个疑问:反复释放空指针会是错误吗?为什么?如果让你设计free函数,你会怎么处理这个问题?

六、内存已被释放,但仍通过指针继续使用。

这里一般有三种情况:

第一种:如上所述,free(p)后,继续通过p指针访问内存。解决的办法就是空p。

第二种:函数返回堆栈内存。这是新手最容易犯的错误。例如,数组是在函数内部定义的,但return语句用于返回指向数组的指针。解决方案是理解栈上变量的生命周期。

第三种:内存的使用太复杂,而且它很难知道哪个内存被释放了,哪个没有。解决办法是重新设计程序,改善对象间的调用关系。

上面详细讨论了六种常见错误及其解决方案。希望读者认真研究,尽量让自己熟悉每一个错误的产生原因和预防措施。一定要多练习,多调试代码,多总结经验。

总结:

语言记忆错误的常见原因:

1、没有为指针分配内存;

2、在使用指针之前,不会验证指针的有效性。

3、分配给指针的内存太小,导致内存越界;

4、给指针分配内存后,内存没有初始化;

5、无法释放请求的内存,导致内存泄漏。

标签:内存指针函数


声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,谢谢。

上一篇: AMDVega56深度评测(与1070非公默认极限大致相当)

下一篇: nipple(nipples什么意思)



推荐阅读

网站内容来自网络,如有侵权请联系我们,立即删除! | 软文发布 | 粤ICP备2021106084号