由作业题目对malloc函数的思考
问题描述
算法题:给定一个字符串,逐个翻转字符串中的每个单词,例如输入是"I am a student in Renmin University of China”,输出"China of University Renmin in Student a am I".(用栈实现)
算法思想:运用俩个栈实现一个特殊队列,遇到空格则将栈1的元素入到栈2里。
先看代码
#include
#include
#include
using namespace std;
#define OVERFLOW -2
#define OK 1
#define NO 0
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef int ElemType;
typedef struct SqStack
{char *base;char *top;int stacksize;
} SqStack;
void Init_SqStack(SqStack &S)
{S.base = (char *)malloc(sizeof(char));if (!S.base)exit(OVERFLOW); //分配失败S.top = S.base;S.stacksize = STACK_INIT_SIZE;return;
}
void push(SqStack &S, char e)
{if (S.top - S.base >= S.stacksize) //栈满追加空间{S.base = (char *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(char));if (!S.base)exit(OVERFLOW);S.top = S.base + S.stacksize;S.stacksize += STACKINCREMENT;}*S.top++ = e;return;
}
void Pop(SqStack &s, char &e)
{if (s.base == s.top){cout<<'error'<=1;i--){char a=*(p+i-1);cout<
问题出现
这是正常运行的结果

这是 调试时候的输出结果

可以发现俩种途径得到输出不一样(人傻了)
后面重新改了一下reverse函数中的一部分发现运行结果和调试结果都是正确的,开始四处询问????????????????????
int isblank=0;//判断是否遇到空格while(s[i]!='\000'){ if (s[i] != ' '){push(tmp, s[i]);//遇到字母入栈i++;}else if (s[i] == ' ')//遇到空格表明一个单词已经入栈完{ isblank=1;push(tmp, ' ');i++;}if(isblank==1)//遇到空格将单词出栈进入结果栈内{spread(tmp,result);isblank=0;}}push(tmp,' ');spread(tmp,result);//处理最后一个单词
然后关于错误代码,尝试输出每次spread时候俩个栈的状态

以及找了一些例子测试
发现不管单词长短,凡是到了32位就发生了内存问题。
问题解决
百思不得其解,后经过老师点拨发现问题。

我们回到初始化代码,貌似也没有问题。
仔细想了一下,俩次malloc(1)可能会引起内存问题。

我们发现c语言中俩次malloc(1)地址竟然相差32,这实际上是不同编译器/标准libriaries之间的实现细节。通常,无法保证随后的malloc调用将返回相邻的内存区域。另请注意,每个内存区域都与其他元数据相关联,包括但不限于区域大小(否则,当运行free时,无法知道要释放多少内存)。此元数据通常放在已分配的区域内,使其大于请求的大小。还有一些其他因素影响实际分配的内存量 - 内存对齐和malloc实现使用的“漏洞”查找算法等。详见:jkt's blog: Tagged pointers, and saving memory in Trojita
解决方法:
1.通过在sizeof前乘以一个大于32的整数(一般选用50或者100),问题得到解决。
2.直接使用new来初始化空间。
debug和直接运行出现差别的分析
一.参考DEBUG模式下, 内存中的变量地址分析 - findumars - 博客园
试验结论 - <
- 如果定义多个变量
- DEBUG模式下, 内存中的变量地址和定义的顺序相同
- 变量开始地址都是模式地址
- 先定义的变量在内存高地址
- 后定义的变量在内存低地址
- Release模式下, 经过优化, 变量的内存地址和变量定义的顺序不相同!
- 如果变量有越界访问的情况, Release模式下的越界访问情况未知.
- 好像有一些规律, 依赖于编译器的优化选项
- 依赖变量越界访问达成变量的存取, 在Release模式下会和Debug模式下的运行效果不相同.
- 运行结果肯定不对了. 是否报错,要看运气了. 如果不报错, 发布后会死的很惨.
所以,之前 修改reverse函数后Debug和Release最后结果一致可能只是运气问题,不一定真的正确。
二.参考使用malloc遇到的奇怪问题——调试的时候正确,运行的时候结果就不对了 - Ash_boy - 博客园
稍微仔细一点的同学都能发现p = (struct A*)malloc(sizeof(struct A*));是不是看起来有点别扭?解释解释?首先malloc函数申请一块sizeof(struct A*)这么大的内存,然后将返回类型强制转换为struct A型的指针赋值给p。那么sizeof(struct A*)是多大呢?熟悉指针的人都知道一般是4个字节,实在不知道的话,进入调试状态在watch窗口输入sizeof(p),因为p为struct A*,其大小即struct A*大小。
问题已经非常明显了,我们要动态开辟一个struct A型的p,那么开创的空间肯定要和struct A一样大的,4字节够了吗?原来是多了一个*。更正以后运行,正确无误了。
问题是比较简单的,也是非常容易解决的。
但是,如果你忘记了free(不会报任何错误),而且你有将动态分配的p进行了很多其他的操作的时候,或者是在其他地方也动态开辟了内存的话。那么你会发现怎么这些数据都非常的奇怪,居然是凭空产生的,非常难以理解。明明是整个的思路,逻辑,语法都没错却有这样奇怪的结果。再回到前面的问题,我们只给p分配了4个字节,那么他是怎么访问结构体里面的100个int型的数据呢?这就不的不提起数组越界了。C是不对越界进行检查的,所以p中就如上面所提到的正确访问,正确赋值,正确输出。但是它访问的并不是它所拥有的空间,也就是其他变量的,如果其他变量进行其他操作的话,它的值也就变了。至于怎么变就不知道了。
debug模式的malloc
参考debug模式的malloc_馋嘴猪没胃口-CSDN博客
我们每次申请一块内存,都会有额外的内存被分配,所以使用连续使用malloc可能会导致内存溢出。

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
