当前位置:网站首页>d的dip1000,1
d的dip1000,1
2022-06-22 16:27:00 【fqbqrr】
DIP1000:现代系统编程语言中的内存安全
现代高级语言如D是内存安全的,可防止用户意外读写未用内存或破坏语言的类型系统.D的安全子集提供保证,用@safe来保证函数内存安全.
@safe string getBeginning(immutable(char)* cString)
{
return cString[0..3];
}
编译器拒绝编译此代码.无法知道cString的三个字符切片会产生什么结果.可能是cString[0]==\0的空串引用,或无\0结尾的1/2字符.此时,结果将是内存违规.
@safe不慢
注意,即使是低级系统编程项目也应尽量用@safe.不使用垃集也可保证安全.
如创建C或C++库接口,或为了性能运行时避免垃集.但,可在完全不用垃集编写安全代码.
D可这样,是因为内存安全子集不会阻止原始内存访问.
@safe void add(int* a, int* b, int* sum)
{
*sum = *a + *b;
}
尽管按完全相同未经检查的C方式解引用这些指针,但可编译且是完全内存安全的.
因为@safeD禁止创建指向未分配内存区域的int*或float**,如int*可指向空(null)地址,但这不是内存安全问题,因为空地址受操作系统保护.
在损坏内存前,解引用它们会使程序崩溃.因为仅在请求更多内存或显式调用垃集时才运行它,不涉及垃集.
D切片类似.运行时索引时,动态检查索引是否小于长度,仅此而已.不会检查是否指向合法内存区域.内存安全通过源头上防止创建引用非法内存切片来实现的,如第一例所示.再一次,与垃集无关.
这启用了许多内存安全,高效且独立于垃集的模式.
struct Struct
{
int[] slice;
int* pointer;
int[10] staticArray;
}
@safe @nogc Struct examples(Struct arg)
{
arg.slice[5] = *arg.pointer;
arg.staticArray[0..5] = arg.slice[5..10];
arg.pointer = &arg.slice[8];
return arg;
}
如上,在@safe代码中,D自由地允许你不检查内存处理.arg.slice和arg.pointer可能在垃集堆上,也可能在静态程序内存中引用内存.语言不关心.
为指针和切片分配内存,程序可能需要垃集或管理一些不安全内存,但已分配内存则不必.如果该函数需要垃集,会因为@nogc而编译失败.
然而…
这里有个历史设计缺陷,即内存也可能在栈上.如果稍微改变函数会怎样?
@safe @nogc Struct examples(Struct arg)
{
arg.pointer = &arg.staticArray[8];
arg.slice = arg.staticArray[0..8];
return arg;
}
arg构是值类型.调用examples时,复制内容(arg构)到栈中,并且可在函数返回后,覆盖它.staticArray也是值类型.像结构中有十个整数一样,与结构的其余部分一起复制.返回arg时,复制staticArray内容到返回值,但ptr和slice继续指向arg,而不是返回的副本!
但可解决.它允许像以前一样,在函数中编写包括引用栈的@safe代码.甚至可安全(@安全)编写一些以前用@system的技巧.该修复程序是DIP1000.因此如果用最新的每晚dmd编译此示例,会默认报告已弃用警告.
先生后死
DIP1000增强了指针,切片和其他引用的语言规则.dip1000.可用-preview=dip1000预览编译器开关启用新规则.现有代码需要一些更改才能使用新规则,因而默认不启用该开关.未来将是默认,因此最好现在就启用它,并努力使代码与它兼容.
基本思想是限制引用(如数组或指针)的生命期.如果栈指针存在时间不超过指向的栈变量,则它并不危险.普通引用继续存在,但只能引用有无穷生命期数据:即垃集内存及静或全局变量.
开始
构造有限生命期引用的最简单方法是用有限生命期对象赋值它.
@safe int* test(int arg1, int arg2)
{
int* notScope = new int(5);
int* thisIsScope = &arg1;
int* alsoScope; // 非初始域
alsoScope = thisIsScope; // 但这就是了
// 先前定义变量,有更长生命期,所以禁止!
thisIsScope = alsoScope;
return notScope; // 好
return thisIsScope; // 错误
return alsoScope; // 错误
}
测试这些示例时,记住使用-preview=dip1000编译器开关,并标记函数为@safe.因为不检查非@safe函数.
或,可显式用scope关键字来限制引用的生命期,这里.
@safe int[] test()
{
int[] normalRef;
scope int[] limitedRef;
if(true)
{
int[5] stackData2= [-1, -2, -3, -4, -5];
//stackData2在limitedRef前结束,因而禁止
limitedRef = stackData2[];
//你这样
scope int[]evenMoreLimited=stackData2[];
}
return normalRef; // 好.
return limitedRef; // 禁止.
}
如果不能返回有限生命期引用,则如何使用它们?简单.记住,仅保护数据地址,而不是数据自身.即有很多方法可从函数中传递出去域数据.
@safe int[] fun()
{
scope int[] dontReturnMe = [1,2,3];
int[] result=new int[](dontReturnMe.length);
//复制数据,而不是让结果`引用`受保护内存.
result[] = dontReturnMe[];
return result;
// 同上,简写.
return dontReturnMe.dup;
// 计算感兴趣数据
return
[
dontReturnMe[0] * dontReturnMe[1],
cast(int) dontReturnMe.length
];
}
取交互过程
目前,DIP1000仅处理有限生命期引用,但也可给函数参数应用scope存储类.保证了退出函数后,不会使用该内存,可按scope参数使用局部数据引用.
@safe double average(scope int[] data)
{
double result = 0;
foreach(el; data) result += el;
return result / data.length;
}
@safe double use()
{
int[10] data = [1,2,3,4,5,6,7,8,9,10];
return data[].average; // 工作!
}
开始,最好关闭自动推导属性.一般,自动推导很好,但它为所有参数安静的添加域属性,很容易忘记当前工作.从而更难学习.
始终明确指定返回类型(或void/noreturn)来避免:用@safe const(char[]) fun(int* val)而不是@safe auto fun(int* val)或@safe const fun(int* val).该函数也不必是模板或在模板内.未来研究scope自动推导.
scope允许处理栈指针和数组,但禁止返回它们.目的呢?输入return scope属性:
//作为字符数组,串也适合`DIP1000`.
@safe string latterHalf(return scope string arg)
{
return arg[$/2 .. $];
}
@safe string test()
{
//在静态程序内存中分配
auto hello1 = "Hello world!";
//在栈上分配,从`hello1`复制
immutable(char)[12] hello2 = hello1;
auto result1 = hello1.latterHalf; // 好
return result1; // 好
auto result2 = hello2[].latterHalf; // 好
//很好!result2是域,不能返回它
return result2;
}
中域参数检查传递给它们的参数是否为scope.是,则按不超过任一中域参数的scope值对待返回值.否(都不是域),则按可自由复制的全局引用对待返回值.像scope,return scope(中域)是保守的.即使不返回returnscope保护地址,编译器仍会像返回一样,检查调用点生命期.
scope是浅的
@safe void test()
{
scope a = "first";
scope b = "second";
string[] arr = [a, b];
}
在test中,不会编译初化arr.因为如果需要,语言会在初化时自动加scope到变量中,这令人惊讶.
但是,请考虑scope string[] arr上的scope保护什么.可保护两样:数组中串地址,或串中字符地址.为了该赋值安全,scope必须保护串中字符,但它只保护顶级引用,即数组中串.因此,该示例不工作.现在更改arr为静态数组:
@safe void test()
{
scope a = "first";
scope b = "second";
string[2] arr = [a, b];
}
因为静态数组不是引用,这工作.在栈上就地分配所有元素内存(即,栈包含元素),与包含引用存储在其他地方元素的动态数组不一样.
静态数组为scope时,按scope对待元素.且如果arr非域,则该示例无法编译,因此可推导scope.
实用技巧
需要时间来理解DIP1000规则,许多人不愿学习.第一个也是最重要的技巧是:尽量避免非@safe代码.
当然,该建议并不新鲜,但对DIP1000来说更重要.总之,在非@safe函数中,语言不会检查非安全函数的域和中域有效性,而调用这些函数时,编译器会假定满足了这些属性.
因而在不安全代码中,域和中域危险.但是只要避免标记为@trusted,D代码几乎不会造成损害.@safe代码中滥用DIP1000可能会导致不必要编译错误,但它不会损坏内存和其他错误.
值得一提的第二个重点是,如果函数属性仅接收静态或GC分配数据,则不必用域和中域.
许多语言禁止程序员引用栈.D可以这样,不代表必须用栈.这样,像DIP1000出来之前,不必花费更多时间来解决编译器错误.如果想用栈,就注解函数.这样,不破坏接口.
下一步?
本文,说明了在DIP1000中使用数组和指针.原则上,还允许读者用类和接口使用DIP1000.
唯一要了解的是包括成员函数中this指针的类引用,同DIP1000一起就像指针一样使用.
DIP1000对ref函数参数,结构和联还有些特性.还会深入探讨DIP1000如何处理非@safe函数和属性自动推导.目前,计划是再写两篇文章.
边栏推荐
- What is flush software? Is it safe to open an account online?
- Heartless sword in Chinese
- High voltage direct current (HVDC) model based on converter (MMC) technology and voltage source converter (VSC) (implemented by MATLAB & Simulink)
- When online and offline integration accelerates and information docking channels are diversified, the traditional center will not be necessary
- MTLs guidelines for kubernetes engineers
- [mysql] data synchronization prompt: specified key was too long; max key length is 767 bytes
- 视频直播系统源码,顶部标题栏的隐藏和标题修改
- clickhouse 21.x 集群四分片一副本部署
- How to do well in R & D efficiency measurement and index selection
- Qt Notes - qmap Custom key
猜你喜欢

推荐7款超级好用的终端工具 —— SSH+FTP

Blazor University (31)表单 —— 验证

. Net release and support plan introduction
![[applet project development -- Jingdong Mall] subcontracting configuration of uni app development](/img/12/53f00e730426880b9a9d0407bb5863.png)
[applet project development -- Jingdong Mall] subcontracting configuration of uni app development

It may be the most comprehensive Matplotlib visualization tutorial in the whole network

内容推荐流程

以小见大:一个领域建模的简单示例,理解“领域驱动”。

imx6ull的GPIO操作方法

Xftp 7 (ftp/sftp client) v7.0.0107 official Chinese free official version (with file + installation tutorial)

A new mode of enterprise software development: low code
随机推荐
来厦门了!线上交流限额免费报名中
Blazor University (31) form - Validation
Noah fortune plans to land on the Hong Kong Stock Exchange: the performance fell sharply in the first quarter, and once stepped on the thunder "Chengxing case"
CMB model 23 ukey is not recognized on win7
Service or mapper cannot be injected into a multithread
MySQL string field to floating point field
TypeScript(7)泛型
synchronized实现原理
Post to asp Net core length limitation and solution when transferring data
面试突击58:truncate、delete和drop的6大区别!
170million passwords of Netcom learning link have been leaked! What are the remedies?
NLog自定义Target之MQTT
Mybaits: interface proxy implementation Dao
有同学问PHP要学什么框架?
Fluentd is easy to get started. Combined with the rainbow plug-in market, log collection is faster
Development mode of JSP learning
Social responsibility: GAC Honda advocates children's road traffic safety in "dream children's travel"
Blazor University (30) form - derived from inputbase
试用了多款报表工具,终于找到了基于.Net 6开发的一个了
MySQL instruction executes SQL file