第2章 C程序的基本组成
2.1 认识C语言与LW_OOPC语言
2.1.1 C语言的身世背景
20世纪70年代,D. Richie及K. Thompson任职于美国贝尔实验室(Bell Labs)。当时Thompson正在设计Unix操作系统,而Richie负责设计新语言来编写Unix程序。新语言继承了Thompson原来设计的B语言,因此,称之为C语言。后来,Unix平步青云,成为标准操作系统,C随即水涨船高,受人青睐。
Unix以前的操作系统,皆以“汇编语言”(Assembly Language)编写。C问世后,逐渐取代了汇编语言的角色。Unix、DOS、Windows、OS/2等操作系统皆以C写成。到了2008年的今天,许多软件系统,如Google推出的Android手机平台,其内核模块仍是C语言的杰作呢!
C语言能获得极大的成功,D. Richie认为有五项主要因素:
1. C语言既不够优雅,也不够正式;
2. C语言符合程序员的需要,并且到处可见支持C语言的程序开发环境;
3. 众多人持续加以修改,与大环境保持交互;
4. 一开始把焦点设定在操作系统的开发是相当正确的,所谓好的开始是成功的一半;
5. 运气好。
2.1.2 C影响C++、Java、C#等语言文化
Java是由SUN公司的James Gosling率领的小组开发出来的。本来该小组的任务是要开发可以控制小家电、交互电视等的编程语言,之后因缘际会,与当时逐渐流行的Internet技术结合,在1995年发布并取名为Java。
Gosling在学生时代就深受C语言的洗礼,他在C.M.U.攻读博士学位的时候,曾经开发了一个C语言版本的Emacs,因而在Unix领域享有盛名。
Java从原本定位为家电控制语言,到后来成为Internet上热门的编程语言,在其发展过程中,从C++那里借鉴了许多宝贵的经验。例如效仿C++的标准程序库,推出一套完善的Java标准程序库;另外加上网络应用,以及最棘手的安全机制。至今,Java仍是Internet应用上最流行的编程语言(见图2-1)。
图2-1
到了2000年以后,Microsoft又从C++派生出C#作为与Java分庭抗礼的编程语言。之后连VB.NET都沿袭了C++的编译引擎设计,从而大幅提升了VB.NET的执行效率,并提供完整的OOP功能。
2.1.3 用C语言编写面向对象(Object-Oriented)程序
何谓OOPC,OOPC是指OOP(Object-Oriented Programming)与C语言结合,藉由C语言的Macro指令定义出OOP概念的关键字(Key Word)。这样,C程序员就能运用这些关键字来表达OOP的概念,如类、对象、信息、继承、接口等。
虽然OOPC程序的语法不像C++那么简洁,但是OOPC也有其亮丽的特色,就是编译后的程序所占的内存空间(Size)比C++程序小,能够满足像Embedded System等的内存限制,程序员也能有效地调整程序的瓶颈,从而提升其执行效率。
C++的功能齐全,但是在一般的嵌入式系统(Embedded System)开发上,经常只用到其中的一小部分功能,而不需要用到全部的机制,例如多重继承(Multiple Inheritance)、运算符的重载(Overloading)等。此时,许多嵌入式系统的开发者就舍弃C++的庞大身躯而回归到精简的C环境里。只是开发者舍弃C++的同时也舍弃了珍贵的OOP能力,实在太可惜了。
OOPC就是要弥补这个缺憾的。它是以C的宏写成的Header文件,可以任由C程序员对它瘦身美容,删去不需要的部分,挑出自己所需要的OOP特性,以小而美的身材满足嵌入式系统开发的需要。
以LW_OOPC为例
LW_OOPC是一种既轻便又快速的面向对象的C语言。做嵌入式开发的程序员还是比较青睐C语言的,只是C语言没有对象、类等概念,程序很容易变成意大利面型的结构,维护上比较费力。在1986年C++上市时,开发者希望大家改用C++,但是C++的效率不如C,因而不受嵌入式系统开发的程序员的喜爱。于是,MISOO团队便设计了一个既轻便又高效的OOPC语言。轻便的意思是它只用了约20个C宏语句而已,简单易学。其宏如下:
/* lw_oopc.h */ /* 这就是MISOO团队所设计的C宏 */ #include "malloc.h" #ifndef LOOPC_H #define LOOPC_H #define CLASS(type)\ typedef struct type type; \ struct type #define CTOR(type) \ void* type##New() \ { \ struct type *t; \ t = (struct type *)malloc(sizeof(struct type)); } #define CTOR2(type, type2) \ void* type2##New() \ { \ struct type *t; \ t = (struct type *)malloc(sizeof(struct type)); } #define END_CTOR return (void*)t; #define FUNCTION_SETTING(f1, f2) t->f1 = f2; #define IMPLEMENTS(type) struct type type #define INTERFACE(type) struct type #endif /* end */
高效的意思是它没提供类继承,内部没有虚函数表(Virtual Function Table),所以仍保持着原来C语言的高效率。除了没有继承机制之外,它提供了类、对象、信息传递、接口和接口多态等常用的机制。目前受到不少C程序员的喜爱。
2.1.4 面向对象概念让UML与C携手合作
简单示例:从UML到OOPC程序
UML来自OO(Object-Oriented)领域,通过OO概念,善加运用UML,能让C程序员更上一层楼。由于UML也能创建硬件系统的模型,愈来愈多的嵌入式系统公司藉由UML来表达软件与硬件的分析与设计。
由于一般C语言并没有使用OO概念,因此将UML设计图实现为C程序的过程让人感到不顺畅。所以,MISOO团队将OO概念添加到C语言,让人们既能以OO概念去分析和设计,也能以OO概念去编写C程序。这样一来,从分析、设计到程序编写的过程就非常直截了当了。如下述的步骤。
Step-1 分析出一个类叫Light,它提供两项服务,以UML表达(见图2-2)。
图2-2
Step-2 实现为LW_OOPC程序。
基于上述的lw_oopc.h就可以定义出类了,例如定义一个Light类,其light.h的内容为:
/* light.h */ #include "lw_oopc.h" CLASS(Light) { void (*turnOn)(); void (*turnOff)(); };
类里的函数定义格式为:
返回值的类型 (*函数名称)();
类定义好了,就开始编写函数的实现内容,代码如下:
/* light.c */ #include "stdio.h" #include "light.h" static void turnOn() { printf("Light is ON\n"); } static void turnOff() { printf("Light is OFF\n"); } CTOR(Light) FUNCTION_SETTING(turnOn, turnOn) FUNCTION_SETTING(turnOff, turnOff) END_CTOR
这个FUNCTION_SETTING(turnOn, turnOn)宏的用意是:让类定义(.h文件)函数名称能够与实现的函数名称不同。例如在light.c里可写为:
static void TurnLightOn() { ….. } CTOR(Light) { FUNCTION_SETTING(turnOn, TurnLightOn); ….. }
这是创造.c文件自由替换的空间,是实现接口的重要基础。最后看看如何编写主程序:
/* tc_ex01.c */ #include "stdio.h" #include "lw_oopc.h" #include "light.h" extern void* LightNew(); void main() { Light* light = (Light*)LightNew(); light->turnOn(); light->turnOff(); getchar(); return; }
LightNew()是由CTOR宏所生成的类构造器(Constructor)。由于它定义于别的文件,所以必须加上extern void* LightNew();指令。生成对象的基本格式为:
类名称* 对象指针 = (类名称*)类名称New();
示例:Light* light=(Light*)LightNew()
然后就能通过对象指针去调用成员函数了。
使用Turbo C 2.0开发环境
创建一个项目文件名叫PRJ_EX01.PRJ,并输入内容,如图2-3所示。
图2-3
按ALT+R执行,则输出如图2-4所示的执行结果。
图2-4