重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在我的整个职业生涯里我都在使用C++,而且现在C++依然是我做大多数项目时的首选编程语言。自然的,当我从2007年开始做ZeroMQ(ZeroMQ项目主页)时,我选择用C++来实现。主要的原因有以下几点:1. 包含数据结构和算法的库(STL)已经成为这个语言的一部分了。如果用C,我将要么依赖第三方库要么不得不自己手动写一些自1970年来就早已存在的基础算法。2. C++语言本身在编码风格的一致性上起到了一些强制作用。比如,有了隐式的this指针参数,这就不允许通过各种不同的方式将指向对象的指针做转换,而那种做法在C项目中常常见到(通过各种类型转换)。同样的还有可以显式的将成员变量定义为私有的,以及许多其他的语言特性。3. 这个观点基本上是前一个的子集,但值得我在这里显式的指出:用C语言实现虚函数机制比较复杂,而且对于每个类来说会有些许的不同,这使得对代码的理解和维护都会成为痛苦之源。4. 最后一点是:人人都喜欢析构函数,它能在变量离开其作用域时自动得到调用。如今,5年过去了,我想公开承认:用C++作为ZeroMQ的开发语言是一个糟糕的选择,后面我将一一解释为什么我会这么认为。首先,很重要的一点是ZeroMQ是需要长期连续不停运行的一个网络库。它应该永远不会出错,而且永远不能出现未定义的行为。因此,错误处理对于ZeroMQ来说至关重要,错误处理必须是非常明确的而且对错误应该是零容忍的。C++的异常处理机制却无法满足这个要求。C++的异常机制对于确保程序不会失败是非常有效的——只要将主函数包装在try/catch块中,然后你就可以在一个单独的位置处理所有的错误。然而,当你的目标是确保没有未定义行为发生时,噩梦就产生了。C++中引发异常和处理异常是松耦合的,这使得在 C++中避免错误是十分容易的,但却使得保证程序永远不会出现未定义行为变得基本不可能。在C语言中,引发错误和处理错误的部分是紧耦合的,它们在源代码中处于同一个位置。这使得我们在错误发生时能很容易理解到底发生了什么:int rc = fx (); if (rc != 0) handle_error();在C++中,你只是抛出一个异常,到底发生了什么并不能马上得知。int rc = fx(); if (rc != 0) throw std::exception();这里的问题就在于你对于谁处理这个异常,以及在哪里处理这个异常是不得而知的。如果你把异常处理代码也放在同一个函数中,这么做或多或少还有些明智,尽管这么做会牺牲一点可读性。try { … int rc = fx(); if (rc != 0) throw std::exception(“Error!”); … catch (std::exception e) { handle_exception(); }但是,考虑一下,如果同一个函数中抛出了两个异常时会发生什么?class exception1 {}; class exception2 {}; try { … if (condition1) throw my_exception1(); … if (condition2) throw my_exception2(); … } catch (my_exception1 e) { handle_exception1(); } catch (my_exception2 e) { handle_exception2(); }对比一下相同的C代码:… if (condition1) handle_exception1(); … if (condition2) handle_exception2(); …C代码的可读性明显高的多,而且还有一个附加的优势——编译器会为此产生更高效的代码。这还没完呢。再考虑一下这种情况:异常并不是由所抛出异常的函数来处理。在这种情况下,异常处理可能发生在任何地方,这取决于这个函数是在哪调用的。虽然乍一看我们可以在不同的上下文中处理不同的异常,这似乎很有用,但很快就会变成一场噩梦。当你在解决bug的时候,你会发现几乎同样的错误处理代码在许多地方都出现过。在代码中增加一个新的函数调用可能会引入新的麻烦,不同类型的异常都会涌到调用函数这里,而调用函数本身并没有适当进行的处理,这意味着什么?新的bug。如果你依然坚持要杜绝“未定义的行为”,你不得不引入新的异常类型来区分不同的错误模式。然而,增加一个新的异常类型意味着它会涌现在各个不同的地方,那么就需要在所有这些地方都增加一些处理代码,否则你又会出现“未定义的行为”。到这里你可能会尖叫:这特么算什么异常规范哪!好吧,问题就在于异常规范只是以一种更加系统化的方式,以按照指数规模增长的异常处理代码来处理问题的工具,它并没有解决问题本身。甚至可以说现在情况更加糟糕了,因为你不得不去写新的异常类型,新的异常处理代码,以及新的异常规范。通过上面我描述的问题,我决定使用去掉异常处理机制的C++。这正是ZeroMQ以及Crossroads I/O今天的样子。但是,很不幸,问题到这并没有结束…考虑一下当一个对象初始化失败的情况。构造函数没有返回值,因此出错时只能通过抛出异常来通知出现了错误。可是我已经决定不使用异常了,那么我不得不这样做:class foo { public: foo(); int init(); … };当你创建这个类的实例时,构造函数被调用(不允许失败),然后你显式的去调用init来初始化(init可能会失败)对象。相比于C语言中的做法,这就显得过于复杂了。struct foo { … }; int foo_init(struct foo *self);但是以上的例子中,C++版本真正邪恶的地方在于:如果有程序员往构造函数中加入了一些真正的代码,而不是将构造函数留空时会发生什么?如果有人真的这么做了,那么就会出现一个新的特殊的对象状态——“半初始化状态”。这种状态是指对象已经完成了构造(构造函数调用完成,且没有失败),但init函数还没有被调用。我们的对象需要修改(特别是析构函数),这里应该以一种方式妥善的处理这种新的状态,这就意味着又要为每一个方法增加新的条件。看到这里你可能会说:这就是你人为的限制使用异常处理所带来的后果啊!如果在构造函数中抛出异常,C++运行时库会负责清理适当的对象,那这里根本就没有什么“半初始化状态”了!很好,你说的很对,但这根本无关紧要。如果你使用异常,你就不得不处理所有那些与异常相关的复杂情况(我前面已经描述过了)。而这对于一个面对错误时需要非常健壮的基础组件来说并不是一个合理的选择。此外,就算初始化不是问题,那析构的时候绝对会有问题。
创新互联科技有限公司专业互联网基础服务商,为您提供服务器托管,高防服务器,成都IDC机房托管,成都主机托管等互联网服务。
#include stdio.h
#include stdlib.h
//节点结构
struct queue{
int num;
struct queue *next;
};
//创建空链表
struct queue *initque(void)
{
struct queue *q;
q=(struct queue *)malloc(sizeof(struct queue));
q-next=NULL; //头结点啥也没有
return q;
}
//队列尾部添加数据
void addnum(struct queue * const q,int a)
{
struct queue *n;
struct queue *tmpq=q;//一个会移动的指针
n=(struct queue *)malloc(sizeof(struct queue));//新创建个节点存储值
n-num=a;
n-next=NULL;
while(tmpq-next!=NULL)
tmpq=tmpq-next;
tmpq-next=n;//把新建的节点追加到链表末尾
}
//删除节点
int delnum(struct queue * const q)
{
int i;
struct queue *current;
struct queue *tmpq=q;
if(tmpq-next!=NULL)
{
current=tmpq-next;//头结点的下一结点,要删除的节点
tmpq-next=current-next;//头结点跳过要删除的结点指向下一结点
i=current-num;
free(current);
return i;//返回节点的值
}
return -1;
}
int main(void)
{
struct queue *myq;
myq=initque();
addnum(myq,1);
addnum(myq,3);
addnum(myq,5);
addnum(myq,7);
addnum(myq,9);
printf("%d\n",delnum(myq));
printf("%d\n",delnum(myq));
printf("%d\n",delnum(myq));
printf("%d\n",delnum(myq));
printf("%d\n",delnum(myq));
printf("%d\n",delnum(myq));//队列空了就返回-1
return 0;
}
PCF就支持C呀!
MQ还提供了系统管理的编程接口,通过该接口用户可以编写应用程序从而进行自动化的实时监控及管理。MQ的系统管理接口有两种,即Programmable Command Format(可编程命令格式,简称PCF)和MQ Administration Interface(MQ管理接口,简称MQAI)。
关于WebSphere MQ 可编程命令格式(PCF)
WebSphere MQ 可编程命令格式(PCF)命令使得管理任务能编写到应用程序中,在程序中可以创建队列、进程等对象的定义以及更改对象的属性等。
PCF定义了命令和回复消息,应用程序通过这些命令和回复消息实现和队列管理器之间的信息交换,PCF 命令和MQSC 命令具有相同的命令集,所有通过MQSC命令能够实现的功能,通过PCF都可以实现,因此,通过WebSphere MQ的应用程序可以实现对MQ对象的管理包括:队列管理器,进程定义,队列和通道等。PCF命令可以被发送到本地队列管理器的命令队列,也可以被发送到某个远程队列管理器的命令队列,因此,应用程序可以通过一个本地队列管理器集中管理网络中的任何本地和远程管理器。
MQ的远程管理机制底层就是通过PCF这种方式的,在互相联接的系统中的任意一个节点都可以进行对其他所有节点的配置和管理,这种情形的典型应用就是通过一台Windows操作系统的机器来管理全网的MQ节点。由于MQ在Windows XP/NT/2000平台上提供了图形界面的管理工具,我们可以把一个节点设成管理机,利用管理机可以监控和配置网络中的任一节点,监测和显示整个网络中任何一个节点上的服务器及其各种对象的状态和运行情况,从而实现对中间件的集中管理和监控。
每一个队列管理器有一个名为SYSTEM.ADMIN.COMMAND.QUEUE的管理队列,应用程序可以按照PCF命令消息格式封装的要求,组成PCF消息,并将该PCF命令消息发送到管理队列中,同时,每一个队列管理器也有一个命令服务器(Command Server),它为管理队列中的消息提供服务,在我们使用MQ的控制命令strmqcsv启动命令服务器之后,它将监控管理队列,一旦该队列中有PCF消息到达,它将读取该消息,并解释执行。因此在网络中的任何队列管理器都可以处理PCF消息,通过使用指定的回复队列,回复消息可以被返回给应用程序,应用程序可以获知PCF命令执行成功与否。回复队列由命令消息的消息描述符(MQMD)中的ReplyToQ和ReplyToQMgr两个字段来指定。
PCF命令和回复消息是使用MQ相应的编程接口进行发送和接收的,以C语言为例,我们只需要使用MQPUT将PCF命令消息发送到相应的管理队列,使用MQGET将PCF回复消息从相应的回复队列中取出即可。这里的关键就是如何封装PCF消息。每个MQ指令及其相关参数都是一条单独的命令消息,每个命令消息由PCF头和若干个参数结构块组成,每个参数结构块提供了命令的参数。回复消息的结构与命令消息相同,但是回复消息的个数根据不同的情况可能会有多个,例如:如果我们查询某个队列管理器下所有本地队列的属性,假设本地队列有10个,那么我们将得到10个回复消息,PCF头中的Control字段MQCFC_NOT_LAST和MQCFC_LAST将用于区分是否为最后一个回复消息。
PCF编程接口支持C,Visual Basic, COBOL, RPG, PL/1和Java等,其中在我们最常用的编程语言中,C和Visual Basic编程在PCF的封装上相对Java将会略微复杂一些
网上貌似大堆啊,如果不合适,可以找我,根据你的要求定制或移植
int main(void)
{
int max(int x, int y); //加上声明
int a,b,c; //多定义了没用的数据
scanf("%d,%d",a,b);
c = max(a,b);
printf("max is %d",c);
}
int max(int x,int y) //加上返回值类型int
{
int z;
if(xy) z=x; //不要逗号
else z=y;
return z;
}