《玩转D语言系列》三、轻松*,把它当C语言先用起来

时间:2023-03-08 17:10:10
《玩转D语言系列》三、轻松*,把它当C语言先用起来
  前面说过,本系列文章的前提是您懂C语言,懂面向对象中的一些概念,如果没有任何变成基础,从零开始学习D语言将是一个漫长的过程,因为很多概念都要重新诠释,让一个没有基础的人经过漫长的学习过程,然后还找不到工作,这显然是在坑人,所以我的思路是让有编程经验的人先了解D语言,用D语言,使D语言为更多的项目所用,从而创造出工作机会,然后再让新入行的人开始学习。好了,言归正传,开始我们今天的学习之旅

先来熟悉下编译器 DMD

首先我先带大家从0开始,成功的编写的一D语言程序,然后编译并运行起来,再介绍一些常用的选项。这部分不是要介绍一个hello world程序如何写,因为第一篇文章中已经有了4个版本,而是要向读者们介绍D语言的编译器的使用,关于编译器有哪些以及在哪下载前面文章中已经介绍,不再叙述,安装也都很简单,无论是在哪个系统下,和其它软件的安装也没什么区别,从现在开始假设您已经安装好了DMD(至于GDC的使用,由于属于GCC家族,所以它跟GCC的使用基本上是一致的,只是有些D语言特有的选项)
dmd 编译器的使用所有平台都是一样的,以下使用 $作为命令提示符,对于不了解 *nix 系统的用户需要注意
通过dmd —help 可以查看所有选项
1、最基本的编译命令
$ dmd xx.d
如果编译成功,会生成两个文件,一可执行文件 xx.o 另一个是链接后的可执行文件 xx (Windows 下对应的文件为 xx.obj 和 xx.exe)
2、如何指定生成的文件名(不是编译完了再改名)
$dmd -ofmain xx.d
这样就可以生成 main 和main.o,注意 -of和文件名之间不能有空白,是连续的
3、如果不希望生成 .o 或者 .obj 文件,可以使用额外参数 -o- ,o前后各一个减号。这个选项的设计也是有点意思
如果想要把 obj 文件指定到别的目录,可以使用 -odobjdir 参数,如:
$ dmd -odobjs -ofmain xx.d
将 obj  文件存放到 objs 目录中,同样,目录名和-od是连续的
4、我们知道,D语言是使用GC的,dmd提供一个选项 -vgc 可以在编译过程中输出,哪些地方使用了GC管理内存,如果没有输出,表示没有使用GC
$ dmd -vgc xx.d
5、直接运行源文件,dmd提供了两种方法可以做到这一点
$ dmd -run xx.d
$ rdmd xx.d
其中rdmd是一个单独的程序,如果是 *nix 系统还可以使用 #!/usr/bin/env rdmd 这样的方式,把rdmd做为D源代码的解释器(别忘了给源文件加可执行权限)
两种方式的工作方式是类似的,都是先将源代码编译,然后执行,区别是 dmd -run 是将生成的文件放在当前目录,运行后删除了生成的可执行文件,而rdmd则是将生成的可执行文件放到了临时目录,运行后不删除,如果没有修改源代码,则直接运行上次生成的文件,不会重新编译,所以第一次会比较慢,以后就非常快了(因为只是比直接执行多了一步判断源代码是否被修改),这种特性使D语言可以做为脚本语言来使用,虽然原理与脚本不同,但使用起来跟脚本一样,而dmd -run 由于删除了生成的可执行文件,所以每次都要重新编译
上面这段描述如果只是按我说的记住的话,这是死记,不推荐,我所推荐的学习方式是要多实践,在实践中去验证,这样记忆会更深刻,学习效果也更好
其实这很好验证,写一个简单的程序即可
import std.stdio;
void main(string[] args)
{
writeln(args);// 输出命令行参数,通过这个方法很容易知道我们的程序是如何启动的
}
分别使用 dmd -run 和 rdmd来运行这个程序,很容易能发现区别,以及rdmd生成的文件具体在哪,和如何通过某哈希算法确定源文件是否被修改。
请读者朋友亲自在自己的系统上测试,这里我就不给出我的运行结果了
如果你觉得这没什么也很正常,也确实没什么,只是一个方便的小工具而已。即使没有写一个也不是啥难事,不过D的源文件第一行可以忽略#! 开始的内容,应该说是一个很贴心的设计

*开始

  细心的读者可能发现,上面的小程序中的 string[] args 似乎在传达着一个信息,D语言中的字符串和数组好像比C/C++的有着更高级的特性,没错,D的字符串和数组的确要比C/C++中的要高级,D不仅支持变长的数组,还支持关联数组,这会在很大程度上加快开发效率,可以想像下如果PHP没有了关联数组,Python没有了字典,它还能那么方便吗,不过很遗憾,我并不想在这篇文章中介绍D中的数组,在学习高级数据类型前我们应该先学习基本数据类型,D语言的基本类型也没有那么特别,如果你熟悉C语言,大多数只要看见类型的关键字就能理解它的意思
D的基本类型有:
void、bool、byte、ubyte、short、ushort、int、uint、long、ulong、cent、ucent、float、double、real、ifloat、idouble、ireal、cfloat、cdouble、creal、char、wchar、dchar
看上去很多,不过有些不需要解释,一看就知道是什么意思,但有些不一定能看的出来,因为其它语言中几乎看不到这样的类型,下面对于不容易理解的做出解释
cent、ucent 为128整数,不过目前还没有实现,所以在程序中不可用,但是它是保留的关键字
real 为80位浮点实现
前缀为i的类型为虚数类型,字面量为加上i后缀的形式,如: ifloat x = 1.23i;
前缀为c的为复数类型,字面量为实数+虚数,如:cfloat x = 2.3 + 3.5i;
注:如果你不知道什么是虚数和复数,那只能说你数学不好,该补补数学了,其它语言多数是由库来实现的,但是如果由语言来实现看起来更自然,用起来也便简洁
char,wchar,dchar的区别分别是 UTF-8 8位,UTF-16 16位,UTF-32 32位,都是无符号的,字符语义,而byte是的数值语义
所以D语言支持多字节的字符,如:wchar wc = ‘我’; dchar dc = ‘你’;
如果您还记得前面文章中提到的D语言的源文件编码必须是UTF-8,UTF-16,UTF-32,可能会有这样的疑惑,汉字的UTF-8编码占三个字节,那UTF-8编码的源文件中的汉字字面量不是要占三个字符,怎么能存到wchar 16位中呢。如果是按源文件是什么就存什么的原则肯定是不行的,这里编译器会将UTF-8的编码转换成UTF-16的编码,然后再赋值,同理,虽然UTF-32是四个字节,足以容纳3个字节的汉字,但既然规定了dchar是UTF-32编码,就会将其转换成UTF-32,而不是直接存UTF-8的编码数据。如果您对编码不熟悉,可能不能很好的理解这段叙述,建议您去找下关于字符编码方面的资料学习一下,再回来看
对于字节序,D语言是CPU相关的,这点上和Java不同,做为一个系统编程语言这点我觉得有必要指出,不清楚字节序是什么的建议找下相关资料
由于D语言中的基本数据类型并不特别,能兼容C中的所有数据类型,所以在与C交互的时候也不需要特别的处理,直接找到对应的类型用即可,也许你不觉得这是优点,觉得就应该是这个样子的,那你对比下Go和Rust的做法就会发现,它们的做法也是醉了
关于默认初始化,D中的变量都会默认初始化为类型的默认值,整数类通常是0,但需要注意的是浮点变量的初始值是 类型.nan, 如 float.nan;
字符型 char,wchar,dchar 分别是 0xFF,0xFFFF,0x0000FFFF,并且所有的类型数据的初始值都可以用 类型.init 来表示,如果你有兴趣可以写个小程序测试下,如: writeln(int.init),writeln(wchar.init) 等
因为初始化是一个赋值过程,所以 int a; 与 int a = int.init;是等价的,所以初始化过程一样会进行内存的拷贝,虽然代价是非常小的,但有的时候为了效率我们不希望初始化,也不需要初始化,那怎么办呢,做为兼顾效率和高级特性的语言,是允许不默认初始化的。D语言使用 int a = void;这种形式来让变量不进行默认的初始化,即用void初始化等于不初始化,如果你真的需要这么做的话
对于运算符,C中的运算符在D里面都可以用,而且优先级是一样的,所以这里就不列出所有的运算符了,不过D里面还有一些其它的运算符,这些运算符会在后面的文章中介绍,因为它们是一些高级特性所需要的运算符
最后说下D语言的main函数,这个函数不需要返回值,所以返回类型为void,熟悉C语言的朋友都知道,C里面main的返回值是程序退出时的代码,通常表示程序的结果是否出错,那D没有返回值怎么表示出错呢,D是通过现代语言中流行的异常处理的方式来做的,所以也就不需要返回一个整数了。从程序启动的角度说,C程序的main函数是由 C runtime的启动代码调用的,而D的main函数是被C的main函数调用的,只不过调用D的main函数前要初始化D runtime,通过 rt_init 函数,完了再调用,rt_term,(可能还有一些其它的代码,我没看源码,也不好说)这在第一篇Hello World程序中已经用过
另外D语言中流程控制语句和函数也是兼容C的,所以到目前为止,您已经掌握了很多关于D语言的很多知识了
基本类型
函数的定义与调用(D还支持委托,闭包,纯函数等后面会介绍,目前先当C的函数用就行)
运算符
流程控制语句(D还有一些高级流程控制语句,后面会介绍)
调用C函数(C函数包括C标准库、自定义函数、和系统调用或叫系统API)
所以到目前为止,您用它来写一些练习小程序应该完全没有问题了,如一些小算法练习,一些简单的C的API调用
不过到目前位置,我们所学到的D语言只不过是C语言的代替品,语法上也基本一致,还体现不出D语言的强大之处,但不要因为这个原因而不练习,成功的经验会增加你后续学习的信心,在练习的过程中还可能会发现一些细节上的不同