龙空技术网

程序员教你学C语言(十八)

轻松学C语言 330

前言:

如今你们对“c语言是c的子集”大致比较看重,同学们都想要剖析一些“c语言是c的子集”的相关内容。那么小编同时在网络上搜集了一些关于“c语言是c的子集””的相关内容,希望朋友们能喜欢,大家快快来学习一下吧!

很多小伙伴都老是会碰到疑问,其实还是基础没打扎实,这些题如果你不看答案你能知道多少呢?如果还有很多不知道就证明基础没打扎实,如果你还在入门纠结,如果你还在苦恼怎么入门!小编有个建议,可以加小编弄的一个C语言交流基地,大家可以进入交流基地:379249575,里面新手入门资料,可以说从零到项目实战,都是可以免费获取的,还有程序员大牛为各位免费解答问题,热心肠的小伙伴也是蛮多的。不失为是一个交流的的好地方,小编在这里邀请大家加入我的大家庭。欢迎你的到来。一起交流学习!共同进步!小编等你!还有前面没有看的同学最好从程序员教你学C语言(一)开始看哦,尤其是基础还没打扎实的同学!

第二个知识点是枚举类型enum,如果一个变量你需要几种可能存在的值,那么就可以被定义成为枚举类型。之所以叫枚举就是说将变量或者叫对象可能存在的情况也可以说是可能的值一一例举出来。枚举类型定义的一般形式为:enum 枚举名{ 枚举值表 };

利用枚举,可以定义一个新的整数类型,该类型变量的值域是我们指定的枚举值表,下面的语句定义了一个枚举类型Weekday:

enum Weekday{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};

这个语句定义的是一个类型Weekday,而不是变量。Weekday类型的变量的值可以是类型名称后面大括号中的名称指定的任意值。这些名称叫做枚举常量,每个枚举值都用我们赋予的唯一名称来指定。而且枚举是一个整数类型,指定的枚举常量对应不同的整数值,这些整数默认从0开始,每个枚举常量的值都比它之前的枚举常量大1。在上面这个例子中,Monday到Sunday对应0~6。可以声明Weekday类型的新变量,并初始化它,如下:

enum Weekday today = Wednesday;其实我们就相当于把2赋值给了today,需要注意的是,我们可以给任意的枚举常量指定自己的整数值,比如

enum Weekday{Monday=1,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday};

这样Monday到Sunday的对应值是1~7。再比如:

enum Weekday{Monday=5,Tuesday=4,Wednesday,Thursday=10,Friday=3,Saturday,Sunday};

这样的话,没被指定整数的Wednesday、Saturday和Sunday它们的值又是多少呢,其实只需要知道前面的一条规则就可以了:每个枚举常量的值都比它之前的枚举常量大1。所以Wednesday=5,Saturday=4,Sunday=5。不信的话你可以试试运行下面的程序:

# include <stdio.h>

void main()

{

enum

Weekday{Monday=5,Tuesday=4,Wednesday,Thursday=10,Friday=3,Saturday,Sunday};

printf("Wednesday=%d,Saturday=%d,Sunday=%d\n", Wednesday,Saturday,Sunday);

}

可能有人觉得枚举类型没什么用,其实为一组整数常量取一组合理的名字是有用的,我之前在开发一款卡牌游戏时就用到,当时我要为不同类型的效果设置不同的常量,这时候我就用不同意义的名字放到一个枚举类型里,比如fire代表改卡牌会照成火焰伤害效果,heal代表回复生命效果等等,我总不可能用021、101这样的数据去表示不同类型的效果吧,这样时间长了我自己都不知道它们究竟能代表什么鬼了。

第三个知识点是goto语句,又叫做无条件转移语句,我们在讲条件语句时就应该讲到了,只不过它实在是用的太少了,其一般格式如下: goto 语句标号;其中标号是一个有效的标识符,这个标识符加上一个“:”一起出现在函数内某处,执行goto语句后,程序将跳转到该标号处并执行其后的语句。

另外标号必须与goto语句同处于一个函数中,但可以不在一个循环层中。通常goto语句与if条件语句连用,当满足某一条件时,程序跳到标号处运行。我们来看具体的程序:

#include<stdio.h>

void main()

{

int i = 1;

cmp:

if(i<5)

{

printf("i=%d\n",i++);

goto cmp;

}

运行结果为:

i=1

i=2

i=3

i=4

我们可以看到我们用if和goto语句实现了循环的效果,具体的流程是首先我们在if语句前定义了cmp:这样一个标号,if为真时,我们进入if语句块,每次打印i的结果并进行i++,紧着我们使用goto跳到cmp标号那里,接着再次判断if的真假,如此反复,直到if为假。

其实讲goto并不是因为它很重要,而是它带来的思考,它让我们看到使用条件语句替代循环的可能,那是不是其他语句也可以被替代。或许我们可以思考的更深一点,世界上那些种类繁多的编程语言究竟从何而来,要说到编程语言,就要讲到编译器,每种语言都有自己的编译器,当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于JVM或者说是用Java实现的,IronPython等是基于.NET实现的,但是Java和C#等本身也要依靠C/C++来实现,等于是间接调用了C,不仅如此,大多数编程语言都或多或少继承了C语言的一些特点。那么问题来了,如果大家都用C语言或基于C语言的语言来写编译器,那么世界上第一个C语言编译器又是怎么编写的呢?

早期的程序员采取了一个取巧的办法:先用汇编语言编写一个C语言的一个子集的编译器,再通过这个子集去递推完成完整的C语言编译器。详细的过程如下:

先创造一个只有C语言最基本功能的子集,记作C0语言,C0语言已经足够简单了,可以直接用汇编语言编写出C0的编译器。依靠C0已有的功能,设计比C0复杂,但仍然不完整的C语言的又一个子集C1语言,其中C0属于C1,C1属于C,用C0开发出C1语言的编译器。在C1的基础上设计C语言的又一个子集C2语言,C2语言比C1复杂,但是仍然不是完整的C语言,开发出C2语言的编译器……如此直到CN,CN已经足够强大了,这时候就足够开发出完整的C语言编译器的实现了。可以用图说明这个抽象过程:

这和滚雪球是一个道理,用手(汇编语言)把一小把雪结合在一起,一点点地滚下去就形成了一个大雪球,这大概就是所谓的0生1,1生C,C生万物吧!

那这种思想是怎么来的呢,这当然不是我们程序员前辈的神来之笔,它很大程度上得益于数学里"分解"的思想,也是我们学C语言最重要的思想。"分解"粗略的可以分为三步,第一步是去掉一个系统的非核心部分,所以在这步你需要明白哪些是一个系统的核心部分,哪些仅仅是添砖加瓦的部分;第二步是把系统的核心部分划分成若干个模块,把功能相同或相近的部分放进同一个模块;第三步则只需要实现每个模块里的一个部分,其他部分都可以去掉,因为只需要模块里的这一个部分就能很容易实现功能相似的其他部分。

我们来看看我们之前就列举出来的C89里的32个关键字:

1.数据类型关键字(12个):

char、double、enum、float、int、long、short、signed、struct、union、unsigned、void

2.控制语句关键字(12个):

for、do、while、break、continue、if、else、goto、switch、case、default、return

3.存储类型关键字(4个):

auto、extern、register、static

4.其它关键字(4个):

const、sizeof、typedef、volatile

首先是第一步:仔细观察,这里面很多关键字是用来限定变量作用域、生存周期或者是帮助编译器优化的,它们属于非核心部分,这些部分在编译器实现的初期根本不必加上,所以我们可以把存储类型关键字和其他关键字共8个全部去掉。这样就形成了C的子集,CN语言,CN语言的关键字如下:

1.数据类型关键字(12个):

char、double、enum、float、int、long、short、signed、struct、union、unsigned、void

2.控制语句关键字(12个):

for、do、while、break、continue、if、else、goto、switch、case、default、return

然后我们来划分不同的模块,

整数:char、int、long、short、signed、unsigned

浮点数:float、double

复合数据结构:enum, struct, union

控制语句:for、do、while、break、continue、if、else、goto、switch、case、default、return

对于整数关键字,我们只需要实现int就可以了,因为其他的类型的关键字本质上都是int,只需要到时候进行字节大小的扩充或者精简就好了,去掉其他部分后我们可以得到C3语言:

整数:int

浮点数:float、double

复合数据结构:enum, struct, union

控制语句:for、do、while、break、continue、if、else、goto、switch、case、default、return

对于浮点数,我们只需要实现double即可,float只不过字节大小和精度不同于double,有了double就能很容易实现float,所以可以得到C2语言:

整数:int

浮点数:double

复合数据结构:enum, struct, union

控制语句:for、do、while、break、continue、if、else、goto、switch、case、default、return

对于复合型数据结构,它们其实也是基于基本数据类型,而且主要注意的是,我们的C语言不仅包括关键字,还包括运算符,运算符也可以使用我们的分解思想一步步简化,在C语言里的符合运算符++、--、->这种过于灵活的表达方式此时都可以删掉,我们这里的符合数据结构也都可以去掉。这样我们就得到了C1语言的关键字:

整数:int

浮点数:double

控制语句:for、do、while、break、continue、if、else、goto、switch、case、default、return

我们这里的循环可以简化成一种,循环语句有while循环,do…while循环和for循环,只需要保留while循环就够了;分支语句又有if…{}, if…{}…else, if…{}…else if…, switch,这四种形式,它们都可以通过两个以上的if…{}来实现,因此只需要保留if,…{}就够了。如果再细想一下,所谓的分支和循环不过是条件跳转语句罢了,而且我们前面就用goto实现了循环,函数调用语句也不过是一个压栈和跳转语句罢了,因此只需要goto就够了。我们大胆去掉所有结构化关键字,连函数也没有,得到的C0语言关键字如下:

int、double、goto、break、void

只有5个关键字int、double、goto、break、void,已经完全可以用汇编语言快速的实现了。通过逆向分析我们还原了第一个C语言编译器的编写过程,有了这个最基础的C0语言,我们就可以逐步往上推,得到越来越强大的CN语言,最后得到C语言,另外我们可以用C语言实现C++的全部功能,0生1,1生C,C生万物,实在精妙!我在前面的共享链接里上传了一个C语言的面向对象源码,作者使用的是结构体+函数指针+宏来实现的面向对象,不过有的人不喜欢用宏,直接用结构体+指针也可以实现面向对象。当然,这些都属于扩展知识,有兴趣有能力的可以去掌握,初学者就当扩充知识面了。

END。

标签: #c语言是c的子集