分享
 
 
 

C程序员如何使用D编程(一)

王朝c/c++·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

C 程序员如何使用 D 编程

每个有经验的 C 程序员都积累了一系列的习惯和技术,这几乎成了第二天性。有时候,当学习一门新语言时,这些习惯会因为太令人舒适而使人看不到新语言中等价的方法。所以下面收集了一些常用的 C 技术,以及如何在 D 中完成同样的任务。

因为 C 没有面向对象的特征,所以有关面向对象的论述请参见 C++ 程序员如何使用 D 编程 。

C 的预处理程序在 C 的预处理程序 vs D 中讨论。

获得一个类型的大小

获得一个类型的最大值和最小值

基本类型

特殊的浮点值

浮点除法中的余数

在浮点比较中处理 NAN

断言

初始化数组的所有元素

遍历整个数组

创建可变大小数组

字符串连接

格式化打印

函数的前向引用

无参数的函数

带标号的 break 和 continue

goto 语句

结构标记名字空间

查找字符串

设置结构成员对齐方式

匿名结构和联合

声明结构类型和变量

获得结构成员的偏移量

联合的初始化

结构的初始化

数组的初始化

转义字符串文字量

Ascii 字符 vs 宽字符

同枚举相应的数组

创建一个新的 typedef 类型

比较结构

比较字符串

给数组排序

访问易失性内存

字符串文字量

遍历数据结构

无符号右移

获得一个类型的大小

C 的方式sizeof(int)

sizeof(char *)

sizeof(double)

sizeof(struct Foo)

D 的方式

使用 sizeof 属性:

int.sizeof

(char *).sizeof

double.sizeof

Foo.sizeof

获得一个类型的最大值和最小值

C 的方式#include <limits.h>

#include <math.h>

CHAR_MAX

CHAR_MIN

ULONG_MAX

DBL_MIN

D 的方式 char.max

char.min

ulong.max

double.min

基本类型

D 中与 C 类型对应的类型 bool => bit

char => char

signed char => byte

unsigned char => ubyte

short => short

unsigned short => ushort

wchar_t => wchar

int => int

unsigned => uint

long => int

unsigned long => uint

long long => long

unsigned long long => ulong

float => float

double => double

long double => extended

_Imaginary long double => imaginary

_Complex long double => complex

尽管 char 是一个无符号 8 bit 的类型,而 wchar 是一个无符号 16 bit 的类型,它们还是被划为独立的类型以支持重载解析和类型安全。

在 C 中各种整数类型和无符号类型的大小不是固定的(不同的实现的值可以不同);在 D 中它们的大小都是固定的。

特殊的浮点值

C 的方式 #include <fp.h>

NAN

INFINITY

#include <float.h>

DBL_DIG

DBL_EPSILON

DBL_MANT_DIG

DBL_MAX_10_EXP

DBL_MAX_EXP

DBL_MIN_10_EXP

DBL_MIN_EXP

D 的方式 double.nan

double.infinity

double.dig

double.epsilon

double.mant_dig

double.max_10_exp

double.max_exp

double.min_10_exp

double.min_exp

浮点除法中的余数

C 的方式 #include <math.h>

float f = fmodf(x,y);

double d = fmod(x,y);

long double e = fmodl(x,y);

D 的方式

D 支持浮点操作数的求余(‘%’)运算符: float f = x % y;

double d = x % y;

extended e = x % y;

在浮点比较中处理 NAN

C 的方式

C 对操作数为 NAN 的比较的结果没有定义,并且很少有 C 编译器对此进行检查(Digital Mars C 编译器是个例外,DM 的编译器检查操作数是否是 NAN)。 #include <math.h>

if (isnan(x) || isnan(y))

result = FALSE;

else

result = (x < y);

D 的方式

D 的比较和运算符提供对 NAN 参数的完全支持。 result = (x < y); // 如果 x 或 y 为 nan ,值为 false

断言是所有防卫性编码策略的必要组成部分

C 的方式

C 不直接支持断言,但是它支持 __FILE__ 和 __LINE__ ,可以以它们为基础使用宏构建断言。事实上,除了断言以外,__FILE__ 和 __LINE__ 没有什么其他的实际用处。 #include <assert.h>

assert(e == 0);

D 的方式

D 直接将断言构建在语言里: assert(e == 0);

[注记:追踪函数?]

初始化数组的所有元素

C 的方式 #define ARRAY_LENGTH 17

int array[ARRAY_LENGTH];

for (i = 0; i < ARRAY_LENGTH; i++)

array[i] = value;

D 的方式 int array[17];

array[] = value;

遍历整个数组

C 的方式

数组长度另外定义,或者使用笨拙的 sizeof() 表达式。 #define ARRAY_LENGTH 17

int array[ARRAY_LENGTH];

for (i = 0; i < ARRAY_LENGTH; i++)

func(array[i]);

或:

int array[17];

for (i = 0; i < sizeof(array) / sizeof(array[0]); i++)

func(array[i]);

D 的方式

可以使用“length”属性访问数组的长度: int array[17];

for (i = 0; i < array.length; i++)

func(array[i]);

或者使用更好的方式:

int array[17];

foreach (int value; array)

func(value);

创建可变大小数组

C 的方式

C 不能处理这种数组。需要另外创建一个变量保存长度,并显式地管理数组大小:

#include <stdlib.h>

int array_length;

int *array;

int *newarray;

newarray = (int *) realloc(array, (array_length + 1) * sizeof(int));

if (!newarray)

error("out of memory");

array = newarray;

array[array_length++] = x;

D 的方式

D 支持动态数组,可以轻易地改变大小。D 支持所有的必需的内存管理。

int[] array;

array.length = array.length + 1;

array[array.length - 1] = x;

字符串连接

C 的方式

有几个难题需要解决,如什么时候可以释放内存、如何处理空指针、得到字符串的长度以及内存分配:

#include <string.h>

char *s1;

char *s2;

char *s;

// 连接 s1 和 s2,并将结果存入 s

free(s);

s = (char *)malloc((s1 ? strlen(s1) : 0) +

(s2 ? strlen(s2) : 0) + 1);

if (!s)

error("out of memory");

if (s1)

strcpy(s, s1);

else

*s = 0;

if (s2)

strcpy(s + strlen(s), s2);

// 追加 "hello" 到 s 尾部

char hello[] = "hello";

char *news;

size_t lens = s ? strlen(s) : 0;

news = (char *)realloc(s, (lens + sizeof(hello) + 1) * sizeof(char));

if (!news)

error("out of memory");

s = news;

memcpy(s + lens, hello, sizeof(hello));

D 的方式

D 为 char 和 wchar 数组分别重载了‘~’和‘~=’运算符用于连接和追加:

char[] s1;

char[] s2;

char[] s;

s = s1 ~ s2;

s ~= "hello";

格式化打印

C 的方式

printf() 是通用的格式化打印例程:

#include <stdio.h>

printf("Calling all cars %d times!\n", ntimes);

D 的方式

我们还能说什么呢?这里还是 printf() 的天下:

import stdio;

printf("Calling all cars %d times!\n", ntimes);

(译注:其实现在标准库 phobos 中已经有了 write 和 writeln 这两个函数,但是尚未定型。)

函数的前向引用

C 的方式

不能引用尚未声明的函数。因此,如果要调用源文件中尚未出现的函数,就必须在调用之前插入函数声明。

void forwardfunc();

void myfunc()

{

forwardfunc();

}

void forwardfunc()

{

...

}

D 的方式

程序被看作一个整体,所以没有必要编写前向声明,而且这也是不允许的!D 避免了编写前向函数声明的繁琐和由于重复编写前向函数声明而造成的错误。函数可以按照任何顺序定义。

void myfunc()

{

forwardfunc();

}

void forwardfunc()

{

...

}

无参数的函数

C 的方式 void function(void);

D 的方式

D 是强类型语言,所以没有必要显式地说明一个函数没有参数,只需在声明时不写参数即可。

void function()

{

...

}

带标号的 break 和 continue

C 的方式

break 和 continue 只用于嵌套中最内层的循环或 switch 结构,所以必须使用 goto 实现多层的 break:

for (i = 0; i < 10; i++)

{

for (j = 0; j < 10; j++)

{

if (j == 3)

goto Louter;

if (j == 4)

goto L2;

}

L2:

;

}

Louter:

;

D 的方式

break 和 continue 语句后可以带有标号。该标号是循环或 switch 结构外围的,break 用于退出该循环。

Louter:

for (i = 0; i < 10; i++)

{

for (j = 0; j < 10; j++)

{

if (j == 3)

break Louter;

if (j == 4)

continue Louter;

}

}

// break Louter 跳转到这里

Goto 语句

C 的方式

饱受批评的 goto 语句是专业 C 程序员的一个重要工具。有时,这是对控制流语句的必要补充。

D 的方式

许多 C 方式的 goto 语句可以使用 D 中的标号 break 和 continue 语句替代。但是 D 对于实际的程序员来说是一门实际的语言,他们知道什么时候应该打破规则。所以,D 当然支持 goto !

结构标记名字空间

C 的方式

每次都要将 struct 关键字写在结构类型名之前简直是烦人透顶,所以习惯的用法是:

typedef struct ABC { ... } ABC;

D 的方式

结构标记名字不再位于单独的名字空间,它们同普通的名字共享一个名字空间。因此:

struct ABC { ... };

查找字符串

C 的方式

给定一个字符串,将其同一系列可能的值逐个比较,如果匹配就施行某种动作。该方法的典型应用要数命令行参数处理。

#include <string.h>

void dostring(char *s)

{

enum Strings { Hello, Goodbye, Maybe, Max };

static char *table[] = { "hello", "goodbye", "maybe" };

int i;

for (i = 0; i < Max; i++)

{

if (strcmp(s, table[i]) == 0)

break;

}

switch (i)

{

case Hello: ...

case Goodbye: ...

case Maybe: ...

default: ...

}

}

该方法的问题是需要维护三个并行的数据结构:枚举、表和 switch-case 结构。如果有很多的值,维护这三种数据结构之间的对应关系就不那么容易了,所以这种情形就成了孕育 bug 的温床。另外,如果值的数目很大,相对于简单的线性查找,采用二叉查找或者散列表会极大地提升性能。但是它们需要更多时间进行编码,并且调试难度也更大。典型地,人们会简单地放弃实现这些高效而复杂的数据结构。

D 的方式

D 扩展了 switch 语句的概念,现在它能像处理数字一样处理字符串。所以,字符串查找的实现变得直接:

void dostring(char[] s)

{

switch (s)

{

case "hello": ...

case "goodbye": ...

case "maybe": ...

default: ...

}

}

添加新的 case 子句也变得容易起来。可以由编译器为其生成一种快速的查找方案,这样也就避免了由于手工编码而消耗的时间及引入的 bug 。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有