由作业题目对malloc函数的思考

问题描述

算法题:给定一个字符串,逐个翻转字符串中的每个单词,例如输入是"I am a student in Renmin University of China”,输出"China of University Renmin in Student a am I".(用栈实现)

算法思想:运用俩个栈实现一个特殊队列,遇到空格则将栈1的元素入到栈2里。  

先看代码

#include <malloc.h>
#include <stdlib.h>
#include <iostream>
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'<<endl;
        return ;
    }
    e = *--s.top;
    return ;
}
void showstack(SqStack s)
{
    char *p=s.base;
    int len=s.top-s.base;
    for(int i=len;i>=1;i--)
    {
        char a=*(p+i-1);
        cout<<a;
    }
    cout<<endl;
}
int isempty(SqStack s)
{
    int a=0;
    if(s.base==s.top)
    {
        a=1;
    }
    return a;
}
void spread(SqStack &s1, SqStack &s2)
{
    char e;
    while(!isempty(s1))
    {
        
         Pop(s1, e);
        push(s2, e);
    }
    return;
}
void clear_stack(SqStack &s)
{s.top=s.base;
free(s.base);
s.stacksize=0;
return;
}
void reverse_words(char s[])
{
    SqStack result, tmp;
    Init_SqStack(result);//用于储存输出结果的栈
    Init_SqStack(tmp);//暂时存储中间过程的栈
    int i = 0;
    while (s[i] != '\000')
    {   
        if (s[i] != ' ')
        {
            push(tmp, s[i]);
        }
        else if (s[i] == ' ')
        {
            push(tmp,s[i]);
            spread(tmp,result);//将tmp里的元素出栈,在入result栈
        }
        i++;
    }
    push(tmp,' ');
    spread(tmp,result);//处理最后一个单词

    char e='0';
    while(!isempty(result))
    {
        Pop(result,e);
        cout<<e;
    }
    clear_stack(tmp);
    clear_stack(result);
    return;
}
int main()
{
    char s[100] = {0};
    gets(s);
    reverse_words(s);
    return 0;
}

 问题出现

这是正常运行的结果

这是 调试时候的输出结果

 

可以发现俩种途径得到输出不一样(人傻了)

后面重新改了一下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模式下, 内存中的变量地址分析>>

  1. 如果定义多个变量 
  2.     DEBUG模式下, 内存中的变量地址和定义的顺序相同 
  3.     变量开始地址都是模式地址 
  4.     先定义的变量在内存高地址 
  5.     后定义的变量在内存低地址 
  6.     Release模式下, 经过优化, 变量的内存地址和变量定义的顺序不相同! 
  7.     如果变量有越界访问的情况, Release模式下的越界访问情况未知. 
  8.     好像有一些规律, 依赖于编译器的优化选项 
  9.     依赖变量越界访问达成变量的存取, 在Release模式下会和Debug模式下的运行效果不相同. 
  10.     运行结果肯定不对了. 是否报错,要看运气了. 如果不报错, 发布后会死的很惨.

所以,之前 修改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可能会导致内存溢出。

 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值