理念
printf
是个非常有用
的构造.它可能是有史以来最多优化和调试
的函数
.但是,因为它可能按不安全
方式使用,它不能在@safeD
代码中使用.
添加检查与其相应的格式限定符
不匹配的参数,可缓解其中的许多问题
.该提案解决剩余的问题
,并使printf
在@safe
代码中可用.
理由
printf
是一个有许多优点
的主力函数
:
1,它可能是有史以来优化和调试
最多的非平凡函数
.
2,它非常轻量
,因为生成最少的代码
来调用它
3,无其他依赖,也可工作,比如将D移植到新平台
时
4,无需d运行时
或Phobos
即可工作
5,在BetterC
模式下工作
6,这是周知
的
但是,它有个不安全
的接口.D
正在朝着默认安全
的方向发展,这就使得使用printf
很笨拙.在调用前加上debug
可在@safe
代码中接受printf
调用,但是当在非调试
版本中需要printf
时,这不管用.
printf
的不安全
问题是:
1,参数
与格式限定符
不匹配.编译器检查
这些不匹配
可缓解它
2,%s
按参数
指向串的指针
.虽然只能读取串
,但指针仍无限制遍历串
.
3,%.s
参数带(int,char)
形式的参数.整数
是要打印的符数
,但<=0
值有不安全的行为
.
4,格式串
不是字面
,编译器无法检查它
.
这些是可修复
的问题,或可限制它,来让它内存安全
.
描述
仅当格式串
是串字面
时,才会有效.
重写%s
格式限定符
目前,编译器根据参数类型
检查格式限定符
.如果有不匹配
,则诊断错误
.如果格式限定符
为%s
,则此建议,重写格式限定符
以匹配参数类型
.
如果格式限定符
为%s
,且相应参数
是char
或wchar_t
的D数组
,则用%.*s
(或%.*ls
)替换格式
,且用以下形式
的两个参数
替换参数:
cast(int)argument.length & 0x7FFF_FFFF, argument.ptr
此掩码确保长度
在.*s
的安全工作的区间
内.如果实际长度
超过0x7FFF_FFFF
,则截断整个串输出
,但至少它是内存安全
.
Sprintf,fprintf,snprintf
等
此DIP
适合标有pragma(printf)
和@safe
或@trusted
的函数
.
重大更改和弃用
没有已知的重大更改.
PB
:此时,我发现令人反感的是(a)
,是用一堆编译器内部重写
来实现的更好的接口
,而不是普通的D代码
;(b)
它掩盖了现有C
的printf
函数,而不是与它并存.
这是printf
上面的简单包装.考虑:
printf("%s\n", 3);
这崩溃导致C程序
.目前,对D
,将报错.根据该提案
,如下重写它:
printf("%d\n", 3);
重写只会针对%s
格式限定符.
对以下内容:
char* s;
printf("%s\n", s);
不会重写,但该调用不安全
:
char[] s;
printf("%s\n", s);
目前编译器
拒绝它.根据该提案
,如下重写它:
char[] s;
printf("%.*s\n", cast(int)s.length & 0x7FFF_FFFF);
这将使它安全
.
我想不出该提案让printf
现有用法不可用.如果有,则有解决方法
:
1
.对格式
使用变量
而不是串字面
:
char* fmt = "hello %s!\n";
printf(fmt, "betty");
2
.此行为是由按"pragma(printf)"
标记的函数
触发的.如果不想要它,就不要这样.或按如下声明printf
:
extern (C) int printf(const(char)*, ...);
:如果需要个更安全的DMD
的printf
,它不会有Phobos
的writef
的所有臃肿和包袱
,则,让我们写一个.但是,用普通D
编写它并在普通D模块
中放置它,而不是在用户
背后悄悄重定义printf
.
对我们来说,添加printf
检查参数代码
是个无可挑剔的胜利
.C
和C++
编译器似乎也在添加它
.这只是个小小的改进
.