前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >开发者自述:我为什么从C语言转投了D语言?

开发者自述:我为什么从C语言转投了D语言?

作者头像
新智元
发布于 2019-07-12 07:23:25
发布于 2019-07-12 07:23:25
1.4K00
代码可运行
举报
文章被收录于专栏:新智元新智元
运行总次数:0
代码可运行


作为开发人员,换开发语言其实是一件需要很高成本的事,本文主要讲讲我是怎么从C语言转投D语言的。

其实我的经历和许多系统程序员的故事差不多。曾经有一段时间,C是大多数程序员的首选语言。有一天,我意识到我的大多数C程序都在重新实现C++中的东西:动态数组、更好的字符串、多态类等等。所以我尝试使用C++,起初我很喜欢它。RAII、类、泛型等新的组件和概念让编程再次变得有趣起来。

我曾经想象过,如果我把所有关于C ++的书籍都看一遍,并掌握了模板元编程之类的东西,我说不定会成为系统编程的全能之神,我写的代码会让人大吃一惊。但事后看来,学习也可能最终会产生更多相反的效果: 我写出的代码实际上变得更糟。

总之全能之神当不上了,我很伤心。

我记得我读过Scott Meyer著名的《Effective C ++》,这本书其实更多讲的是指出低效率的C ++编程的问题,我发现自己写的大多数C++代码都对上了号。让我们面对现实吧:C可能很难用,但它确实足够“优雅”,而提到C++,你很难跟“优雅”搭上边。

很多前C ++程序员最终都用回了C。就我而言,我发现了D语言。其实D也不完美,但是我使用它因为它让我感觉更像是C++应该有的样子(C+=1)。比如以下面这个简单的C程序为例(一加一等于几?):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 #include <stdio.h>
 
int main()
{
   printf("1 + 1 = %d!\n", 1 + 1);
   return 0;
}

如果使用C++标准库,代码是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>

int main()
{
    std::cout << "1 + 1 = " << 1 + 1 << "!" << std::endl;
    return 0;
}

如果使用D语言,代码是这样的:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import std.stdio;

void main()
{
    writef("1 + 1 = %d!\n", 1 + 1);
}
代码语言:javascript
代码运行次数:0
运行
复制

这个例子虽然浅显,但它体现出了C++和D之间背后理念的一些区别。

这篇关于C ++成员函数指针的文章也是对D的起源的一个很好的解释。如果你酷爱编程,这篇文章是一个很好的解读, 但我的解读是:C++成员函数指针应该是感觉像是一个低级功能(就像普通函数指针一样),但其实现的复杂性和多样性说明它们真的很“高级”。

这些指针的实现过程很复杂,因为关于它们能做什么/不能做什么的规则是很微妙的。作者解释了几个C ++编译器的实现,包括优雅而简单的Digital Mars C ++实现,即DMC。DMC编译器是由Walter Bright编写的,他是“D语言”的发明者。

D具有C ++的类和模板以及其他核心功能,但设计者花费了大量时间思考C ++规范,以及如何让设计和编程变得更简单。Walter曾经说过,他在部署C ++模板的痛苦经历,让他考虑过根本不把该功能纳入D,后来他意识到,这个过程本来不需要那么复杂。

下面对D语言的功能和特点进行一番大概的介绍,其实可以把D视作一个“改进版”的C语言。介绍中时刻少不了和C/C++的对比。

-betterC开关

D编译器支持-betterC开关,该开关可以启用/禁用D运行时以及依赖于它的所有高级功能。上面的C代码可以直接转换为betterC:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 import core.stdc.stdio;

extern(C):

int main()
{
    printf("1 + 1 = %d!\n", 1 + 1);
    return 0;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ dmd -betterC example.d
$ ./example
1 + 1 = 2!

生成的二进制文件看起来很像等效的C二进制文件。事实上,如果你在betterC中重写了一个C库,仍然可以链接到已经对C版本编译的代码,无需修改就可立即使用。

实际上,如果只是要在D语言中编写类似C的代码,并不需要-betterC开关。只有在没有D Runtime的特殊情况下才需要使用。

静态assert()

这个功能允许开发者在编译时验证一些假设。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static assert(kNumInducers<16;

系统代码通常对对齐或结构大小或其他事物做出假设。使用静态assert不仅可以记录这些假设,而且如果有人通过添加struct成员或其他东西来破坏假设,则会触发编译错误。

Slices

典型的C代码中存在大量的“指针/长度”参数对,一个常见bug就是二者的不同步。对于由指针和长度定义的一系列内存,Slice是一种简单且超级有用的抽象表示。现在不必使用这样的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
buffer_p += offset;
buffer_len -= offset;  // Got to update both

而可以用下边这种更不容易出bug的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
buffer = buffer[offset..$];

Slice 其实就是具备优秀语法功能的指针/长度对。

编译时间函数估计 (CTFE)

许多函数都可以用编译时间来评估。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
long factorial(int n) pure
{
   assert (n >= 0 && n <= 20);
   long ret = 1;
   foreach (j; 2..n+1) ret *= j;
   return ret;
}
 
// Statically allocated array
// Size is calculated at compile time
Permutation[factorial(kNumThings)]permutation_table;

scope Guards

函数的一部分中的代码通常会在后续部分带上一段清理代码。一个常见的错误来源是未能正确匹配该代码,(尤其是涉及多个控制流路径时)。D的scope guards设定使得这个问题变得不再困难:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
p = malloc(128);
// free() will be called when thecurrent scope exits
scope (exit) free(p);
// Put whatever if statements, or loops,or early returns you like here

你甚至可以在作用域中使用多个scope,或嵌套使用scope。清理代码将在需要时以正确的顺序被调用。

D语言还利用结构析构函数支持RAII。

常量和不可变量

有一个流行的说法是,C和C++中的const对编译器优化很有用。不过D的作者表示,每当他想到一个新的基于const的C++优化时,最终都发现它在实际代码中并不起作用。所以他对D的const语义做了一些修改,并添加了不可变量。可以在D const FAQ中阅读更多内容。

函数纯度

可以实施函数纯度功能。我之前写过关于pure关键字的一些好处。

@Safe

SafeD是D的一个部分,禁止使用指针类型转换和内联汇编等高风险语言功能。标记为@safe的代码由编译器强制执行,不使用这些功能,因此高风险代码可以仅限需要这些功能的应用程序的一小部分。

元编程

如前所述,元编程在一些C ++程序员中名声不好。但是D中的元编程具备一些没那么有趣的优点,程序员一般倾向于只在必要时才用,而不是一个有趣的谜题。

需要将枚举类型的名称作为数组?容易!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
enum State
{
   stopped,
   starting,
   running,
   stopping,
}
 
string[] state_names =[__traits(allMembers, State)];

没有预处理器

好吧,这其实是一个“非功能”,但D没有相当于C的预处理器的功能。所有理智的用例都被替换为本机语言功能,如清单常量和模板。这包括适当的模块支持,这意味着D可以摆脱旧#include黑客的限制。

关于D语言的更多内容,可查看D语言作者Walter Bright的更详细的介绍:

https://dlang.org/blog/2018/06/11/dasbetterc-converting-make-c-to-d/

参考链接:

https://theartofmachinery.com/2019/04/05/d_as_c_replacement.html

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 新智元 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
《Java初阶数据结构》----8.<java对象的比较总结>
有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整堆,那该如何处理呢?
用户11288958
2024/09/24
1350
《Java初阶数据结构》----8.<java对象的比较总结>
【IT领域新生必看】深入浅出Java:揭秘`Comparator`与`Comparable`的神奇区别
在Java编程中,对象排序是一个常见的需求。为了实现对象的排序,Java 提供了两个重要的接口:Comparable和Comparator。对于初学者来说,理解这两个接口的区别及其使用场景,是编写高效和灵活代码的关键一步。本篇文章将详细介绍Comparator与Comparable的定义、用法及其区别,帮助你全面理解这些重要概念。
E绵绵
2024/07/12
2000
Java中的比较器Comparable与Comparator
使用背景: 当元素没有实现java.lang.Comparable接口而又不方便改代码,或者是实现了Comparable接口,也指定了两个对象的比较大小的规则,但此时不想按照预定义的方法比较大小。
鱼找水需要时间
2023/04/28
7950
Comparable和Comparator
---- 1. Comparable接口 在java.lang包下,实现了Comparable函数式接口的对象可以自然排序,而数组和集合实现了该接口,所以我们会用Arrays.sort()或Collections.sort()来排序 Comparable比较大于就返回1,小于返回-1,等于返回0 如果自定义的对象也要排序,就需要实现该接口并且手动重写里面的compareTo()方法 返回值 函数名 解释 int compareTo(T o) 将此对象与指定的对象进行比较以进行排序 需要排序的自定义对
晚上没宵夜
2020/03/10
5210
【Java 学习】:抽象类&接口
💢💢在Java中,一个类如果被 abstract 修饰称为抽象类,抽象类中被 abstract 修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。
IsLand1314
2024/10/15
1680
【Java 学习】:抽象类&接口
《JAVA SE》面向对象编程(下篇)
上一篇讲到了接口,接下来将补充一下常用的接口以及Object类的初识,链接如下: 《JAVA SE》面向对象编程(中篇)
VIBE
2022/12/02
2810
Java 解惑:Comparable 和 Comparator 的区别
该文介绍了Java中的自然排序和比较器排序两种方式,并举例说明了使用这两种方式进行排序的具体实现。同时,也探讨了Comparator接口在定制排序中的实现和应用。
张拭心 shixinzhang
2018/01/05
2.1K0
Java 解惑:Comparable 和 Comparator 的区别
你所要知道的关于接口的知识点
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第31天,点击查看活动详情
用户9996207
2023/01/12
2530
Java的接口
推荐一个网站给想要了解或者学习人工智能知识的读者,这个网站里内容讲解通俗易懂且风趣幽默,对我帮助很大。我想与大家分享这个宝藏网站,请点击下方链接查看。 https://www.captainbed.cn/f1
鲜于言悠
2024/06/02
1130
Java的接口
抽象类与接口(3)(接口部分)
❤️❤️观察其内部结构我们可以知道在Comparable后面还有个<T>,在语法上这是泛型,之后会讲,这并不影响我们现在的思路,这个<T>中的T你写student类,后面的compareTo方法中的第一个参数就是student类,如上图。
E绵绵
2024/04/08
1000
抽象类与接口(3)(接口部分)
Java List排序:Comparable与Comparator接口及Stream API应用
在 Java 编程中,集合(List)元素排序是常见需求。本文将解读使用 Comparable 接口、Comparator 接口及 JDK 8 的 Stream API 对 List 进行高效排序,并通过实例代码演示用法和区别。
Yeats_Liao
2024/12/29
1280
Java List排序:Comparable与Comparator接口及Stream API应用
【数据结构】关于Java对象比较,以及优先级队列的大小堆创建你了解多少???
上期博客讲了优先级队列,优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,为了简单起见,我们只是插入了Integer类型,那优先级队列中能否插入自定义类型对象呢?
用户11288949
2024/09/24
1050
【数据结构】关于Java对象比较,以及优先级队列的大小堆创建你了解多少???
Comparable和Comparator的区别和用法
(2)、自己写一个比较类class,实现Comparator接口并重写compare()方法
静谧星空TEL
2021/04/27
4360
Comparable和Comparator的区别和用法
java-comparator
要使自己的类拥有排序功能,就要实现comparator接口,重写compare方法。
luxuantao
2021/02/24
5410
Comparable 和 Comparator的理解
list或者数组实现了这个接口能够自动的进行排序,相关类的方法有Collections.sort(),Arrays.sort();
cxuan
2019/06/03
5920
Java.lang.Comparable接口和Java.util.Comparator接口的区别
Java的Comparator和Comparable当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。 1.Comparable简介: Java.lang.Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。 此外,实现此接口的对象可以用作有序映射中的键
10JQKA
2018/05/09
1.1K0
Java.lang.Comparable接口和Java.util.Comparator接口的区别
java中Comparable和Comparator的区别
java.lang.Comparable和java.util.Comparator是两个容易混淆的接口,两者都带有比较的意思,那么两个接口到底有什么区别,分别在什么情况下使用呢?
子润先生
2021/06/22
3310
【day14】异常处理与Object类深入解析
在深入探讨异常处理与Object类之前,让我们回顾一下【day13】中的关键内容:
程序员波特
2024/12/25
1110
【day14】异常处理与Object类深入解析
【如何通过JAVA实现接口的应用和图书的管理】
我们可以通过查看String源码看到它里面有一个compareTo的方法,它能够帮助我们去比较引用类型的大小。
ImAileen
2024/12/17
880
【如何通过JAVA实现接口的应用和图书的管理】
似懂非懂的Comparable与Comparator
  Comparable与Comparator都是用于集合的排序,对于大多数人来说Comparator可能略微比Comparable要熟悉一点,类似下面这几句代码的使用频率应该是最高的。
用户1148394
2019/01/08
8360
推荐阅读
相关推荐
《Java初阶数据结构》----8.<java对象的比较总结>
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档