1.2 基于Turbo C 2.0环境的评估
在Turbo C 2.0环境里,C程序加上美好的面向对象机制,需要付出多少代价呢?答案是一个类增加0.5KB(即400 Bytes)左右,即使你的程序含有10个类(这已经是大系统了),也不过增加5KB左右。以下就以Turbo C 2.0 IDE的评估来说明这是一项很划算的策略。
1.2.1 加入第1个类
在C程序里加入第1个类时,你的程序大小会增加约0.5KB。如果继续加入,每增加一个类,会增加约0.3KB ~0.5KB。一般而言,如果系统环境里搭配有RTOS(Real-Time Operating System),通常不会斤斤计较500B的空间,本书就是针对这类环境而写的。现在先测量一个没有类的程序,代码如下所示:
/* CX01-01.C */ #include "stdio.h" static void turnOn() { printf("Light is ON\n"); } static void turnOff() { printf("Light is OFF\n"); } /*----------------------------------*/ int main() { turnOn(); turnOff(); getchar(); return 0; }
这是一个传统的Turbo C程序,并没有搭配LW_OOPC机制,此程序生成一个CX01-AP1.EXE的可执行文件,其大小如图1-1所示。
图1-1
这个程序在Turbo C 2.0 IDE上编译及连接之后,可执行文件的大小为10.4KB,我们将以这个范例来作为评估的基准,来算出增加第1个类必须付出的代价。现在就来配上LW_OOPC并规划一个Light类,代码如下所示。
定义Light类
/* light.h */ #include "lw_oopc.h" CLASS(Light) { void (*turnOn)(); void (*turnOff)(); };
实现Light类
/* 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
编写main()主函数
/* 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; }
建立一个Turbo C的TC_EX01.Prj文件
light.c tc_ex01.c
此程序生成一个TC_EX01.EXE可执行文件,其大小如图1-2所示。
图1-2
这个程序在Turbo C 2.0 IDE上编译及连接之后,可执行文件的大小为10.9KB。
与前面CX01-AP1.EXE相比,增加了0.5KB。这是加入第1个类(即Light类)所付出的代价。当你看到这个OOPC程序时,可能对CLASS、CTOR等宏很好奇。不过,在本章里,我们只把焦点放在OOPC机制对程序大小的影响上,后续各章会对这些宏逐一说明。
1.2.2 加入第2个类
为了测量第2个类对程序大小的影响,我们把上述范例加以扩充,增加了三个一般函数,先测量此程序的大小,然后将此函数纳入第2个类里,最后再测量程序的大小,进行比较。现在就来加入三个函数,代码如下所示。
定义及实现三个函数
/* control.c */ #include "stdio.h" #include "light.h" static Light *pl; void init() { pl = (Light*)LightNew(); } void turnOn() { pl->turnOn(); } void turnOff() { pl->turnOff(); }
编写main()主函数
/* cx01-01.c */ #include "stdio.h" #include "lw_oopc.h" extern void init(); extern void turnOn(); extern void turnOff(); void main() { init(); turnOn(); turnOff(); getchar(); return; }
建立一个Turbo C的TC_EX02.Prj文件
light.c control.c tc_ex02.c
此程序生成一个TC_EX02.EXE可执行文件,其大小如图1-3所示。
图1-3
此程序大小为11.2KB,比上一个程序增加了0.3KB,这不是由增加类而引起的,而是由增加init()等三个函数所引起的。我们先以此程序大小为准,然后将这三个函数纳入类里,再测量其大小,就可以看出新增类得付出多少代价了。现在就将其纳入新类(称为CTRL)里,代码如下所示。
定义CTRL类
/* ctrl.h */ #include "lw_oopc.h" #include "light.h" CLASS(CTRL) { void (*init)(CTRL*); void (*turnOn)(CTRL*); void (*turnOff)(CTRL*); Light *pl; };
实现CTRL类
/* ctrl.c */ #include "stdio.h" #include "ctrl.h" extern void* LightNew(); static void init(CTRL *t) { t->pl = (Light*)LightNew(); } static void turnOn(CTRL *t) { t->pl->turnOn(t->pl); } static void turnOff(CTRL *t) { t->pl->turnOff(t->pl); } CTOR(CTRL) FUNCTION_SETTING(init, init) FUNCTION_SETTING(turnOn, turnOn) FUNCTION_SETTING(turnOff, turnOff) END_CTOR
编写main()主函数
/* tc_ex02.c */ #include "stdio.h" #include "lw_oopc.h" #include "ctrl.h" extern void* CTRLNew(); void main() { CTRL* ctrl = (CTRL*)CTRLNew(); ctrl->init(ctrl); ctrl->turnOn(ctrl); ctrl->turnOff(ctrl); getchar(); return; }
建立一个Turbo C的TC_EX03.Prj文件
light.c ctrl.c tc_ex03.c
此程序生成一个TC_EX03.EXE可执行文件,其大小如图1-4所示。
图1-4
此程序大小为11.9KB,与前面TC_EX02.EXE相比,增加了0.7KB。这是加入第2个类(即CTRL类)的代价。
1.2.3 加入第3个类
为了测量第3个类对程序大小的影响,我们把上述范例加以扩充,加了两个函数,先测量此程序的大小;之后将此函数纳入第3个类里,再测量程序的大小,进行比较。现在就来加入两个函数,代码如下所示。
定义及实现两个函数
/* sw.c */ #include "stdio.h" void swDown() { printf("SW is Down\n"); } void swUp() { printf("SW is Up\n"); }
撰写main()主函数
/* tc_ex04.c */ #include "stdio.h" #include "lw_oopc.h" #include "ctrl.h" extern void swDown(); extern void swUp(); void main() { CTRL* ctrl = (CTRL*)CTRLNew(); ctrl->init(ctrl); ctrl->turnOn(ctrl); swDown(); ctrl->turnOff(ctrl); swUp(); getchar(); return; }
建立一个Turbo C的TC_EX04.Prj文件
light.c ctrl.c sw.c tc_ex04.c
此程序产生一个TC_EX04.EXE执行文件,其大小如图1-5所示。
图1-5
此程序大小为12.0KB。这比上一个程序增加了0.1KB,这其实不是由增加类而引起的,而是由增加swDown()等函数所影响的。我们先以此程序大小为准,然后将这两个函数纳入类里,再测量其大小,就可以看出新增类得付出多少代价了。现在就将其纳入新类(称为DoorSwitch)里,代码如下所示。
定义DoorSwitch类
/* DoorSwitch.h */ #include "lw_oopc.h" CLASS(DoorSwitch) { void (*swDown)(); void (*swUp)(); };
实现DoorSwitch类
/* DoorSwitch.c */ #include "stdio.h" #include "switch.h" static void swDown() { printf("SW is Down\n"); } static void swUp() { printf("SW is Up\n"); } CTOR(DoorSwitch) FUNCTION_SETTING(swDown, swDown) FUNCTION_SETTING(swUp, swUp) END_CTOR
撰写main()主函数
/* tc_ex05.c */ #include "stdio.h" #include "lw_oopc.h" #include "ctrl.h" #include "switch.h" void main() { CTRL* ctrl = CTRLNew(); DoorSwitch *psw = DoorSwitchNew(); ctrl->init(ctrl); ctrl->turnOn(ctrl); psw->swDown(); ctrl->turnOff(ctrl); 78psw->swUp(); getchar(); return; }
建立一个Turbo C的TC_EX05.Prj文件
light.c ctrl.c switch.c tc_ex05.c
此程序产生一个TC_EX05.EXE执行文件,如图1-6所示。
此程序大小为12.4KB。与前面TC_EX04.EXE相比,增加了0.4KB。这是加入第3个类(即DoorSwitch类)的代价。
图1-6
1.2.4 加入第4个类
为了测量第4个类对程序大小的影响,我们把上述范例加以扩充,加了两个函数,先测量此程序的大小;之后将此函数纳入第4个类里,再测量程序的大小,进行比较。现在就来加入两个函数,代码如下所示。
撰写两个新函数及main()主函数
/* tc_ex06.c */ #include "stdio.h" #include "lw_oopc.h" #include "ctrl.h" #include "switch.h" double length, width; void init() { length = 10.5; width = 5.125; } void print_perimeter() { printf("P = %f\n", 2*(length + width)); } void main() { CTRL* ctrl = (CTRL*)CTRLNew(); DoorSwitch *psw = (DoorSwitch*)DoorSwitchNew(); ctrl->init(ctrl); ctrl->turnOn(ctrl); psw->swDown(); ctrl->turnOff(ctrl); psw->swUp(); init(); print_perimeter(); getchar(); return; }
建立一个Turbo C的TC_EX06.Prj文件
light.c ctrl.c switch.c tc_ex06.c
此程序产生一个TC_EX06.EXE执行文件,如图1-7所示。
图1-7
此程序大小为26.5KB。这比上一个程序增加了14.1KB,这不是由增加类而引起的,而是由增加print_perimeter()等函数所影响的。我们先以此程序大小为准,然后将这两个函数纳入类里,再测量其大小,就可以看出新增类得付出多少代价了。现在就将其纳入新类(称为Rectangle)里,代码如下所示。
撰写Rectangle新类及main()主函数
/* tc_ex07.c */ #include "stdio.h" #include "lw_oopc.h" #include "ctrl.h" #include "switch.h" CLASS(Rectangle) { void (*init)(Rectangle*); void (*print_perimeter)(Rectangle*); double length, width; }; static void init(Rectangle *t) { t->length = 10.5; t->width = 5.125; } static void pr_perimeter(Rectangle *t) { printf("P = %f\n", 2*(t->length + t->width)); } CTOR(Rectangle) FUNCTION_SETTING(init, init) FUNCTION_SETTING(print_perimeter, pr_perimeter) END_CTOR /* --------------------------------------------------------------------- */ void main() { CTRL* ctrl = (CTRL*)CTRLNew(); DoorSwitch *psw = (DoorSwitch*)DoorSwitchNew(); Rectangle *rect = (Rectangle*)RectangleNew(); ctrl->init(ctrl); ctrl->turnOn(ctrl); psw->swDown(); ctrl->turnOff(ctrl); psw->swUp(); rect->init(rect); rect->print_perimeter(rect); getchar(); return; }
建立一个Turbo C的TC_EX07.Prj文件
light.c ctrl.c switch.c tc_ex07.c
此程序产生一个TC_EX07.EXE执行文件,如图1-8所示。
图1-8
此程序大小为26.8KB。与前面TC_EX06.EXE相比,增加了0.3KB。这是加入第4个类(即Rectangle类)的代价。
1.2.5 评估统计图
以上总共加入了四个类,如果你继续加入更多类时,会发现每一个类大约都增加0.3KB~0.5KB左右。如图1-9所示。
综合上述的评估,兹说明如下:
● 由于LW_OOPC是由C语言的宏(Macro)所建立的,在编译之前就已经全部被转为一般非对象化的C程序了。所以,类能与类外的一般函数并存于你的程序里。这意味着,你可以视你的内存资源的宽裕情形而决定写入多少个类,并非要全部类化(即对象化)不可。因此,程序大小与执行速度并不是决定要不要采用LW_OOPC类机制的关键因素。反而,如何藉由LW_OOPC改善嵌入式程序架构,以提升系统的稳定性和可靠性才是采用LW_OOPC的最主要诱因。因为程序员能自由地决定程序的形式,一方面满足硬件资源的限制,另一方面又能追求最顶级的美好结构。因此,对一个C程序员及其撰写的嵌入式软件而言,LW_OOPC只是给他(或它)黄袍加身,并不会伤害到龙体本身。
● 从图1-9中可以看到,如果你的TurboC程序使用LW_OOPC提供的机制,而且写了n个类时,其程序大小的增加量大约为n * 0.5KB。如果写了10个精简型的类(即n的值为10),大约需付出5KB的代价。一般而言,C程序能写到10个类已经算是复杂的了,只付出不到5KB代价,却能让复杂的程序得到美好结构和高度稳定性,真是非常划算的策略。
图1-9