恶心的GObject[Part I][v0.1]

Posted on Sat, 18 Sep 2010 05:43:43 -1100

虽然GObject最初是为了简化C语言的OO开发而设立的,但毕竟C不是天生的OO语言,而其中有太多抽象的东西,反而让其难以入门.....

虽然以前花了很长时间去研究这东西,但限于当时的水平,一些东西并没有弄透彻,甚至有不少错误....

因为前段自己尝试用C语言来模拟OO,积累了不少经验,最近偶然又回顾了下GObject,一些似懂非懂的概念明朗了许多.....

Part I. GObject中最恶心的概念

下面罗列几个概念,都是难以弄明白的....

GCClosure,GClosure,GClosureMarshal,

GClosureNotifyData,GClosureNotify,SignalAccumulator,GSignalAccumulator...

晕了吧,估计不少学了一段Gtk的人对上面的概念还是一知半解...

在具体展开前,我们需要知道GObject是怎么描述类,以及如何进行类型的判定和转换的....

我们知道C语言进行OO编程的时候要大量用到结构体函数指针,GObject也不例外,

而GObject中每一个类也是一到多个结构体[多个结构体是干嘛的后面会解释],GObject本身用多个int型变量来表示各个类型,类型的检测就是一个int型变量的检查,而类型的转换也是根据这个int型的变量[需要判断是否可以转换],进行结构体或指针的强制转换.

GCClosure,GClosure就是两个结构体!

前面的GCClosure多了个C,自然是C语言用的,不难想到GCClosure是GClosure一个封装,提供针对C语言的一种绑定,自然,GObject可以绑定到多种语言,比如Python,Ruby等等,目前绑定的比较好的是C[原生]/CPP/VALA/PYTHON

仔细看下

struct _GCClosure
{
  GClosure	closure;
  gpointer	callback;
};

 

注意,_GCClosure会用typedef在定义成GCClosure,这一点后面都类似,以后不再特殊说明,

通过GCClosure定义,不难发现其封装了一个GClosure的同时,还有一个callback,猜到了吧!GCClosure的主要功能就是回调!

估计不够淡定的朋友会觉得这也太扯淡了,回调的本质就是一个函数指针,包装这么多层干嘛?GObject这样设计自然有它的道理,

我们知道不同语言有不同的类型,比如C语言里就没有原生的String,

而即便是C语言我们仅在C语言,我们在定义回调函数时也可能要用到不同的形式,比如有的返回int,有的返回void,我们怎么区别这些呢?

不急,待我一步一步分析,

GOBject中GClosureMarshal是一个函数指针,但是要注意它是用来定义回调函数类型的而不是直接调用的!

typedef void  (*GClosureMarshal)	(GClosure	*closure,
					 GValue         *return_value,
					 guint           n_param_values,
					 const GValue   *param_values,
					 gpointer        invocation_hint,
					 gpointer	 marshal_data);

 

看到它的参数没有,指定了回调返回值类型,回调函数参数的个数等等

而每一个GClosure都要有一个绑定的GClosureMarshal,

具体来看看

struct _GClosure
{
  /*< private >*/
  volatile      	guint	 ref_count : 15;
  volatile       	guint	 meta_marshal : 1;
  volatile       	guint	 n_guards : 1;
  volatile       	guint	 n_fnotifiers : 2;	/* finalization notifiers */
  volatile       	guint	 n_inotifiers : 8;	/* invalidation notifiers */
  volatile       	guint	 in_inotify : 1;
  volatile       	guint	 floating : 1;
  /*< protected >*/
  volatile         	guint	 derivative_flag : 1;
  /*< public >*/
  volatile       	guint	 in_marshal : 1;
  volatile       	guint	 is_invalid : 1;

  /*< private >*/	void   (*marshal)  (GClosure       *closure,
					    GValue /*out*/ *return_value,
					    guint           n_param_values,
					    const GValue   *param_values,
					    gpointer        invocation_hint,
					    gpointer	    marshal_data);
  /*< protected >*/	gpointer data;

  /*< private >*/	GClosureNotifyData *notifiers;

};

 

注意,官方网上的Manual可能描述上过老,容易误导初学者,上面的源自GOjbect源码

罗列上面代码并不是大家一个一个分析的,只是要大家知道前面我说的一个Closure绑定一个Marshaller.....

我前面说过GClosureMarshal是用来定义回调函数类型的,不是用来调用的,GObject中真正的回调是marshal_data[够抽象的,这个是一个void *指针] ,关于这个我不多说什么[因为自己也没时间研究],因为一般不常用,主要用于其它语言间的绑定.

对于C语言,GObject本身已经提供了很多现成的C语言专用的Marshaller,下面给出一个最简单的C专用的Marshaller

/* VOID:VOID (./gmarshal.list:26) */
void
g_cclosure_marshal_VOID__VOID (GClosure     *closure,
                               GValue       *return_value G_GNUC_UNUSED,
                               guint         n_param_values,
                               const GValue *param_values,
                               gpointer      invocation_hint G_GNUC_UNUSED,
                               gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__VOID) (gpointer     data1,
                                           gpointer     data2);
  register GMarshalFunc_VOID__VOID callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 1);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__VOID) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            data2);
}

看到了吧那个callback可以指向marshal_data,而marshal_data也是一个函数指针,一般来说它应指向GCClosure中的callback,只是真正调用时是从GClosure中来调用而已.

而原始的GClosureMarshal本质上也是这样的......

注意VOID_VOID表示回调返回VOID,额外的参数为VOID[就是没有额外的参数]

 

好了,分析的差不多了,有了上面的基础我们就不难理解signal的链接与回调

对于C语言,

一个SIGNAL在CONNECT时,实际创建了一个C语言的Closure,并绑定了一个C语言的Marshaller,而C语言Marshaller的类型,要在SIGNAL 创建时来定义[注意常见的Gtk中g_signal_connect的常见SIGNAL都是已经定义好回调的类型的,所以你在定义已知信号的回调是要按类型定义],回调函数是在GCClosure中定义的,但本质上是通过GClosure来调用....

简单表示下

GCClosure{
  GClosure {
      ...
     其它很多和类型相关的东西; 
      ...
      GClosureMarshal marshal; //这是个函数指针,
      //这个的参数中有一个要指向下面定义的callback
     ...
  }
 void *callback;
}

而我们在ui使用回调是将SIGNAL发送[EMIT]出去,若在signal队列中有绑定的HANDLER[就是我们的callback],就由其处理...

signal的发送非常复杂,这里不再赘述[如果要研究这个,可以从SignalNode结构入手].....

我们仅需要知道[对于C语言]一旦找到了相应的SIGNAL的ID,就会找到指定GClosure[不是GCClosure],然后通过其找到GClosure并调用其中的marshall函数[传入的参数有何callback相关的],进而调用回调函数.

如果是C语言,可以调用类似g_cclosure_marshal_VOID__VOID的一个定义好的marshal,其内部会调用对应的指向callback的函数[具体参考上面那个C语言VOID_VOID型marshal,调用了传入marshal_data函数指针,也就是我们绑定到GCClosure的callback指针]

好了最扯淡的部分结束了,还有四个[GClosureNotifyData , GClosureNotify , SignalAccumulator , GSignalAccumulator]没说,不过这四个比较简单,理解起来也容易些,下面为了节约时间,一并说了.

GClosureNotifyData 是一个结构体,它包含了一个函数指针GClosureNotify

SignalAccumulator 也是一个结构体,他也包含了一个函数指针GSignalAccumulator

具体代码

struct _GClosureNotifyData
{
  gpointer       data;
  GClosureNotify notify;
};

typedef void  (*GClosureNotify)     (gpointer    data,
                     GClosure   *closure);

typedef struct
{
  GSignalAccumulator func;
  gpointer           data;
} SignalAccumulator;

typedef gboolean (*GSignalAccumulator)  (GSignalInvocationHint *ihint,
                     GValue            *return_accu,
                     const GValue          *handler_return,
                     gpointer               data);

上面的data即回调函数传入的参数

如果我们回调的参数中有定义在堆上的,并且调用完要释放,那么应该通过绑定一个GClosureNotify型的指针,而GSignalAccumulator指针则是用来控制某个信号响应后是否继续向下传递用的.

注意: GClosure中有指向GClosureNotifyData的指针,而SignalNode结构体中有个指向SignalAccumulator结构体的指针.

 

好了,本篇就到此为止,基本分析了GObject中最难理解的概念,因为写的仓促和个人水平有限希望发现问题的朋友能够给予指正.

我的邮件地址 pingf0@gmail.com

OOC-Embeded_C语言模拟对象的应用

Posted on Sun, 05 Sep 2010 01:21:10 -1100

其实还是在折腾如何用C语言模拟对象的机制,

现在这个版本个人该觉还是不错的,可以用GCC以及KEIL,IAR等附带的编译器来编译

另外还在此基础上实现了常用的数据结构,包括下面这些,

链表[包括单链表,双链表,循环链表],堆栈,队列,集合,

哈希表[包括链式和开放地址式],二差树[普通二叉树,平衡二叉树],

堆/优先队列,图......

项目地址:

http://code.google.com/p/ooc-embeded/

提供win下编译好的DLL,源代码可通过SVN获取

核心代码如下,

/**
 * OOC-Embeded 
 * version : see the log file
 * status  : beta 
 * author  : Jesse Meng 
 * Blog    : http://www.pingf.me 
 * E-mail  : pingf0@gmail.com
 */
#ifndef __JC_OOP__
#define __JC_OOP__
#include <stdio.h>
#include <stdlib.h>
//
#ifdef BUILD_DLL
/* DLL export */
#define EXPORT __declspec(dllexport)
#elif   defined BUILD_WITH_DLL  
/* EXE import */
#define EXPORT __declspec(dllimport)
#else
#define EXPORT
#endif
//
#define USE_HEAP_FOR_CLASS
//
#ifdef USE_CALLOC_FOR_CLASS
#define _CLASS_ALLOC(this,Type) \
this=(struct _##Type *)malloc(sizeof(struct _##Type));  
#else
#define _CLASS_ALLOC(this,Type) \
this=(struct _##Type *)calloc(1,sizeof(struct _##Type));  
#endif 
/////////////////////////////////////////////////////////////
#ifdef USE_HEAP_FOR_CLASS
#define _CLASS(Type) \
typedef struct _##Type Type; \
EXPORT int init##Type(struct _##Type * this,void *param); \
EXPORT int fin##Type(struct _##Type *this,void *param); \
EXPORT struct _##Type * new##Type(void *param); \
EXPORT int del##Type(struct _##Type **this,void *param); \
struct _##Type \
{
//////////////////////////////////////
#define _CTOR(Type) \
EXPORT struct _##Type * new##Type(void *param) \
{ \
	struct _##Type *this; \
	int initRet=0; \
	_CLASS_ALLOC(this,Type); \
	if( NULL==this ) { \
        return NULL; \
    } \
	initRet=init##Type(this,param); \
	if(-1==initRet) { \
        free(this); \
        return NULL; \
    } \
	return this; \
} \
EXPORT int init##Type(struct _##Type * this,void *param) \
{  
//
#define _DTOR(Type) \
EXPORT int del##Type(struct _##Type **this,void *param) \
{ \
	int finRet=0; \
	if(NULL==(this)) { \
        return -1; \
    } \
	finRet=fin##Type(*(this),param); \
	if(-1==finRet) {\
        return -1; \
    } \
	free(*(this)); \
	(*(this))=NULL; \
	return 0; \
} \
EXPORT int fin##Type(struct _##Type *this,void *param) \
{ 
//
#else
#define _CLASS(Type) \
typedef struct _##Type Type; \
EXPORT int init##Type(struct _##Type * this,void *param); \
EXPORT int fin##Type(struct _##Type *this,void *param); \
struct _##Type \
{
//////////////////////////////////////
#define _CTOR(Type) \
EXPORT int init##Type(struct _##Type * this,void *param) \
{  
//
#define _DTOR(Type) \
EXPORT int fin##Type(struct _##Type *this,void *param) \
{ 
//  
#endif 
/////////////////////
#define _END_CLASS };
//
#define _END_CTOR \
	return  0; \
} 
// 
#define _END_DTOR \
return 0; \
}
//
#define _HAVES(Type,name) \
static struct _##Type##S s; \
//
#define _AUTOS(Type,name,sparam) \
do{ \
    init##Type##S(&s,sparam); \
    this->name=&s; \
}while(0);
//
#define _HAVEP(Type) \
static struct _##Type##P *p=NULL; 
//
#define _CASTP(Type) \
do{ \
    p=(struct _##Type##P *)param; \
}while(0);
/////////////////////////////////////////////////////////////
#define CLASS(Type) _CLASS(Type)
#define END_CLASS _END_CLASS 
//
#define CTOR(Type) _CTOR(Type)
#define END_CTOR _END_CTOR
//
#define DTOR(Type) _DTOR(Type)
#define END_DTOR _END_DTOR
//
#define HAVES(Type,name) _HAVES(Type,name)  
//
#define AUTOS(Type,name,sparam) _AUTOS(Type,name,sparam)  
//
#define HAVEP(Type) _HAVEP(Type)  
//
#define CASTP(Type) _CASTP(Type)  
//
#endif


SlickEdit v15.0.1.3[目前最新版本] 破解方法

Posted on Sat, 07 Aug 2010 01:25:50 -1100

在此,还是要感谢下zhiwei.li那位大牛,虽然他的博客还未更新,

但是基本的方法还是一样的,

假设你用的是IDA PRO这个工具

那么首先要导入vs.exe

经前面提到的那位大牛的分析,vs.exe其中有个函数块只要返回0,就可正常使用SlickEdit了

 

仔细研究了下发现,这个exe文件在调用vsSubscription函数会调用一段代码,

而这段代码可以通过查询"|== NOT FOR RESALE ==|"字符串,这时会展现出一块比较大的图片

不难发现有两处可以返回(retn),图片中左路无需额外关注,重点在右边,只要把右路最后的一块代码(有关返回值的)顶部的

mov ax,*** (不同的版本可能有所不同)改为 xor ax,ax 就可以啦!

 

当然,如果你觉得上面的叙述很麻烦,那么直接看下面

对应最新的v15.0.1.3 的Windows版本(v15.0.0.X版本及更早的偏移地址有所不同,不通用),

       将vs.exe 偏移地址 0x0000EC90 处的  mov eax, ebp  改成  xor eax,eax

       对应的 16进制 的值就是  将8B C5 改成  33 C0

Linux版本也是大同小异,这两天没时间去弄,以后有时间再弄,或是等别人弄了.....

JCOOP入门指南[04]

Posted on Sun, 18 Jul 2010 06:32:18 -1100

//修改 : 2010.7.25 修正自JCOOP 0.12之后[不包括0.12]SUB_CTOR宏接受三个参数,最后一个参数显示的指明共享部分的指针名称

 

//这两天对JCOOP核心的宏做了一些修改[本文编写时这个版本还未上传,不过也快了],主要是增加了些返回的状态,

//便于出错后的检测,不过因为用到的printf,所以某些超低端嵌入式平台如果使用最好做一些修改,以减小代码体积.

继前文介绍了JCOOP[sourceforge.net/projects/jcoop]类的设计方法[简化类,标准类,以及含有继承的类],

本篇介绍使用JCOOP中的"包含"

假设某个机车的核心是一个发动机,

我们可以这样设计这些类,

先设计一个发动机类,再从此类派生一个汽车类.

但也可采用下面的方法,

设计一个汽车类,在此类中包含一个指向原先设计好的发动机类的指针.

上面的例子中显然后者更和常理,绝大多数情况下,两种设计可以互相转化.

在良好的设计中应该尽可能的使用第二种设计方式,尽管这样会舍弃了一些语言本身的简易特性,但会使得代码更易修改,耦合度低,程序更加紧凑.....

下面的叙述还是采用上一篇中的代码,稍作修改,以演示如何在JCOOP中方便的使用"包含"的方式去设计一个类

先设计一个非常简单的Toy类

CLASS(Toy)
	unsigned char *name;
	SHARE(Toy)
		METHOD(void,playWithToy)(void *);
END_CLASS

然后我们假设每个小孩都有一个玩具,并且可以玩!

这样Baby类就变成了下面这样

CLASS(Baby)
	HAVE(Toy,toy);
	unsigned int age; //表示年龄的成员
	SHARE(Baby)
		HAVES(Toy,toy);
		METHOD(unsigned int,getAge)(void *);
		METHOD(void,setAge)(void *,unsigned int);
		METHOD(void,cry)(); //两个void分别对应返回值和参数
END_CLASS

而Baby的构造函数就变成了

CTOR(Baby) //构造函式
	SUB_CTOR(Toy,toy);   //注意,0.12后的版本应使用SUB_CTOR(Toy,toy,toy),这样可以指定对应共享部分指针的名称
	printf("Baby constructor called ... \n");
	ASSIGN(age,1); //赋值
	CTORS(Baby)
		SUB_CTORS(Toy,toy);
		printf("Baby share part constructor called ... \n");
		CONNECT(cry,Jcry); //链接到指定的函式
		CONNECT(getAge,JgetAge);
		CONNECT(setAge,JsetAge);
END_CTOR

其析构函数也相应的变成了

DTOR(Baby) //析构函式
	printf("Baby destructor called ... \n");
	UNASSIGN(age); //置零
	SUB_DTOR(Toy,toy);
	DTORS(Baby)
		printf("Baby share part destructor called ... \n");
		DISCONNECT(getAge);
		DISCONNECT(setAge);
		DISCONNECT(cry); //断开链接
		SUB_DTORS(Toy,toy);
END_DTOR

注意到

在CLASS中使用的是HAVE...HAVES的组合[要注意,共享和非共享部分的指针名称应保证一致,否则会报错]

在CTOR中使用的是SUB_CTOR...SUB_CTORS的组合
在DTOR中使用的是SUB_DTOR...SUB_DTORS的组合

下面列出完整的代码

#include <stdio.h>                                                                                                     
#include "jc_oop.h"


CLASS(Toy)
	unsigned char *name;
	SHARE(Toy)
		METHOD(void,playWithToy)(void *);
END_CLASS


CLASS(Baby)
	HAVE(Toy,toy);
	unsigned int age; //表示年龄的成员
	SHARE(Baby)
		HAVES(Toy,toy);
		METHOD(unsigned int,getAge)(void *);
		METHOD(void,setAge)(void *,unsigned int);
		METHOD(void,cry)(); //两个void分别对应返回值和参数
END_CLASS

CLASS(Boy)
	EXTD(Baby);
	unsigned int sex;
	unsigned char * favor;
	SHARE(Boy)
		EXTDS(Baby);
		METHOD(int,setFavor)(void *,const unsigned char *);
		METHOD(unsigned char *,getFavor)(void *);
		METHOD(void,cry)();  
END_CLASS

static void JplayWithToy(void *z){
	ZS(Toy);
	printf("\n>>>>>                           \n");
	printf("playing with %s\n",this->name);
	printf("                           <<<<<\n\n");
}
static void Jcry(){
	printf("baby is crying...\n");
}
static void JboyCry(){
	printf("boy is crying...\n");
}
static unsigned int JgetAge(void *z){
	ZS(Baby);
	return this->age;
}

static void JsetAge(void *z,unsigned int age){
	ZS(Baby);
	this->age=age;
}

static int JsetFavor(void *z,const unsigned char *str){
	ZS(Boy);
	unsigned int len=strlen(str)+1;
	if((this->favor=(unsigned char *)malloc(sizeof(unsigned char)*len))!=NULL){
		strncpy(this->favor,str,len);
		return 1;
	}
	return 0;
}

static unsigned char * JgetFavor(void *z){
	ZS(Boy);
	return this->favor;
}
 


CTOR(Toy) //构造函式
	printf("Toy constructor called ... \n");
	ASSIGN(name,"audi car mini-model"); //赋值
	CTORS(Toy)
		printf("Toy share constructor called ... \n");
		CONNECT(playWithToy,JplayWithToy);
END_CTOR

DTOR(Toy) //构造函式
	printf("Toy destructor called ... \n");
	UNASSIGN(name);
	DTORS(Toy)
		printf("Toy share destructor called ... \n");
		DISCONNECT(playWithToy);
END_DTOR

CTOR(Baby) //构造函式
	SUB_CTOR(Toy,toy);  //注意,0.12后的版本应使用SUB_CTOR(Toy,toy,toy),这样可以指定对应共享部分指针的名称	
        printf("Baby constructor called ... \n");
	ASSIGN(age,1); //赋值
	CTORS(Baby)
		SUB_CTORS(Toy,toy);
		printf("Baby share part constructor called ... \n");
		CONNECT(cry,Jcry); //链接到指定的函式
		CONNECT(getAge,JgetAge);
		CONNECT(setAge,JsetAge);
END_CTOR

DTOR(Baby) //析构函式
	printf("Baby destructor called ... \n");
	UNASSIGN(age); //置零
	SUB_DTOR(Toy,toy);
	DTORS(Baby)
		printf("Baby share part destructor called ... \n");
		DISCONNECT(getAge);
		DISCONNECT(setAge);
		DISCONNECT(cry); //断开链接
		SUB_DTORS(Toy,toy);
END_DTOR


CTOR(Boy)
	SU_CTOR(Baby);
	printf("Boy constructor called ... \n");
	ASSIGN(Baby.age,10);
	ASSIGN(sex,1);
	unsigned int len=strlen("play football")+1;
	this->favor=(unsigned char *)malloc(sizeof(unsigned char)*len);
	strncpy(this->favor,"play football\0",len);
	CTORS(Boy)
		SU_CTORS(Baby);
		printf("Boy share part constructor called ... \n");
		CONNECT(cry,JboyCry); //链接到指定的函式
		CONNECT(getFavor,JgetFavor);
		CONNECT(setFavor,JsetFavor);
END_CTOR



DTOR(Boy)
	printf("Boy destructor called ... \n");
	UNASSIGN(sex);
	free(this->favor);
	this->favor=NULL;
	SU_DTOR(Baby);
	DTORS(Boy)
		printf("Boy share part destructor called ... \n");
		DISCONNECT(cry); //断开链接
		DISCONNECT(getFavor);
		DISCONNECT(setFavor);
		SU_DTORS(Baby);
END_CTOR

	


int main(){
	NEW_BUNDLE(Boy,boy,bs);
	printf("the boy's age is %d\n",bs->Baby.getAge(boy));    
	bs->Baby.setAge(boy,15);
	printf("the boy's age is %d\n",bs->Baby.getAge(boy)); 
	bs->cry();	
	printf("the boy's favor is %s\n",bs->getFavor(boy)); 
	bs->setFavor(boy,"play basketball");
	printf("the boy's favor is %s\n",bs->getFavor(boy)); 
	
	bs->Baby.toy->playWithToy(boy->Baby.toy);
	 
	printf("\n***** now test the conversion *****\n\n"); 
	
	BabyShare *babys=NULL;
	babys=CAST_PTR(BabyShare,bs);
	babys->cry();
	Baby *baby=NULL;
	baby=CAST_PTR(Baby,boy);
	printf("the baby's age is %d\n",babys->getAge(baby)); 
	   
	DEL(Boy,boy);
	DELS(Boy,bs);
	return 0;
}

正确的运行结果如下,

Toy share constructor called ... 
Baby share part constructor called ... 
Boy share part constructor called ... 
Toy constructor called ... 
Baby constructor called ... 
Boy constructor called ... 
the boy's age is 10
the boy's age is 15
boy is crying...
the boy's favor is play football
the boy's favor is play basketball

>>>>>                           
playing with audi car mini-model
                           <<<<<


***** now test the conversion *****

baby is crying...
the baby's age is 15
Boy destructor called ... 
Baby destructor called ... 
Toy destructor called ... 
Boy share part destructor called ... 
Baby share part destructor called ... 
Toy share destructor called ... 

使用C语言来写网页

Posted on Mon, 12 Jul 2010 11:59:59 -1100

前面的几篇文章简单介绍了一些基础知识,

本文给出一个具体的实例,

先说明一下

测试环境是xampp套件(apache)+win32

开发工具是SlickEdit+Mingw

 

下面的文字主要介绍,用C语言[CGI]捕获html表单传递的信息.

其实和PHP,PERL类似,简单的方法就是环境变量的获取

[常用的环境变量,我前面的文章已经罗列了一些,这里就不多说了]

在C语言中,环境变量需要通过

	char *  getenv (const char *)

这个函数来获取,使用时需要包含stdlib.h这个头文件

有了这个函数,其它的就很简单了,写一个html的表单,比如

	<form name="input" action="../cgi-bin/test.exe" method="post">
Username: 
<input type="text" name="user" />
<input type="submit" value="Submit" />
</form>

注意 : action指向所需可执行文件[win下自然是exe了,linux下管用.cgi的后缀]

当然还有一个前提,就是Apache的CGI功能必须打开,这个配置很简单的,过阵子有空了再总结,这里不多罗嗦.

如果我们,输入了一些字符,点击提交后[假设那个test.exe里面用getenv捕获了某个环境变量,并打印出来],

会发现和我们想象的有些差异,

这里简单说明一下,

        1.最常见的疑问是html中的转义字符串,命令行中的\n或是\n\r到html里就应该用</br>,类似的还有一些

        2.如果我们输入的有空格,那么返回时编程了'+'号

        3.如果我们输入一个中文词组[假定GB2132编码]--"你好",那么返回的是 "%C4%E3%BA%C3"

        4.其它没有总结的......(哈哈)

1的话自己注意下就好,2和3可以写一个简单的函数避免这些问题,比如像下面这样

void   decode(unsigned char *src , unsigned char *dst)
{
	unsigned int len=0;
	unsigned int i=0,j=0;

	len=strlen(src);
	while (i<len) {
		if (*(src+i) == '+') {
			*(dst+j) = ' ';
		} else if (*(src+i) == '%') {
			unsigned int code;    
			if (sscanf(src+i+1,"%2x",(unsigned int *)&code)!=1) {
				code=(unsigned int)'?';
			}
			*(dst+j)=(unsigned char)code;
			i+=2; 
		} else{
			*(dst+j)=*(src+i);
		}
		++i;
		++j;
	} 
	*(dst+j)='\0';  
}    

啰嗦的就到这里,下面给出完整的测试用例[关于环境变量的意义,见我前文的总结]

#include<stdio.h>
#include<stdlib.h>    
 
void   decode(unsigned char *src , unsigned char *dst)
{
	unsigned int len=0;
	unsigned int i=0,j=0;

	len=strlen(src);
	while (i<len) {
		if (*(src+i) == '+') {
			*(dst+j) = ' ';
		} else if (*(src+i) == '%') {
			unsigned int code;    
			if (sscanf(src+i+1,"%2x",(unsigned int *)&code)!=1) {
				code=(unsigned int)'?';
			}
			*(dst+j)=(unsigned char)code;
			i+=2; 
		} else{
			*(dst+j)=*(src+i);
		}
		++i;
		++j;
	} 
	*(dst+j)='\0';  
}    



int   main()    
{    
	unsigned char *env;

	long len;

	printf("%s\n\n", "Content-Type:text/html;charset=gb2312");


	env=getenv("CONTENT_LENGTH");
	printf("CONTENT_LENGTH : %s</br>",env);

	env=getenv("DOCUMENT_ROOT");
	printf("DOCUMENT_ROOT : %s</br>",env);

	env=getenv("HTTP_REFERER");
	printf("HTTP_REFERER : %s</br>",env);

	env=getenv("HTTP_USER_AGENT");
	printf("HTTP_USER_AGENT : %s</br>",env);

	env=getenv("PATH_INFO");
	printf("PATH_INFO : %s</br>",env);

	env=getenv("PATH_TRANSLATED");
	printf("PATH_TRANSLATED : %s</br>",env);

	env=getenv("QUERY_STRING");
	printf("QUERY_STRING : %s</br>",env);

	env=getenv("REMOTE_ADDR");
	printf("REMOTE_ADDR : %s</br>",env);

	env=getenv("REMOTE_ADDR");
	printf("REMOTE_ADDR : %s</br>",env);

	env=getenv("REMOTE_HOST");
	printf("REMOTE_HOST : %s</br>",env);

	env=getenv("REQUEST_METHOD");
	printf("REQUEST_METHOD : %s</br>",env);

	env=getenv("SCRIPT_NAME");
	printf("SCRIPT_NAME : %s</br>",env);

	env=getenv("REQUEST_URI");
	printf("REQUEST_URI : %s</br>",env);

	env=getenv("SERVER_NAME");
	printf("SERVER_NAME : %s</br>",env);

	env=getenv("SERVER_PORT");
	printf("SERVER_PORT : %s</br>",env);

	unsigned char oriData[500];
	unsigned char data[500];
	fgets(oriData,500,stdin);
	printf("original data : %s , %d</br>",oriData,strlen(oriData)  );
	decode(oriData,data);
	printf("data : %s , %d</br>",data,strlen(data)  );

	return   0;    
}    

对应的html表单的代码前面贴过了,就不重复贴了,

假设我们在表单中填上"你好"这个词组,那么当我们点击提交按钮后,

如果配置正确的话,浏览器会像我们返回类似如下的字串,

CONTENT_LENGTH : 17
DOCUMENT_ROOT : E:/xampp/htdocs
HTTP_REFERER : http://localhost/test.html
HTTP_USER_AGENT : Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6
PATH_INFO : (null)
PATH_TRANSLATED : (null)
QUERY_STRING :
REMOTE_ADDR : 127.0.0.1
REMOTE_ADDR : 127.0.0.1
REMOTE_HOST : (null)
REQUEST_METHOD : POST
SCRIPT_NAME : /cgi-bin/test2.exe
REQUEST_URI : /cgi-bin/test2.exe
SERVER_NAME : localhost
SERVER_PORT : 80
original data : user=%C4%E3%BA%C3 , 17
data : user=你好 , 9

JCOOP入门指南[03]

Posted on Fri, 09 Jul 2010 07:35:21 -1100

通过前面的介绍,相信本文的读者(具备一定编程基础)对JCOOP[sourceforge.net/projects/jcoop]有了大致的了解

本节依旧是以实例为主来说明如何使用JCOOP来模拟"继承"这一OO中重要的元素.

 

C语言中本没有类的概念,但其实有了结构体和函数指针,一个类的最基本功能是都可以实现的.前文中这些都已介绍了.

不过学过CPP和JAVA都知道,OO中必然有继承,这是C语言中所没有的,那么如何用C来实现呢?

准确说来C只能说是来模拟,而不能实现所有继承的功能,而模拟的方法自然还是用结构体[C中就那么点东西啊....]

具体见下面的示例

struct A{

        int a;

};

struct B{

        struct  A   stA;

        int b;

};

后面的结构体b包含了a结构体的一个实例.而类型转换时遵循位置靠前的原则.

在JCOOP中定义了如下一些宏来方便的实现继承这一功能,

类的声明中使用-->EXTD(共享部分使用EXTDS)

类的构造中使用-->SU_CTOR(共享部分使用SU_CTORS)

类的析构中使用-->SU_DTOR(共享部分使用SU_DTORS)

具体可以看下面的代码[如果感觉下面的代码仍有些怪异,请阅读前面相关JCOOP的文章]

 

#include <stdio.h>                                                                                                     
#include "./include/jc_oop.h"


CLASS(Baby)
	unsigned int age; //表示年龄的成员
	SHARE(Baby)
		METHOD(unsigned int,getAge)(void *);
		METHOD(void,setAge)(void *,unsigned int);
		METHOD(void,cry)(); //两个void分别对应返回值和参数
END_CLASS

CLASS(Boy)
	EXTD(Baby);
	unsigned int sex;
	unsigned char * favor;
	SHARE(Boy)
		EXTDS(Baby);
		METHOD(int,setFavor)(void *,const unsigned char *);
		METHOD(unsigned char *,getFavor)(void *);
		METHOD(void,cry)();  
END_CLASS


static void Jcry(){
	printf("baby is crying...\n");
}
static void JboyCry(){
	printf("boy is crying...\n");
}
static unsigned int JgetAge(void *z){
	ZS(Baby);
	return this->age;
}

static void JsetAge(void *z,unsigned int age){
	ZS(Baby);
	this->age=age;
}

static int JsetFavor(void *z,const unsigned char *str){
	ZS(Boy);
	unsigned int len=strlen(str)+1;
	if((this->favor=(unsigned char *)malloc(sizeof(unsigned char)*len))!=NULL){
		strncpy(this->favor,str,len);
		return 1;
	}
	return 0;
}

static unsigned char * JgetFavor(void *z){
	ZS(Boy);
	return this->favor;
}
 




CTOR(Baby) //构造函式
	printf("Baby constructor called ... \n");
	ASSIGN(age,1); //赋值
	CTORS(Baby)
		printf("Baby share part constructor called ... \n");
		CONNECT(cry,Jcry); //链接到指定的函式
		CONNECT(getAge,JgetAge);
		CONNECT(setAge,JsetAge);
END_CTOR

DTOR(Baby) //析构函式
	printf("Baby destructor called ... \n");
	UNASSIGN(age); //置零
	DTORS(Baby)
		printf("Baby share part destructor called ... \n");
		DISCONNECT(getAge);
		DISCONNECT(setAge);
		DISCONNECT(cry); //断开链接
END_DTOR


CTOR(Boy)
	SU_CTOR(Baby);
	printf("Boy constructor called ... \n");
	ASSIGN(Baby.age,10);
	ASSIGN(sex,1);
	unsigned int len=strlen("play football")+1;
	this->favor=(unsigned char *)malloc(sizeof(unsigned char)*len);
	strncpy(this->favor,"play football\0",len);
	CTORS(Boy)
		SU_CTORS(Baby);
		printf("Boy share part constructor called ... \n");
		CONNECT(cry,JboyCry); //链接到指定的函式
		CONNECT(getFavor,JgetFavor);
		CONNECT(setFavor,JsetFavor);
END_CTOR



DTOR(Boy)
	printf("Boy destructor called ... \n");
	UNASSIGN(sex);
	free(this->favor);
	this->favor=NULL;
	SU_DTOR(Baby);
	DTORS(Boy)
		printf("Boy share part destructor called ... \n");
		DISCONNECT(cry); //断开链接
		DISCONNECT(getFavor);
		DISCONNECT(setFavor);
		SU_DTORS(Baby);
END_CTOR

	


int main(){
	NEW_BUNDLE(Boy,boy,bs);
	printf("the boy's age is %d\n",bs->Baby.getAge(boy));    
	bs->Baby.setAge(boy,15);
	printf("the boy's age is %d\n",bs->Baby.getAge(boy)); 
	bs->cry();	
	printf("the boy's favor is %s\n",bs->getFavor(boy)); 
	bs->setFavor(boy,"play basketball");
	printf("the boy's favor is %s\n",bs->getFavor(boy)); 


	printf("\n***** now test the conversion *****\n\n"); 
	
	BabyShare *babys=NULL;
	babys=CAST_PTR(BabyShare,bs);
	babys->cry();
	Baby *baby=NULL;
	baby=CAST_PTR(Baby,boy);
	printf("the baby's age is %d\n",babys->getAge(baby)); 
	   
	DEL(Boy,boy);
	DELS(Boy,bs);
	return 0;
}

运行时,会显示下面的字符,

Baby share part constructor called ... 
Boy share part constructor called ... 
Baby constructor called ... 
Boy constructor called ... 
the boy's age is 10
the boy's age is 15
boy is crying...
the boy's favor is play football
the boy's favor is play basketball

***** now test the conversion *****

baby is crying...
the baby's age is 15
Boy destructor called ... 
Baby destructor called ... 
Boy share part destructor called ... 
Baby share part destructor called ... 

上面这个实例实现了一个Boy类,该类继承了前面的Baby类[见前文],

这里仅简单说明以下几点,

    1.Boy中继承了Baby类,共享部分也是如此.

    2.Boy中类也实现了单独的cry方法,你可以将这种方式理解成函数的重写.

    3.类型转换时,因为上面程序中都是使用CAST_PTR宏,因为类都建立在堆上,我们只是用指向其的指针.

    4.如果最后调用babys->cry();那么还是打印baby is crying ......

    5.注意上面constructor和destructor调用的辅助文字仅仅在说明调用的次序,当然如果你改变它的位置,打印的位置也会有所改变.

    6.另外JboyCry()仅仅是因为两个类都放在一个文件中,为了避免重名才这样做.

 

 

 

JCOOP入门指南[02]

Posted on Thu, 08 Jul 2010 12:54:01 -1100

上一篇中用JCOOP[sourceforge.net/projects/jcoop]设计了一个最最简单的Baby类,但是如果都像这样做的话一旦类的规模增大,

每声明一个实例,有些时候就要额外的占用一定的空间[比如类方法使用的指针],

为了节约资源,在JCOOP中一个类其实包含了两部分,一部分存储每个实例的属性成员,

另一部分存储公共的变量,可以是共享变量也可以是公用的方法[这类似与C++中的虚表]

而其声明方式类似下面这样,

CLASS(A)

        ......//普通成员

        SHARE(A)

                ......//方法或共享成员

END_CLASS

而每个类的私有部分和公有部分通过一个名为share的指针(如果声明了公有部分则存在于私有部分,否则不存在)

指向公有部分.

类似的,类的构造和析构也有相应的宏CTOR...CTORS...END_CTORDTOR...DTORS...END_DTOR来声明.

具体见下面的代码,

 

#include <stdio.h>                                                                                                     
#include "./include/jc_oop.h"


CLASS(Baby)
	unsigned int age; //表示年龄的成员
	SHARE(Baby)
		METHOD(unsigned int,getAge)(void *);
		METHOD(void,setAge)(void *,unsigned int);
		METHOD(void,cry)(void *); //两个void分别对应返回值和参数
END_CLASS

static void Jcry(void *z){
	printf("baby is crying...\n");
}

static unsigned int JgetAge(void *z){
	ZS(Baby);
	return this->age;
}

static void JsetAge(void *z,unsigned int age){
	ZS(Baby);
	this->age=age;
}


CTOR(Baby) //构造函式
	printf("Baby constructor called ... \n");
	ASSIGN(age,1); //赋值
	CTORS(Baby)
		printf("Baby share part constructor called ... \n");
		CONNECT(cry,Jcry); //链接到指定的函式
		CONNECT(getAge,JgetAge);
		CONNECT(setAge,JsetAge);
END_CTOR

DTOR(Baby) //析构函式
	printf("Baby destructor called ... \n");
	UNASSIGN(age); //置零
	DTORS(Baby)
		printf("Baby share part destructor called ... \n");
		DISCONNECT(getAge);
		DISCONNECT(setAge);
		DISCONNECT(cry); //断开链接
END_DTOR

int main(){
	NEW_BUNDLE(Baby,baby,bs);
	printf("the baby's age is %d\n",bs->getAge(baby));    
	bs->cry(baby);
	bs->setAge(baby,3);
	printf("the baby's age is %d\n",bs->getAge(baby));    
	DEL(Baby,baby);
	DELS(Baby,bs);
	return 0;
}

这里要特别声明的是如果用ZS宏,第一个参数的声明应为void *z,这样才会正确的转换成相应的"this指针"

编译正确后,会输出如下,

Baby share part constructor called ... 
Baby constructor called ... 
the baby's age is 1
baby is crying...
the baby's age is 3
Baby destructor called ... 
Baby share part destructor called ... 

另外顺带要说一下NEW_BUNDLE这个宏,其相当于NEWS和NEW这两个宏的组合,第一次声明某个实例时使用该宏会更加简洁.

和前面讲到的CTORS和DTORS一样,那个末尾的S表示SHARE,也就是公有部分.

而程序中我们实际使用的是两个类型分别BabyShareBaby指针,其分别指向了两个在堆上创建的实例.

当然如果你正确的使用NEW和DEL等宏,那么一般不用过多关注堆上类所用内存的分配.

JCOOP入门指南[01]

Posted on Thu, 08 Jul 2010 07:39:44 -1100

写一点关于JCOOP的入门指南,JCOOP最核心的部分是由一堆宏构成的,

能够简化C语言描述对象的过程,具体代码见sourceforge.net/projects/jcoop

下面是最简单的一个例子,

涉及到用JCOOP构造类的方法之一以及构造函数与析构函数的使用

因为这个例子非常简单,多的就不写了,直接看注释就好

[那个jc_oop.h要到上面的网址下载,另外最好用GCC来编译,MS那套东西我没测试过,Mingw肯定欧科的]

 
#include <stdio.h>                                                                                                     
#include "./include/jc_oop.h"

static void cry(){
	printf("baby is crying...\n");
}


CLASS(Baby)
	int age; //表示年龄的成员
	METHOD(void,cry)(void); //两个void分别对应返回值和参数
END_CLASS

CTOR(Baby) //构造函式
	printf("Baby constructor called ... \n");
	ASSIGN(age,1); //赋值
	CONNECT(cry,cry); //链接到指定的函式
END_CTOR

DTOR(Baby) //析构函式
	printf("Baby destructor called ... \n");
	UNASSIGN(age); //置零
	DISCONNECT(cry); //断开链接
END_DTOR

int main(){
	NEW(Baby,baby,NULL);
	printf("the baby's age is %d\n",baby->age);    
	baby->cry();
	DEL(Baby,baby);
	return 0;
}

运行时会打印如下的字符串

Baby constructor called ... 
the baby's age is 1
baby is crying...
Baby destructor called ... 

需要注意的是上面的Baby类是构造在堆上的,所以baby自然是一个指针

其在NEW时分配相应的内存,并初始化相应的值(构造函数),

在DEL时调用析构函数释放对应的内存.

从上面的代码不难发现,使用JCOOP不但可以形象的描述一个类,

而且简化了代码的编写,特别是涉及内存分配相关的部分.

 

 

 

爱因斯坦谜题的c语言解答

Posted on Tue, 16 Mar 2010 02:10:25 -1100

[旧文搬迁]

题目如下,
1、在一条街上,有5座房子,喷了5种颜色。 2、每个房里住着不同国籍的人 3、每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物 另外, 1、英国人住红色房子 2、瑞典人养狗 3、丹麦人喝茶 4、绿色房子在白色房子左面 5、绿色房子主人喝咖啡 6、抽Pall Mall香烟的人养鸟 7、黄色房子主人抽Dunhill香烟 8、住在中间房子的人喝牛奶 9、挪威人住第一间房 10、抽Blends香烟的人住在养猫的人隔壁 11、养马的人住抽Dunhill香烟的人隔壁 12、抽Blue Master的人喝啤酒 13、德国人抽Prince香烟 14、挪威人住蓝色房子隔壁 15、抽Blends香烟的人有一个喝水的邻居


问题是:谁养鱼?

 以下是c语言的解答

#include <stdio.h>
#include <stdlib.h>
enum Nat
{
    ENG,SWE,DEN,NOR,GER
};
enum Hou
{
    RED,GREEN,WHITE,YELLOW,BLUE
};
enum Pet
{
    DOG,BIRD,CAT,HORSE,FISH
};
enum Dri
{
    TEA,COFFEE,MILK,BEER,WATER
};
enum Cig
{
    PALLMALL,DUNHILL,BLENDS,BLUEMASTER,PRINCE
};
struct S//scence
{
    int *nat; //nationality
    int *hou; //house
    int *pet; //pet
    int *dri; //drink
    int *cig; //cigar
} ;
void Permute(int a[120][5]) //permutation process
{
    int i=0,j=0,r1,r2,r3,r4,r5;
    for (r1=0;r1<5;r1++)
    {
        for (r2=0;r2<5;r2++)
        {
            if (r2!=r1)
                for (r3=0;r3<5;r3++)
                {
                    if (r3!=r2&&r3!=r1)
                        for (r4=0;r4<5;r4++)
                        {
                            if (r4!=r3&&r4!=r2&&r4!=r1)
                                for (r5=0;r5<5;r5++)
                                {
                                    if (r5!=r4&&r5!=r3&&r5!=r2&&r5!=r1)
                                    {
                                        a[i][j++]=r1;
                                        a[i][j++]=r2;
                                        a[i][j++]=r3;
                                        a[i][j++]=r4;
                                        a[i++][j]=r5;
                                        j=0;
                                    }
                                }
                        }
                }
        }
    }
//printf("\n***枚举过程完成***\n\n");
    return ;
}
int NatPos(struct S s,enum Nat t) //get position by nationality
{
    int i;
    for (i=0;i<5;i++)
        if (s.nat[i]==t)
            return i;
    return -1;
}
int HouPos(struct S s,enum Hou t) //get position by house
{
    int i;
    for (i=0;i<5;i++)
        if (s.hou[i]==t)
            return i;
    return -2;
}
int PetPos(struct S s,enum Pet t) //get position by pet
{
    int i;
    for (i=0;i<5;i++)
        if (s.pet[i]==t)
            return i;
    return -3;
}
int DriPos(struct S s,enum Dri t) //get position by drink
{
    int i;
    for (i=0;i<5;i++)
        if (s.dri[i]==t)
            return i;
    return -4;
}
int CigPos(struct S s,enum Cig t) //get position by cigar
{
    int i;
    for (i=0;i<5;i++)
        if (s.cig[i]==t)
            return i;
    return -5;
}
int main()
{
    int a[120][5];
    struct S s;
    int r1,r2,r3,r4,r5,i;
    Permute(a); //get permuted array
    for (s.dri=a[0],r1=0;r1<120;s.dri+=5,r1+=1) //permute 5 rounds for scene
    {
        if (s.dri[2]==MILK) //condition required
            for (s.nat=a[0],r2=0;r2<120;s.nat+=5,r2+=1)
            {
                if (s.nat[0]==NOR&&s.dri[NatPos(s,DEN)]==TEA) //condition required
                    for (s.hou=a[0],r3=0;r3<120;s.hou+=5,r3+=1)
                    {
                        if (s.hou[NatPos(s,ENG)]==RED&&s.dri[HouPos(s,GREEN)]==COFFEE&&
                            (HouPos(s,GREEN)+1==HouPos(s,WHITE))&&
                            ((NatPos(s,NOR)==HouPos(s,BLUE)+1)||(NatPos(s,NOR)==HouPos(s,BLUE)-1)))
                            for (s.pet=a[0],r4=0;r4<120;s.pet+=5,r4+=1)
                            {
                                if (s.pet[NatPos(s,SWE)]==DOG) //condition required
                                    for (s.cig=a[0],r5=0;r5<120;s.cig+=5,r5+=1)
                                    {
//<----
//////////////////////////////////////////////////////////////////
  if (((CigPos(s,BLENDS)==DriPos(s,WATER)+1)||(CigPos(s,BLENDS)==DriPos(s,WATER)-1))&&
       ((PetPos(s,HORSE)==CigPos(s,DUNHILL)+1)||(PetPos(s,HORSE)==CigPos(s,DUNHILL)-1))&&
        ((CigPos(s,BLENDS)==PetPos(s,CAT)+1)||(CigPos(s,BLENDS)==PetPos(s,CAT)-1))&&
           s.dri[CigPos(s,BLUEMASTER)]==BEER&&s.pet[CigPos(s,PALLMALL)]==BIRD&&
           s.cig[HouPos(s,YELLOW)]==DUNHILL&&s.cig[NatPos(s,GER)]==PRINCE)
          {
                        switch (s.nat[PetPos(s,FISH)])
                        {
                               case ENG:printf("***英国人养鱼***\n");break;
                               case SWE:printf("***瑞典人养鱼***\n");break;
                               case DEN:printf("***丹麦人养鱼***\n");break;
                               case NOR:printf("***挪威人养鱼***\n");break;
                               case GER:printf("***德国人养鱼***\n");break;
                               default:printf("***解不存在***\n");break;
                        }
                        printf("\n此时具体情况如下");
                        printf("\n\n*****************\n");
                        printf(" 标号 1 2 3 4 5 \n");
                        printf("\n 国籍 ");
                        for (i=0;i<5;i++)
                        {
                               switch (s.nat[i])
                               {
                                   case ENG:printf("英国 ");break;
                                   case SWE:printf("瑞典 ");break;
                                   case DEN:printf("丹麦 ");break;
                                   case NOR:printf("挪威 ");break;
                                   case GER:printf("德国 ");break;
                                   default:printf("错误 ");break;
                               };
                       }
                     printf("\n\n 房屋颜色 ");
                     for (i=0;i<5;i++)
                     {
                              switch (s.hou[i])
                              {
                                   case RED:printf("红色 ");break;
                                   case GREEN:printf("绿色 ");break;
                                   case WHITE:printf("白色 ");break;
                                   case YELLOW:printf("黄色 ");break;
                                   case BLUE:printf("蓝色 ");break;
                                   default:printf("错误 ");break;
                              };
                     }
                    printf("\n\n 宠物 ");
                    for (i=0;i<5;i++)
                    {
                             switch (s.pet[i])
                             {
                                   case DOG:printf("狗 ");break;
                                   case BIRD:printf("鸟 ");break;
                                   case CAT:printf("猫 ");break;
                                   case HORSE:printf("马 ");break;
                                   case FISH:printf("鱼 ");break;
                                   default:printf("错误 ");break;
                             };
                    }
                   printf("\n\n 饮品 ");
                   for (i=0;i<5;i++)
                  {
                             switch (s.dri[i])
                             {
                                    case TEA:printf("茶 ");break;
                                    case COFFEE:printf("咖啡 ");break;
                                    case MILK:printf("牛奶 ");break;
                                    case BEER:printf("啤酒 ");break;
                                    case WATER:printf("水 ");break;
                                    default:printf("错误 ");break;
                             };
                   }
                  printf("\n\n 香烟 ");
                  for (i=0;i<5;i++)
                  {
                             switch (s.cig[i])
                             {
                                    case PALLMALL:printf("帕玛 ");break;
                                    case DUNHILL:printf("杜希尔 ");break;
                                    case BLENDS:printf("布莱登 ");break;
                                    case BLUEMASTER:printf("布鲁玛 ");break;
                                    case PRINCE:printf("布里斯 ");break;
                                    default:printf("错误 ");break;
                             };
                    }
                    printf("\n\n*****************\n\n");
       }
                                    }
                            }
                    }
            }
    }
    return 0;
}