int 和 枚举类型enum的转换 in c++

        最近在将c源文件改成cpp时,编译时出现很多的错误,对于不同类型间的转换c编译器没问题的,但在c++编译器里可能就报错,如将int类型直接赋给enum类型,将会出现类似这种错误:

         我们知道在c中,enum类型默认是int类型,它们之间可以自动转换,但在c++中却不是这样的。可以参考:https://isocpp.org/wiki/faq/newbie#enumeration-is-its-own-type

截取如下:

 c++编译器支持从enum类型自动转换为int,但反过来是不支持的。需要进行强制转换,有代码如下:

#include <stdio.h>
#include <stdlib.h>

enum type 
{
	TYPE_1 = 0,
	TYPE_2,
	TYPE_3,
	Type_4
};

enum type2
{
	TYPE2_1 = 0,
	TYPE2_2,
	TYPE2_3,
	TYPE2_4,
	TYPE2_5
};

int main()
{
	enum type t;
	t = type(2); //强制转换,也可以这样t = (type)2;
	printf("t = %d\n", t);

	t = type(TYPE2_5); //不的同枚举类型间转换也需要强制转换
	int a = TYPE2_4; //自动转换,没有问题
	printf("t = %d, a = %d\n", t, a);
	return 0;
}

 只要在目标enum范围内,转换是没有问题的,但是否有意义需要根据代码实际看。

上面说到enum类型的范围,它默认是int类型,所以enum的取值范围就是int的取值范围了,如:

#include <stdio.h>
#include <stdlib.h>


enum type
{
	TYPE_1 = 0,
	TYPE_2,
	TYPE_3,
	Type_4,
	TYPE_MAX = 255
};

int main()
{
	enum type t;
	t = (type)(0x7FFFFFFF); //取最大值,最高位是符号位为0,即为正数最大值
	printf("t = %d\n", t);
	
	return 0;
}

 若是t = (type)(0x7FFFFFFF + 1); 会怎么样呢?

 表达式中的整数溢出。所以enum默认取值范围就是int的取值范围。

在c++11中,enum可以指定类型来限制enum的取值范围,而不是默认的Int类型,如:

#include <stdio.h>
#include <stdlib.h>

typedef unsigned char uint8;
enum type: uint8
{
	TYPE_1 = 0,
	TYPE_2,
	TYPE_3,
	Type_4,
	TYPE_MAX = 255 //最大值,大于这个值是编译不过的
};

int main()
{
	enum type t;
	t = (type)(256);
	printf("t = %d\n", t);

	return 0;
}

 为什么结果为 0 呢?因为我们定义enum时限制了其最大值(unsigned char的最大值)。

我们看计算器上256的二进制表示为:

 enum只取前8位,所以为0。

如果给enum赋负数呢,结果又是多少呢?

若:t = (type)(-10);

 一样的道理,t 只会取低8位的值。那就取决于-10的低8位是什么了。因为负数的存储是以补码存的,而负数的补码=反码+1,反码=除符号位外其余位取反,我们先看-10的原码为:1000 1010,其反码为:1111 0101(除最高符号位外,其余位取反), 补码为:1111 0110(1111 0101 + 1的结果),那二进制1111 0110表示的数值是多少呢?因为我们定义enum时限制为unsigned char类型,所以最高位也表示数据而不是符号位,即:

其他类型如 unsigned short, unsigned int 有兴趣的同学可以自行验证。

  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
适合新手的教程,我自己也在学,以下是部分学习笔记 69 函数指针 70 动态内存分配 Dynamic memory pointer = new type pointer = new type [elements] 第一个表达式用来给一个单元素的数据类型分配内存。第二个表达式用来给一个数组分配内存。 例如: int * bobby; bobby = new int [5]; if (bobby == NULL) { // error assigning memory. Take measures. }; delete pointer; delete [ ] pointer; 在C语言中,为了动态分配内存,我们必须求助于函数库stdlib.h 因为该函数库在C++中仍然有效,并且在一些现存的程序仍然使用,所以我们下面将学习一些关于这个函数库中的函数用法。 函数malloc void * malloc (size_t nbytes); char * cp; cp = (char *) malloc (10); int * bobby; bobby = (int *) malloc (5 * sizeof(int)); 这一小段代码将一个指向可存储5个int型整数的内存块的指针赋给bobby,它的实际长度可能是 2,4或更多字节 数,取决于程序是在什么操作系统下被编译的。 int * bobby; bobby = (int *) calloc (5, sizeof(int)); malloc 和calloc的另一点不同在于calloc 会将所有的元素初始化为0。 它被用来改变已经被分配给一个指针的内存的长度。 void * realloc (void * pointer, size_t size); 参数pointer 用来传递一个已经被分配内存的指针或一个空指针,而参数size 用来指明新的内存长度。这个函数 给指针分配size 字节的内存。这个函数可能需要改变内存块的地址以便能够分配足够的内存来满足新的长度要 求。在这种情况下,指针当前所指的内存中的数据内容将会被拷贝到新的地址中,以保证现存数据不会丢失。函 数返回新的指针地址。如果新的内存尺寸不能够被满足,函数将会返回一个空指针,但原来参数中的指针 pointer 及其内容保持不变。 函数 free 这个函数用来释放被前面malloc, calloc 或realloc所分配的内存块。 void free (void * pointer); 注意:这个函数只能被用来释放由函数malloc, calloc 和realloc所分配的空间。 74 数据结构 76-78 结构指针(Pointers to structures) -> 这是一个引用操作符,常与结构或类的指针一起使用,以便引用其中的成员元素,这样就避免使用很多括号。例如,我们用: pmovie->title 来代替: (*pmovie).title 79 自定义数据类型(User defined data types) typedef existing_type new_type_name; typedef char C; typedef unsigned int WORD; typedef char * string_t; typedef char field [50]; 80 联合(Union) union mytypes_t { char c; int i; float f; } mytypes; 81 枚举Enumerations (enum) enum model_name { value1, value2, value3, . . } object_name; 例如,我们可以定义一种新的变量类型叫做color_t 来存储不同的颜色: enum colors_t {black, blue, green, cyan, red, purple, yellow, white}; 注意在这个定义里我们没有使用任何基本数据类型。换句话说,我们创造了一种的新的数据类型,而它并没有基 于任何已存在的数据类型:类型color_t,花括号{}中包括了它的所有的可能取值。例如,在定义了colors_t 列举 类型后,我们可以使用以下表达式: 84 类(Class) 类(class)是一种将数据和函数组织在同一个结构里的逻辑方法。定义类的关键字为class ,其功能与C语言中的struct类似,不同之处 是class可以包含函数,而不像struct只能包含数据元素。 类定义的形式是: class class_name { permission_label_1: member1; permission_label_2: member2; ... } object_name; 其中 class_name 是类的名称 (用户自定义的类型) ,而可选项object_name 是一个或几个对象(object)标识。Class的声明体中包含 成员members,成员可以是数据或函数定义,同时也可以包括允许范围标志 permission labels,范围标志可以是以下三个关键字中 任意一个:private:, public: 或 protected:。它们分别代表以下含义: ● private :class的private成员,只有同一个class的其他成员或该class的“friend” class可以访问这些成员。 ● protected :class的protected成员,只有同一个class的其他成员,或该class的“friend” class,或该class的子类(derived classes) 可以访问这些成员。 ● public :class的public成员,任何可以看到这个class的地方都可以访问这些成员。 如果我们在定义一个class成员的时候没有声明其允许范围,这些成员将被默认为 private范围。 以下是怎样读前面例子中出现的一些指针和类操作符 (*, &, ., ->, [ ]): ● *x 读作: pointed by x (由x指向的) ● &x 读作: address of x(x的地址) ● x.y 读作: member y of object x (对象x的成员y) ● (*x).y 读作: member y of object pointed by x(由x指向的对象的成员y) ● x->y 读作: member y of object pointed by x (同上一个等价) ● x[0] 读作: first object pointed by x(由x指向的第一个对象) ● x[1] 读作: second object pointed by x(由x指向的第二个对象) ● x[n] 读作: (n+1)th object pointed by x(由x指向的第n+1个对象) 由关键字struct和union定义的类 类不仅可以用关键字class来定义,也可以用struct或union来定义。 因为在C++中类和数据结构的概念太相似了,所以这两个关键字struct和class的作用几乎是一样的(也就是说在C++中struct定义的 类也可以有成员函数,而不仅仅有数据成员)。两者定义的类的唯一区别在于由class定义的类所有成员的默认访问权限为private,而 struct定义的类所有成员默认访问权限为public。除此之外,两个关键字的作用是相同的。 union的概念与struct和class定义的类不同, 因为union在同一时间只能存储一个数据成员。但是由union定义的类也是可以有成员函 数的。union定义的类访问权限默认为public。 94 操作符重载(Overloading operators) + - * / = < > += -= *= /= << >> <<= >>= == != <= >= ++ -- % & ^ ! | ~ &= ^= |= && || %= [] () new delete 96 this指针 97 静态成员(静态变量、静态函数)(Static members) 静态成员类直接访问,不属于类对象的成员 98 类之间的关系(Relationships between classes) 友元函数(Friend functions) 101 类的继承 记住,this 代表代码正在被执行的这一个对象的指针。 142 函数模块 154 出错处理 (Exception handling) 159 类型转换高级 (Advacned Class Type-casting) 163 typeid 164 预处理指令 168 标准函数库
这上传的资源中包含一套我工作中常用的模板库,及不需要MFC支持的excel操作接口,导出函数调用栈(dump stack)接口,可以直接用VS2008运行TestCodeLib.sln来根据unit test来了解用法。 ⑴ 需求(requirements) 重量级的BOOST非常强大,但有时候项目中没有引入它,这时候我们需要自己的模板库。 BOOST is very powerful, but some projects have not include BOOST library. So we need out own template type trait library -- it is the responsibility of this lightweight library. 即使BOOST非常强大,但有些常用的功能其也没有,而经常性的代码中又需要这些功能。比如把运行期数据转换为元程序需要的编译期数据。 Even if BOOST is very powerful,it can't still meet all requirements. e.g. convert runtime data into compile period data needed by metaprogramming. /*************************************************************************************************************************************/ ⑵ 益处(advantage) 此泛型库抽象了一些常用的业务需求,可以避免大量的重复工作。 它是完全泛型的并且是类型安全的(没有强制类型转换),如果使用错误将导致编译失败,从而提高了正确率(正确性由编译器保证)。 这个库的很多模板类型推导不需要C++11的支持,这是一个大的优势(VS2010才开始支持C++11)。 this general library draws out some common business and avoid unnecessary repeat work. it is completed general and type-safe(without any type cast), mistake(s) will cause compile failure, so it improves correctness. In this library , type deduce need't C++11's support, it is big advantage. (VS2010 begin to support C++11) /*************************************************************************************************************************************/ ⑶ 用法(usage) 下载这个库后,使用VS打开.\CodeLib\testcase\TestCodeLib\TestCodeLib.sln,直接按F5启动,即可以看到许多单元测试的用法/测试用例的输出。 如果需要使用某功能,可以参考其对应的测试代码的用法。(每个功能文件.\CodeLib\include\MiniMPL\xxx.hpp,都对应一个测试文件.\CodeLib\testcase\MiniMPL\test_xxx.hpp) (这个库的使用及修改是完全自由的,只需要保留文件头中的注释即可) usage: download this library, open .\CodeLib\testcase\TestCodeLib\TestCodeLib.sln with VS,you can see many usage/test output of unit test. every feature has according unit test file, it shows its usage. e.g. .\CodeLib\include\MiniMPL\xxx.hpp has according ".\CodeLib\testcase\MiniMPL\test_xxx.hpp" this library is all free, the only requirement is that you need to keep the comments in header file. /*********************************************************************************************************************************************************/ ⑷ 本库提供的主要功能介绍: major feature in this lib: ◆ [typeTraits.hpp] ★ 测试类型的基本属性,比如IsConst/IsVoliate/IsRef/isAtomType/isBuildInType/isEnumType/IsIterator/IsPointer/isString/isInnerFloat/isArray/IsBaseDerive/.... ★ 转换类型的基本属性,比如AddConst/AddVoliate/AddRef/AddPointer,..,RemoveConst/RemoveVoliate/RemoveRef/RemovePointer,... 这类功能是元程序库的基本支持组件,其它库(比如boost)也提供了,但本库时提供的检测属性更多。 ☆ detect type property. e.g.IsConst/IsVoliate/IsRef/isAtomType/isBuildInType/isEnumType/IsIterator/IsPointer/isString/isInnerFloat/isArray/IsBaseDerive/.... ☆ convert type basic qualifier,e.g. AddConst/AddVoliate/AddRef/AddPointer,..,RemoveConst/RemoveVoliate/RemoveRef/RemovePointer,... get type traits.e.g.const/voliate/ref/isAtomType/isBuildInType/isEnumType/isString/isInnerFloat/isArray/IsBaseDerive/.... It is base support component of metaprogramming system,it is similiar with BOOST , but this lib provide more. ◆ [typeConvert.hpp] ★ 实现类型的修饰符转换。比如让输出参数类型的修饰符(const/voliate/ref/*)和输入参数类型的修饰符一样。 SameConst/SameVoliate/SameRef/SamePointer/SameAllQualifier/RefAdapter 应用场景:存取结构体的某类成员,当输入参数有某种const/voliate/ref修饰符时,通常要求返回值也有类似的修饰符。 ★ 当把"智能指针/stl迭代器/C指针/前三者嵌套"都视为指针时,其内的最终值(非指针值)是一致的,在模板函数中,某些场景需要取得其最终的非指针值。 应用场景:发模板函数,如 template<typename T> void transmit(T p) { receive(p); } //void receive(int&); 如果transmit的传入实参p为指针类型(比如smartpointer<vector<int*>::iterator>*或者vector<int*>::iterator), 但是发的接收函数receive的形参为非指针类型(比如int&),理论上是可以实现转换的。 Get::finalValue接口提供了这种自动的: template<typename T> void transmit(T p) { receive(Get::finalValue(p)); } ☆ Convert type qualifiers,e.g. addConst/removeConst.. , keep same output qualifier (const/voliate/ref/*) with input type. apply scenario: get member of one structure object. ☆ Think "stlSmartptr<T>/StlContainer<T>::iterator/T*" as pointer, their inner non-pointer value is same. in some scenario, the final non-pointer value is needed. e.g. template<typename T> void transmit(T p) { receive(p); } //void receive(int&); if real paremeter "p" is smartpointer<vector<int*>::iterator>* or vector<int*>::iterator , but needed parameter by "receive" is int&, in theory it is OK. Get::finalValue provide this conversion: template<typename T> void transmit(T p) { receive(Get::finalValue(p)); } ◆ [traverseTypeSet.hpp] ★ C++语法不支持模板函数/模板成员函数作为回调函数。本库采用了封装,可以支持模板函数的回调,并且支持最多7个可变参数(可以简易扩充参数个数)。 可以遍历一个TypeList或者枚举值范围CEnumRange,然后以满足条件的类型回调用户的模板函数。 其广泛的应用场景即是把运行期数据以一种非hard-code的方式化为编译期数据,从而满足元程序对编译期数据的需求。 ☆ C++ doesn't support template-based callback function. this lib package support template-based callback function(MAX 7 various parameters,easy to expand). It can traverse one TypeList or enum value , then call user's template function by suitable type/enum value. This feature converts runtime data into compile data to meet metaprogramming requirement without hard-code way, it is one big advantage. ◆ [functionTraits.hpp] ★ 获取任意类型函数的各种特征,比如函数的所有参数Params_T,返回值类型Return_T,对象类型Object_T(如果是成员函数),第N个参数的类型GetFunctionParam<F,N>, 这些类型都是包含修饰符(const/voliate/ref)的完整类型。 这些组件对于操作函数非常重要。 ☆ get some traits of any function, include all parameter type "Params_T",return type "Return_T", host type "Object_T"(if member-function) , No.x parameter type "GetFunctionParam<F,x>". this type include all signature qualifiers. This component is very important for metaprogramming based on function. ◆ 有时候STL的算法并不好用,经常是为了第三个参数需要自己写一个专用的琐碎的小函数。 虽然可以用std的bind或者boost的lambda,但是对于某些嵌套情况,用起来非常麻烦,这个库提供了下面的一些解决方式: sometimes STL algorithm is not good and it needs one traival function object(third parameter) , althrough std::bind/boost::lambda is available, but for some nest case, it is very hard to be used.this library provide below features: [function.hpp] ★ 把既有的多元函数转换为一元函数对象UnaryFunction。它通常应用于泛型(比较/排序/遍历)算法的第三个参数。 ☆ convert existing multi-parameters into unary function, it is general used as 3rd parameter in general algorithm. e.g. stl::for_each [functionobject.hpp] ★ 把一些常用目的的函数封装成函数对象,比如"比较器/测试器" ☆ function object with special abstract targart. e.g. "comparer/Tester" ◆ [functionCreater.hpp] ★ 把多元函数封装为一元函数的帮助函数。(一元函数对象的类型通常不易于书写) ☆ helper function to pack multi-parameters into unary function.(it is hard to write unary function object type) ◆ [paramPackage.hpp] ★ 实现了把任意多个(最多7个,可简易扩充),任意类型的参数封装成一个参数以利于数据传递。 ☆ pack any number parameter (max 7,easy expand) into one parameter . it is easy to transfer. ◆ [classregister.hpp] ★ MFC的动态创建不是泛型的,创造出来的对象必须是CObject的派生类,而且支持的创造方式单一,不够灵活有时候甚至不能满足需求。 本库里提供了一个泛型的动态创建方式,可以以多种灵活的方式甚至用户自定义的方式来匿名动态创建对象,创建的对象基类可以由用户指定(必须存在派生关系)。 ☆ like MFC's DYNAMIC_CREATE, but the one of MFC is not general,the instance MUST be drived from class CObject, MFC dynamic creation has only one create way,sometimes it is not enough。 this library provides general dynamic create way, can create object by multiple ways , even customized way.and base class can be specified by user. ◆ [callbackWorker.hpp] ★ 最易于使用的回调函数是无参数的回调函数。 此功能可以把任意多个参数的多元(成员/非成员)函数封装成一个无参数函数,作为简单的回调函数。 ☆ best callback function is non-parameter function. This feature packs multiple-parameter function into one no-parameter function, it is easy to be used as callback function. ◆ [memberPtr.hpp] ★ 以统一的方式实现了任意级数的结构体成员的存和取,比如多级结构体嵌套。例子:a.m_b.m_c.m_d.....m_x,非常易于在模板设计中使用。 ☆ access any level member of structure object by one unified way. e.g:a.m_b.m_c.m_d.....m_x,it is easy to be used in template componment. ◆ [anyObject.hpp] ★ 任意对象类(CAnyObject)。提供模板化的指针操作符,如果不支持用户指定指针类型,则转换结果为NULL,从而保证正确性。 ☆ package any object(CAnyObject), it operator function is template-based. if it doesn't support conversion, it return NULL. ◆ [dataset.hpp] ★ 把STL容器和经典数组封装成统一的形式,在使用上不再区别对待。对于C数组,将会自动检测越界情况。 ★ 可以使用初始化列表对数组,STL容器进行(反复)初始化。例如:vector<int> a={1,2,3,45,2}; ☆ pack STL container and class array into unified object with several same interfaces. ☆ can initialize array/stl container with initalization list repeated. e.g. vector<int> a={1,2,3,45,2}; ◆ [macroLoop.hpp] ★ 当多条语句的差别仅仅是一个数字时,可以利用提供的循环宏简化成一条宏语句,从而简化书写。用法可参见对应的单元测试例子。 ☆ if only one number is different in multiple statements, can use one macro loop to simplify them (one macro statement) usage refer to unit test. ◆ [mathOperator.hpp] ★ 泛型的数学操作符。"equal/lesser/NotBinary/NotUnary/notEqual/lesserEqual/greater/greaterEqual及交换函数swap/swapif" ☆ general math operator. "equal/lesser/NotBinary/NotUnary/notEqual/lesserEqual/greater/greaterEqual and swap/swapif" /*************************************************************************************************************************************/ ⑸ 感谢及借鉴: 本库中的占位符[placeHolder.hpp]借鉴于boost库,感谢boost库的大师们的灵感。 typelist来自loki库,但是把命名空间Loki改为MiniMPL以避免频繁的命名域切入/切出,感谢Andrei Alexandrescu的精彩演绎与启发. thanks and borrow: Args [placeHolder.h] comes from BOOST::MPL. thanks for BOOST team. typelist comes from loki lib with tiny modification(rename namespace loki to MiniMPL to avoid field switch frequently).thanks for Andrei Alexandrescu
定义数据结构如下: typedef struct tagMyNode { Mytype type; //元件类型 MySubtype Subtype; //元件子类型 tagMyNode* input1; //输入端1 tagMyNode* input2; //输入端1 tagMyNode* output1; //输出端1 UINT input1value; //输入端input1的值 UINT input2value; //输入端input2的值 UINT output1value; //输出端output1的值 int inputs; //当前已经有几个输入端有值 int number; //对于输入结点的序号 CPoint Orgpoint; //记录元件左上角位置 int width; //记录元件宽度 int height; //记录元件高度 }MyNode;  元件类型:元件类型Mytype type中Mytype是一个枚举类型定义如下: enum Mytype { Node, //结点 Gate, //门 }; 分为两种类型:Node结点和Gate门。  元件子类型:元件子类型MySubtype Subtype中MySubtype也是一个枚举类 型,定义如下: enum MySubtype { Input, //输入端 Output, //输出端 ANDGate, //与门 ORGate, //或门 NOTGate, //非门 NORGate, //或非门 NANDGate, //与非门 XORGate, //异或门 };  指针连接: tagMyNode* input1; tagMyNode* input2; tagMyNode* output1 是指向此结点的指针。由于元件之间是要相互连接的,于是设置这几个指针用于元件之间的连接。其中特殊情况有: 非门:由于非门只有一个输入端,所以非门不用tagMyNode* input2; 输入结点:输入结点只有一个链接端(这里称之为触点),采用tagMyNode* output1 输出结点:同输入结点,只有一个触点,采用tagMyNode* input1;  保存触点值:由于要进行仿真计算,所以还需保存各个触点的值: UINT input1value; UINT input2value; UINT output1value; 同指针连接,有3种特殊情况: 非门:不用UINT input2value; 输入结点:采用UINT output1value; 输出结点:采用UINT input1value;  进位标志:int inputs; 在进行仿真计算时,要用进位标志辅助计算。如与门只有在两个输入端都有值时,即inputs==2时,才能进位。  输入结点序号:int number; 每个输入结点都有不同的序号,从1开始递增。  元件位置和大小: CPoint Orgpoint; int width; int height; Orgpoint用于记录元件在视图中左上角的坐标 width用于记录元件宽度 height用于记录元件高度  电路图编辑模块 电路图编辑模块又分为两个子模块:鼠标放置元件模块,鼠标连接元件模块 首先在工具栏中可以选择这两种状态,如图4 图4 在按钮上单击可以切换状态。 定义一个枚举类型MyStatus来记录当前状态: enum MyStatus { NONE, //鼠标连接元件状态 ANDGATE, ORGATE, NOTGATE, NORGATE, NANDGATE, XORGATE, NODEINPUT, NODEOUTPUT }; MyStatus Status; 其中:NONE为鼠标连接状态,其他为鼠标放置状态。  鼠标放置元件模块 其算法如图5: 图5  DrawObject函数: 首先根据Status的状态,即六个门,两个端结点。共8种来调用DrawObject函数  引入准备好的八张位图(六个门,两个端) CBitmap MyBitMap; MyBitMap.LoadBitmap (nID);  将引入的位图拷贝入窗体窗户区 BITMAP bmpInfo; MyBitMap.GetBitmap (&bmpInfo); pOldBitmap=dc.SelectObject (&MyBitMap); ClientDC.BitBlt (point.x ,point.y,bmpInfo.bmWidth ,bmpInfo.bmHeight,&dc,0,0,SRCAND); dc.SelectObject (pOldBitmap);  用全局变量bmWidth和bmHeight来保存元件的宽度和高度 bmWidth=bmpInfo.bmWidth ; bmHeight=bmpInfo.bmHeight ;  CreateMyObject函数 函数声明为:CreateMyObject(Mytype type, MySubtype Subtype, CPoint point)  初始化元件 MyNode* pNode=new MyNode; pNode->type =type; pNode->Subtype =Subtype; pNode->input1 =0; pNode->input2 =0; pNode->output1 =0; pNode->output2 =0; pNode->Orgpoint =point; pNode->width =bmWidth; pNode->height =bmHeight; pNode->input1value =0; pNode->input2value =0; pNode->output1value =0; pNode->inputs =0;  如果创建的元件为输入结点,则要创建并画输入结点前的序号,这里 采用一个全局数组CArray<CPoint,CPoint> numpoint来记录结点前序号。 if(Subtype==Input) { //当创建Input时加入点到numpoint数组中 numpoint.Add (CPoint(point.x-15,point.y)); pNode->number =numpoint.GetSize (); //创建时重绘序号 redrawnum(); } 而redrawnum()函数就是将所有输入结点前的序号重绘。  最后将元件加入到全局链表CList<MyNode*,MyNode*> MyList中。 MyList.AddTail (pNode);  鼠标连接元件模块 鼠标连接元件模块分为三个过程模块:鼠标移动模块,鼠标按下模块,鼠标抬起模块。  鼠标移动模块 其算法如图6 图6 代码如下: void CMyView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //此时必然是非画图状态,所以status==NONE; if(Status==NONE) { //当前点在某个物件上吗? 并且 //当前点在该物件触点上吗? if(IsPointInObject(point) && IsPointInPut(point)) { //全局变量pNodeNow是在IsPointInObject()这个函数里面记录的 //circlepoint和put是在IsInInput1() IsInInput2() IsInOutput1() //这三个函数中记录的 //判断此时触点时否己连接非常重要 if(IsPutLinked()) { //如果此时触点己连接,则退出 return; } //此时鼠标移进触点 //当前是连接态吗? if(IsLink) { //连接态画图 LinkStatusDraw(point); } //开启画圆圈态 IsDrawCircle=TRUE; //画圆圈 DrawMyCircle(); } else//此时鼠标移出触点 { //如果此时已画圆圈,则要擦除圆圈 if(IsDrawCircle==TRUE) { EraserMyCircle(); //关闭画圆圈状态 IsDrawCircle=FALSE; //重绘连接线 moveoutredrawline(); //重绘圆圈所在的那个物件,因为擦除圆圈的时候可能擦除了部分物件 //------------------- redrawMyObject(pNodeNow); //如果此时是连接状态,连接态画图 } if(IsLink) { //连接态画图 LinkStatusDraw(point); } } } CView::OnMouseMove(nFlags, point); }  两个关键状态:可连接态IsDrawCircle和正在连接态IsLink  可连接态IsDrawCircle 当且仅当鼠标移动到某个元件上的某个尚未连接的触点上,才开启可连接态IsDrawCircle。之所以取名IsDrawCircle是因为此时会在鼠标停留的尚未连接的触点上画一个黑色小圆圈。 当鼠标移动离开触点,可连接态IsDrawCircle关闭。  正在连接态IsLink 当鼠标按下(见图5)并且此时可连接态IsDrawCircle开启(为TRUE)时正在连接态IsLink开启。  判断当前点是否在某个元件函数:IsPointInObject() 其算法如图7 图7  判断当前点是否在该元件触点上函数:IsPointInPut() 其算法如图8 图8 与门与其它5个门有所不同,与门只有一个输入端,所以要分开来判断 对于输入结点,则判断当前点是否在第一个输出端触点。 对于输出结点,则判断当前点是否在第一个输入端触点。 输入结点和输出结点的这样判断,一眼看上去似乎反了,但实际上有利于整个程序的编写。可以简单地这样分类:总共只有两种端,一种输入,一种输出。 这样,我们就可以将判断触点分为三个函数: IsInInput1() IsInInput2() IsInOutput1() 拿IsInInput1()来分析: centerpoint=GetCirclePoint(Input_1); if(IsInArea(point)) { //说明此时就在触点Input_1,用全局变量put记录下来 put=Input_1; //如果当前点在,则要保存触点中心点 circlepoint=centerpoint; return TRUE; } else { //如果移出触点,肯定不要再保存中心点 return FALSE; } 首先,调用函数GetCirclePoint()来取得当前触点的中心点。然后调用IsInArea(point)函数来判断当前点point是否在以当前触点中心点为中心的矩形区域内。如果是,则用一个全局枚举变量put来记录来前触点是两个输入端和一个输出端中哪一个。 我们看这个枚举类型enum Myput { Input_1, Input_2, Output_1 }; 接下来用一个全局变量circlepoint来记录当前触点中心点。再返回真。 如果当前点不在以当前触点中心点为中心的矩形区域内,则返回假。这时千万不能记录当前触点中心点。这点不注意会出大错。  判断当前触点是否已连接函数:IsPutLinked() BOOL CMyView::IsPutLinked() { switch(put) { case Input_1: if(pNodeNow->input1 !=0) return TRUE; break; case Input_2: if(pNodeNow->input2 !=0) return TRUE; break; case Output_1: if(pNodeNow->output1 !=0) return TRUE; } return FALSE; } 这里根据全局变量put的类型和全局变量pNodeNow所指向的元件, 就可以判断当前元件的当前触点是否已连接。如果连接相应指针不为0。返回真,否则返回假。  连接态画图函数:LinkStatusDraw() void CMyView::LinkStatusDraw(CPoint point) { CClientDC clientDC(this); CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject (&whitepen); clientDC.MoveTo (startpoint); clientDC.LineTo (lastpoint); clientDC.SelectObject (pOldPen); CPen redpen(PS_DOT ,1,RGB(255,0,0)); pOldPen=clientDC.SelectObject (&redpen); clientDC.MoveTo (startpoint); clientDC.LineTo (point); clientDC.SelectObject (pOldPen); lastpoint=point; //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); } 这里,startpoint是鼠标按下开始连接时起始元件触点中心点坐 标,lastpoint是上一次鼠标移动所停留的点。为了实在连接时鼠标移动 的动画效果,我们要先擦除上一次移动画的线(用白笔),然后再从startpoint到当前点point画线。移动时由于不信的擦除重画,可能将先前已画的元件,输入结点前的序号,和已经连接好的线擦除。于是我们需要重绘。 重绘所有输入结点前的序号 redrawnum(); void CMyView::redrawnum() { CClientDC dc(this); char buffer[20]; CPoint point; //重绘所有Input前的序号 for(int i=0;i<numpoint.GetSize ();i++) { //将数字转换成字符,存于buffer中 _itoa(i+1,buffer,10); point=numpoint.GetAt (i); dc.TextOut (point.x ,point.y ,buffer); } } 由于每创建一个输入结点,就要相应地记录一个序号。这个序号的位置点记录在numpoint这个数组中。数组声明如下: CArray<CPoint,CPoint> numpoint; 而数组的下标加1就为序号。 所以每次重绘为了方便,将所有序号都重绘。 重绘连接线 void CMyView::LinkLineRedraw(CPoint startpoint, CPoint point) { //将起点startpoint到终点point扩充成一个矩形drawrect CRect drawrect(startpoint,point); //rect用于产生连接线最大矩形 CRect rect; //rectInter用于计算两个矩形的相交区域 CRect rectInter; //point1和point2用于产生连接线最大矩形 CPoint point1; CPoint point2; drawrect.NormalizeRect (); drawrect.InflateRect (1,1); //遍历MyPointList链表 POSITION pos=MyPointList.GetHeadPosition (); while(pos!=0) { //pPointArray用于指向点数组对象首址 CArray<CPoint,CPoint>* pPointArray=MyPointList.GetNext (pos); point1=pPointArray->GetAt (0); switch(pPointArray->GetSize ()) { //分两种情况 :2个点和4,5个点的情况 case 2: //2个点时 point2=pPointArray->GetAt (1); break; default: //4,5个点时 point2=pPointArray->GetAt (3); } //用point1和point2设置矩形rect rect.left =point1.x ; rect.top =point1.y; rect.right =point2.x; rect.bottom =point2.y; rect.NormalizeRect (); rect.InflateRect (1,1); //如果两个矩形相交,则要重绘 if(rectInter.IntersectRect (&drawrect,&rect)) { DrawLinkLine(pPointArray); } } } 主要的算法思想是:将起点startpoint到当前点point扩充成一个矩形drawrect,然后遍历连接线链表,将每根连接线扩充成一个矩形rect,再判断这两个矩形是否相交,若相交,则需要重绘这根连接线。 连接线链表声明如下: CList<CArray<CPoint,CPoint>*,CArray<CPoint,CPoint>*> MyPointList; 链表中每个结点是一个数组对象的地址,而这个数组中每个元素是一个点。这样一个数组就表示了一根连接线,而一个链表可以遍历所以连接线。  画提示连接的小圆圈函数:DrawMyCircle() void CMyView::DrawMyCircle() { //此时全局变量circlepoint记录了要画圆圈的 //而pNodeNow指向了当前的物件 //将物件坐标中的circlepoint转换成VIEW中的坐标 int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CClientDC dc(this); //创建一个黑色的画刷 CBrush brush(RGB(0,0,0)); //创建指针pOldBrush用于保存原来的画刷 CBrush* pOldBrush; //将黑色的画刷选进设备装置DC,并用pOldBrush保存原来的画刷 pOldBrush=dc.SelectObject (&brush); //画一个圆圈,圆心是(x,y) //半径是4 dc.Ellipse (x-4,y-4,x+4,y+4); //将原来的画刷选回 dc.SelectObject (pOldBrush); } 由于全局变量circlepoint保存的是元件内部的相对坐标,需要将它 转换成视图中的坐标 x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; 以上两句完成坐标的转换。 然后以(x,y)为圆心,4为半径,画一个黑色小圆圈 dc.Ellipse (x-4,y-4,x+4,y+4)  擦除小圆圈函数:EraserMyCircle() void CMyView::EraserMyCircle() { int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CClientDC dc(this); CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); dc.Ellipse (x-4,y-4,x+4,y+4); dc.SelectObject (pOldPen); } 与画小圆圈不同的是,擦除时要选择白色的笔和白色的画刷(默认) CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); 以上3句选择白色的笔。  鼠标移开触点重绘连接线函数:moveoutredrawline() 为什么需要这个函数,原因是在鼠标称出触点后,此时要擦除刚才画 的小圆圈,而如果此时已经生成了连接线,则会擦除掉连接线的一小部分。于是需要这个函数。 void CMyView::moveoutredrawline() { int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CPoint point1; CPoint point2; point1.x=x-4; point1.y=y-4; point2.x=x+4; point2.y=y+4; LinkLineRedraw(point1,point2); } 此时pNodeNow指向刚擦除小圆圈的元件,而circlepoint则记录着 触点中心。于是只要将以ciclepoint为中心的半径为4的矩形的左上角点和右下角点为参数调用LinkLineRedraw即可。  重绘元件函数redrawMyObject() void CMyView::redrawMyObject(MyNode* pNode) { switch(pNode->Subtype ) { case ANDGate: DrawObject(pNode->Orgpoint ,IDB_ANDGATE); break; case ORGate: DrawObject(pNode->Orgpoint,IDB_ORGATE); break; case NOTGate: DrawObject(pNode->Orgpoint,IDB_NOTGATE); break; case NORGate: DrawObject(pNode->Orgpoint,IDB_NORGATE); break; case NANDGate: DrawObject(pNode->Orgpoint,IDB_NANDGATE); break; case XORGate: DrawObject(pNode->Orgpoint,IDB_XORGATE); break; case Input: DrawObject(pNode->Orgpoint,IDB_NODEINPUT); break; case Output: DrawObject(pNode->Orgpoint,IDB_NODEOUTPUT); break; } } 该函数参数为指向元件的指针,用于重绘所指向的元件。  鼠标按下模块 如图5 图5 前面已经分析了放置元件状态,现在看连接元件状态中的判断: “当前点是否在某个元件未连接的触点上”其实就是判断“可连接态”IsDrawCircle是否为真。代码如下: if(IsDrawCircle)//当前点在某个元件未连接的触点上 { //全局变量IsLink表示开始连接状态 IsLink=TRUE; //全局变量pNodeStart记录当前物件 pNodeStart=pNodeNow; //全局变量startpoint记录当前触点中心坐标(注,此时要进行坐标转换 startpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; //全局变量startput记录当前触点类别:Input_1,Input_2,Output_1; startput=put; //lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint; } 进行连接初始化:首先开启开始连接状态 IsLink=TRUE; 然后用全局变量pNodeStart指向当前元件 pNodeStart=pNodeNow 全局变量startpoint记录当前触点中心坐标(这时要进行坐标的转换) startpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; 全局变量startput记录当前触点类别 startput=put; 最后lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint;  鼠标抬起模块 其算法如图9 图9 代码如下: void CMyView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(IsLink) { //首先擦除从startpoint到point CClientDC clientDC(this); CClientDC* pDC=&clientDC; CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject (&whitepen); clientDC.MoveTo (startpoint); clientDC.LineTo (point); clientDC.SelectObject (pOldPen); //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); if(IsDrawCircle) { //用全局变量pNodeCurrent记录终点连接的物体 pNodeCurrent=pNodeNow; //用全局变量currentput记录终点连接的触点 currentput=put; //用全局变量currentpoint记录终点触点的中心坐标 currentpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; currentpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; //IsTwoObjectsCanLink()函数判断两个物件是否能连接 if(IsTwoObjectsCanLink()) { //先擦除圆圈 //EraserMyCircle();没有必要,只要鼠标移开时重绘连接线就可 //开始两个物件的画图连接 LineLink(); //开始真正连接:指针连接 RealLink(); } } //关闭连接状态: IsLink=FALSE; } CView::OnLButtonUp(nFlags, point); }  判断两个元件是否可以连接 BOOL CMyView::IsTwoObjectsCanLink() { //判断两个物件是否能连接 //这两个物件分别由pNodeStart和pNodeCurrent指向 //两个触点分别由startput和currentput标识 //若所指同一物件 if(pNodeStart==pNodeCurrent) { MessageBox("连接错误!自身物件不能相互连接"); return FALSE; } //输出直接结输出 if(startput==Output_1 && currentput==Output_1) { MessageBox("连接错误!输出端不能相互连接"); return FALSE; } //输入直接连接输入 if( (startput==Input_1 || startput==Input_2) && (currentput==Input_1||currentput==Input_2) ) { MessageBox("连接错误!输入端不能相互连接"); return FALSE; } //循环连接 if( (startput==Output_1) &&(currentput==Input_1||currentput==Input_2) ) { if(pNodeCurrent->output1 ==pNodeStart) { MessageBox("连接错误!不能循环连接"); return FALSE; } } if( (startput==Input_1||startput==Input_2) &&(currentput==Output_1) ) { if(pNodeStart->output1 ==pNodeCurrent) { MessageBox("连接错误!不能循环连接"); return FALSE; } } //如果以上情况都不发生,表示可以连接 return TRUE; } 用图来表示上述几种错误: 同一元件不能连接 图10 输出端不能连接输出端 图11 输入端不能连接输入端 图12 两个元件不能循环连接 图13  两个元件的画图连接:LineLink() 该函数调用了recordLine() 代码如下: void CMyView::recordLine () { //记录两个物件之间的连接线经过的关键点 //先动态生成一个数组CArray<CPoint,CPoint>之对象 //记录下连接线的关键点,然后将这个数组对象之地址加入到 //CList<CArray<CPoint,CPoint>*,CArray<CPoint,CPoint>*> MyPointList中 int x0,y0,x1,y1,delta_x,delta_y; //(x0,y0)用于记录输出端起始点坐标 //(x1,y1)用于记录输入端终点坐标 //delta_x,delta_y用于记录x和y的偏移量 //一定是从输出端向输入端画线 if(startput==Output_1) { x0=startpoint.x; y0=startpoint.y; x1=currentpoint.x; y1=currentpoint.y; } else { x1=startpoint.x; y1=startpoint.y; x0=currentpoint.x; y0=currentpoint.y; } delta_x=5; //动态生成数组对象 CArray<CPoint,CPoint>* pPointArray=new CArray<CPoint,CPoint>; //根据点的位置分为三种情况:2个点,4个点,5个点 if(x0<x1) { if(y0==y1) { //两个点情况 pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x1,y1)); } else { //4个点情况 pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x0+delta_x,y0)); pPointArray->Add (CPoint(x0+delta_x,y1)); pPointArray->Add (CPoint(x1,y1)); } } else if(x0==x1) { //两个点情况 pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x1,y1)); } else //x0>x1 { //5个点情况 if(y0<y1) { delta_y=20; } else { delta_y=-20; } pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x0,y0+delta_y)); pPointArray->Add (CPoint(x1-delta_x,y0+delta_y)); pPointArray->Add (CPoint(x1-delta_x,y1)); pPointArray->Add (CPoint(x1,y1)); } //加入当前数组对象地址到MyPointList MyPointList.AddTail (pPointArray); //用数组中的点画线 DrawLinkLine(pPointArray); } 首先保证从输出端向输入端画线,这样可以统一画线操作。 然后动态生成数组: CArray<CPoint,CPoint>* pPointArray=new CArray<CPoint,CPoint>; 用指针pPointArray指向该数组,用于存储连接线的关键点。 连接线根据位置总共有三种线型,如下图所示: (1)两个关键点的连接线: 图14 (2)4个关键点的连接线 图15 (3)5个关键点的连接线 图16  两个元件的指针连接:RealLink(); 其代码如下: void CMyView::RealLink() { //一定是输入连接输出 或 输出连接输入 if(startput==Input_1||startput==Input_2) { //输入连接输出 if(startput==Input_1) { pNodeStart->input1 =pNodeCurrent; } else { pNodeStart->input2 =pNodeCurrent; } pNodeCurrent->output1 =pNodeStart; } else//startput==Output_1 { //输出连接输入 pNodeStart->output1 =pNodeCurrent; if(currentput==Input_1) { pNodeCurrent->input1 =pNodeStart; } else { pNodeCurrent->input2 =pNodeStart; } } } 指针连接只有两种情况:输入连接输出和输出连接输入。可以用下图来表示 输入端连接输出端 图17 输出端连接输入端 图18  元件库模块 代码如下: UINT CMyView::gatefunction(MyNode *pNode) { UINT result; switch(pNode->Subtype ) { case ANDGate: result=pNode->input1value & pNode->input2value ; break; case ORGate: result=pNode->input1value | pNode->input2value ; break; case NOTGate: result=pNode->input1value ; result=1-result; break; case NORGate: result=pNode->input1value | pNode->input2value ; result=1-result; break; case NANDGate: result=pNode->input1value & pNode->input2value ; result=1-result; break; case XORGate: result=pNode->input1value ^ pNode->input2value ; } return result; } 这里pNode是指向当前元件的指针,根据当前元件的类型,及当前元件的输入端的值input1value和input2value(注:非门只有一个输入端)来返回元件的输出端的值。 各个门真值表如下表所示: (1)与门 输入端1 输入端2 输出端 0 0 0 0 1 0 1 0 0 1 1 0 表1 (2)或门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 1 表2 (3)非门 输入端 输出端 0 1 1 0 表3 (4)与非门 输入端1 输入端2 输出端 0 0 1 0 1 1 1 0 1 1 1 0 表4 (5)或非门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 1 表5 (6)异或门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 0 表6  计算结果(仿真)模块 仿真模块在整个仿真器中占有最重要的作用。 当在视图窗体上放置元件,连接元件后。接下工具栏开始按钮 开始计算结果,进行仿真。 其主要算法如图19 图19 代码如下: void CMyView::OnBegin() { //开始计算,输出真值表 // TODO: Add your command handler code here //判断是否能够连接 if(CalculateResult()==-1) { MessageBox("连接线路失败!请检查线路"); } else { //可以连接 //调用函数计算 beginCalculate(); //生成对话框对象 CMyDialog MyDialog; MyDialog.DoModal (); } } 其中判断线路是否正确调用了CalculateResult(),这是仿真中最重要的函数。它的返回值是最终输出结点的值。如果返回-1,说明线路有误。其具体的算法如图20: CalculateResult() 图20 代码如下: int CMyView::CalculateResult() { //用于从输入端开始计算输出端结果 //遍历所有输入结点 MyNode* pNode; MyNode* pNodeNext; POSITION pos=MyList.GetHeadPosition (); while(pos!=0) { pNode=MyList.GetNext (pos); //判断当前结点是否是输入结点 if(pNode->Subtype ==Input) { for(;;) { //判断当前的输入结点的输出端指向的结点是否为空 //如果为空,表示连接失败 if(pNode->output1 ==0) { //连接失败,返回-1 return -1; } //否则不为空 //输出到它指向结点的输入端 //此时要判断输入到哪个输入端:input1 OR input2; pNodeNext=pNode->output1 ; if(pNodeNext->input1 ==pNode) { //如果是输入到input1 pNodeNext->input1value =pNode->output1value ; //输入的值++,如果到了2,就可以计算进位了 pNodeNext->inputs ++; } else { //如果是输入到input2 pNodeNext->input2value =pNode->output1value ; pNodeNext->inputs ++; } //指针跳向下一个结点 pNode=pNodeNext; //判断此时是否是输出结点,如果是返回输出结点的值input1value; if(pNode->Subtype ==Output) { return pNode->input1value ; } //判断是否可以进位,对于非门,只要有一个输入值即可inputs==1 //对于其他门,要两个输入值inputs==2 if(pNode->Subtype==NOTGate) { //非门 if(pNode->inputs==1) { //可以进位 pNode->output1value =gatefunction(pNode); } else { //不能进位 break;//跳出for(;;) } } else { //其他门 if(pNode->inputs ==2) { pNode->output1value =gatefunction(pNode); } else { //不能进位 break;//跳出for(;;) } } //请空输入值个数inputs,以便下次计算 pNode->inputs =0; }//for(;;) }//判断当前结点是否是输入结点 }//while(pos!=0) //遍历完后若没有返回,说明连接失败 return -1; } 其算法主要思想是:遍历每一个输入结点,将输入结点的值送入到它所连接的元件的输入端,若此时该元件可以进位,则调用该元件进位函数gatefunction()计算该元件的输出端的值,再将该输出端的值送入它的下一个连接元件,再判断下一个元件能否进位,如此循环,直到输出结点。若此时不可以进位,则遍历下一个输入结点。 可以用下图来说明: 图21 假设此时输入结点遍历的顺序是1->2->3 (顺序不唯一)。假设此时1,2,3号输入结点取值0,1,0。首先将1号输入结点的值送入它所连接的或门的第一个输入端,即input1value=0。此时或门进位标志inputs= =1。于是不能进位。遍历下一个输入结点2,将2号1号输入结点的值送入它所连接的与门的第一个输入端,同样此时与门进位标志也为inputs= =1,不能进位。最后遍历到输入结点3,将值送入到与门的输入端2。由于此时有两个输入了,即与门进位标志inputs= =2,调用与门函数计算与门输出端output1value.然后将此值送入或门,同样或门进位标志inputs= =2,调用或门函数计算或门输出端值,最后送入输出结点,结束。 计算真值表:beginCalculate() 代码如下: void CMyView::beginCalculate() { //计算输入结点的个数,输出真值表 n=numpoint.GetSize (); //计算要进行循环的次数 int i; x=1; for(i=1;i<=n;i++) { x=x*2; } //动态生成x个字符串保存真值表 //用一个字符串格式化数据 CString str; //用一个数组I[1]~I[n]记录每个输入结点值 UINT* I=new UINT[n+1]; //用数组J[1]~J[n]辅助计算 UINT* J=new UINT[n+1]; //初始化J[1]~J[n] J[1]=1; for(i=2;i<=n;i++) { J[i]=J[i-1]*2; } //进行x次循环,计算真值表 for(i=0;i<x;i++) { //增加数组元素:加入一个字符串 strs.Add (CString()); for(int k=1;k<=n;k++) { I[k]=i & J[k]; //向右移位 I[k]=I[k]>>(k-1); //将输入端1~n的值加入字符串 str.Format ("%d ",I[k]); // //连接起来 strs[i]=strs[i]+str; } //给输入结点1~n初始化值 POSITION pos=MyList.GetHeadPosition (); MyNode* pNode; while(pos!=0) { pNode=MyList.GetNext (pos); //如果结点是输入结点 if(pNode->Subtype ==Input) { //结点中的pNode->number 记录了结点序号 //给结点初始化值 pNode->output1value =I[pNode->number ]; } } //调用函数计算,将计算结果保存 int result=CalculateResult(); //生成字符串以便输出 str.Format ("%10d",result); strs[i]+=str; } } 代码分析: (1) 首先得到输入结点的个数:n=numpoint.GetSize (); 这里numpoint是记录输入结点前序号位置点的数组,而有多少个这样的点,就有多少个输入结点。 (2)然后计算真值表的行数,因为有n个输入结点,真值表就有2^n行。 x=1; for(i=1;i<=n;i++) { x=x*2; } 这里我们用x来保存真值表的行数。 (3)产生所有的输入结点值的组合。 如果有3个输入结点,它的所有组合如下表 输入3 输入2 输入1 i 0 0 0 0 0 0 1 1 0 1 0 2 0 1 1 3 1 0 0 4 1 0 1 5 1 1 0 6 1 1 1 7 我们可以从表的第四列看出可以用数字i从0到x-1来分解出这些组合。 例如:当i为2时,它在内存中最后3位为:010。 此时 输入3=010 & 100=000,再右移2位即可得到0。 于是我们可以得到:输入1 & 001 再右移0位 输入2 & 010 再右移1位 输入1 & 100 再右移2位 我们用一个数组J[1]~J[n]来记录相与的数字。为1,2,4,......,2^(n-1) 初始化J[1]~J[n] J[1]=1; for(i=2;i<=n;i++) { J[i]=J[i-1]*2; } 用I[1]~I[n]记录输入1~输入n 产生一行输入值: strs.Add (CString()); for(int k=1;k<=n;k++) { I[k]=i & J[k]; //向右移位 I[k]=I[k]>>(k-1); //将输入端1~n的值加入字符串 str.Format ("%d ",I[k]); // //连接起来 strs[i]=strs[i]+str; } 给输入结点1~n初始化值 POSITION pos=MyList.GetHeadPosition (); MyNode* pNode; while(pos!=0) { pNode=MyList.GetNext (pos); //如果结点是输入结点 if(pNode->Subtype ==Input) { //结点中的pNode->number 记录了结点序号 //给结点初始化值 pNode->output1value =I[pNode->number ]; } } 调用函数计算,将计算结果保存 int result=CalculateResult(); 最后生成字符串以便输出 str.Format ("%10d",result); strs[i]+=str; 完成以上操作后,生成一个对话框,然后将字符串数组strs[]加入到列表框内。最终输出整个真值表。 参考文献: 1 Electronics Workbench 5.0 1992-1996 Interactive Image Technologies Ltd 2 MSDN Libary July 2000 Microsoft

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值