翻译d作者消除内存崩溃

1002 篇文章 1 订阅
7 篇文章 0 订阅

作者:d作者(w.b)
结合这两篇来一起学习:参数所有权d的所有权与借贷
内存崩溃:1,致命昂贵的问题.2,很难手动检查.3,由于粗心很容易引入(审查问题)
第1,是缓冲溢出
结束缓冲溢出:1,溢出保护.2,尽量用动态数组而不是原始指针.3,用ref(引用)而不是原始指针.4,用常和不变.
结束栈崩溃:1,ref.2,,3,.
结束别名问题:转换非指针到指针,联的其他类型覆盖指针.
结束分配漏洞:用垃集.但我不想用垃集.1,显式分配/释放,包括写自己的分配器.2,资源获取即初化,即带析构器.3,引用计数.
显式分配/释放内存:1,忘记释放了(泄露内存).2,释放后使用.3,多次释放.4,光释放,无分配.
RAII:1,对域对象不错.2,其他类型不友好.
引用计数如下:

构 S{*p;}f(引用 S s,引用 S t){
    s=S();//消灭s的原先内容
    *t.p=1;//崩溃
}
空 主(){
    S s;整 i;s.p=&i;f(s,s);//同一指针
}

两个指针同样类型,但其中一个是可变的.
dip1021参数所有权与调用函数.如是可变,则禁止传递超过1个的同一内存对象引用(即多个同一指针)给函数.再扩展一下:如有可变,则禁止多个引用指向同一内存对象(重要,内存安全的精髓,借贷指针的原则).
再讲明白点:允许一个可变引用/多个常引用,但禁止超过一个可变引用/混杂可变和常引用.
所有权:一个内存对象单可变引用拥有该对象.

*f();*p=f();//现在,p拥有f()返回的对象

移动(传输)所有权:移动可变引用时,就传输所有权.原引用无效.

*p=f();//p是所有者*q=p;//现在,q是所有者,p无效了,
*q=3;//可以,所有者修改的权利
*p=3;//错误!无效了,

复制(借用)引用:复制可变引用借用所有权,借用完毕,就归还了.用来表示借用.即我借用所有权,是有生命期的,因为他不是我所有的,到时要归还,所以用域表示这是有生命期的

*p=f();//现在p是所有者
域 整*b=p;//b从p借
*b=3;//借用期间,随便用
*p=4;//对不起,原主人申明要用了,b失效了.
*b=5;//错误,b已经失效了.

当以下情况发生时,借用失效:1,借用引用的最后1次使用.2,借用引用离开了.3,原主人又用了.
开始借用到上面3个之一完借用生命期.也叫非词法域.这是通过分析数据流实现的.
分解函数结构一堆代表从一个块到另一个块的路径连接起来的代码块.
对每个块构建数据流等式:输出=转换(输入).(输入,输出为每个跟踪变量的状态).
解这个方程式:N个方程,N个未知变量.
创建指针:调用返回指针的函数时,创建指针.

*f();//返回所有者指针的函数*p=f();//然后,移动至p.

析构指针:移至函数时,析构指针.如:

g(*p);
g(p);//p放弃了所有权.这里有块边界流的变化
*p=3;//无效.

悬挂指针:

@活 空 s(){*p=f();
}//错误,退出时p是活的.

带所有权函数:

@活 空 g(*p){
}//错误,p是悬挂指针
@活 空 h(*p){
    g(p);//传输给g了,是g的事情了
}

指针在跨块间流动(传输).而不是仅在块间.
函数借用指针:

@活 空 m(域 整*b){
}//好,p是域指针,只是借用而已
@活 空 n(域 整*b){
    m(b);//没问题,借用后回来了,
    g(b);//错误,借用指针逃离(`作用域`)了,即跑到其他地方了,而`域`表示,b是借用的.离开这个区就应该消灭,不再存在了.
}g(*p);

控制流:

*p=f();==>p是有效的
整*p=f();==>g(p);==>p是无效的.
p,究竟有效不?

错误,p不能既是有效,又是无效.
分配和释放,注意他们不能为@活.

*分配();
空 释放(*);

泄露内存1:

f(){*p=分配();
}//错误,退出时p是活的.

注意:对语言而言,分配和释放,没有特殊行为.即,你可以按第一类公民自定义分配器.
泄露内存2:

*p=分配();
p=分配();//错误,重写活指针

双释放:

*p=分配();
释放(p);
释放(p);//错误,p有未定义值

释放后用:

*p=分配();
*p=3;//使用中
释放(p);//释放了.
*p=4;//错误,p有未定义值

消灭借用指针:

@活 空 m(*p){
    域 整*b=p;
    释放(b);//错误,不能把借用指针转成所有者,即不能释放非所有者指针
}//错误,p是悬挂指针.

现在,p已经归m函数所管了,所以你不要忘记处理p了.
常指针:

@活 空 f(*p){
    域 常()*c1=p;//借用常指针
    域 常()*c2=p;//借用常指针
    整 i=*c1;//c1,活
    整 j=*c2;//c2,活
    j=*c1;//c1活
    *p=3;//使用p,使`c1,c2`都无效.使用可变指针后,常指针都无效了.因为`一个可变指针/多个常指针`原则,两个不能并存.
    i=*c1;//错误,c1无效
    j=*c2;//错误,c2无效
    释放(p);//释放p.
}

调用函数:

b(域 常 整*,域 常 整*);//都是常针b(域 整*,域 常 整*);//不能混合可变与常
@活 空 f(*p){
    b(p,p);//编译
    b(p,p);//编译不过
}

回忆引用计数问题:

构 S{//省略引用计数机制*p;
}f(引用 S s,引用 S t){
    s=S();//消灭s的原先内容
    *t.p=1;//崩溃
}
@活 空 主(){//加上@活
    S s;整 i;s.p=&i;f(s,s);//在此暴露问题,活暴露问题
}

全局变量:
@活不能访问全局变量,只能从参数引进指针/引用.
其他指针类型:引用,出,类,隐式本,包装指针,动态数组,闭包,关联数组.
垃集分配指针,像其他指针一样处理,没啥特殊的,不区分.
@活和其他函数.
@活依赖对接时尊重@活接口的非活函数,@系统,@安全,@信任可与@活对接.因此可渐进添加活函数.
异常:1,在块间导致多个复杂边界.2,当遇见异常控制流时,放弃优化数据流.3,@活依赖分析数据流,因而不能放弃.因此@活不抛的,这也是我(作者)推动不抛作为默认部分原因.
实现:最新的d编译器基本原型模式已经可用了.
结论:1,在已有成功且安全的机制上构建.2.在机制保证内存安全方面的很大进步.3,并不破坏已有代码,可渐进添加.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值