对SFINAE(替换失败并非错误)的理解

今晚从《C++必知必会》上看到SFINAE这个C++的特性,也就是substitution failure is not an error,可惜怎么看都不能够理解。后来google了一下,参考了两篇文章,算是有点明白其中的微妙了吧。

我们都知道对于非模板函数的重载来说,无论是否被调用,或是无论调用点需要的是什么类型的重载,编译器会将所有参与了重载的函数一个不落的全部编译。而且这些函数的所有信息已经具备,当进行调用的时候,编译器就能根据参数的个数跟类型来调用相关度最高的函数。但对于模板函数来说就不一样了,因为事先编译器根本无法获得所有信息,编译器也不可能为所有重载的模板函数生成真正的执行代码,而是会选择最相关的模板函数进行实例化。看下面的一个例子(引自http://hi.baidu.com/_%E2d_%B7%B3_%DE%B2%C2%D2/blog/item/748139d003b74088a1ec9c07.html):

#include <iostream>
using namespace std;
void print( int iNum )
{
cout<<"int print( int )"<< endl;
}
template < typename _Ty >
void print( _Ty tt )
{
typename _Ty::value_type vt_someval;
cout<<"template < typename _Ty >"<< endl;
}
int main()
{
short siNum = 10;
print( siNum );
return 0;
}

这段代码是不能经过编译的,原因是:当进行编译的时候,编译器放弃了对short进行向上转换为int,而是选择了模板函数进行实例化。刚开始用short来实例化模板函数是最合适的,可是在实例化的时候就出现问题了,因为int::value_type是错误的。

再看下面的一个例子:

#include <iostream>
using namespace std;
void print( int iNum )
{
cout<<"int print( int )"<< endl;
}
template < typename _Ty >
void print( _Ty tt, typename _Ty::value_type* pvt_dummy = NULL )
{
typename _Ty::value_type vt_someval;
cout<<"template < typename _Ty >"<< endl;
}
int main()
{
short siNum = 10;
print( siNum );
return 0;
}

这时候发现能够编译成功,输出的信息为:int print(int)。

这次为什么能够成功调用void print(int iNum)呢?对于第一次的代码,由于对于模板函数来说,返回值跟参数都能匹配成功,就表示编译器会认为特化成功而选择模板函数进行特化进而放弃其他选择,这时候自然就会产生错误。但是多了typename _Ty::value_type*后,编译器在展开的时候就会发现错误。这时由于SFINAE的存在,编译器就会放弃特化转而去选择void print(int INum),而不是简单报错。这就是SFINAE,C++一个非常重要的属性。

看最后一个例子:

template <typename T>
bool is_class(int T::*) {
return true;
}

template <typename T>
bool is_class(...) {
return false;
}

struct Test {
};

int main(void) {
std::cout<<is_class<Test>(0)<<endl;
std::cout<<is_class<int>(0)<<endl;
}

输出的结果为1跟0。对于第二个调用来说,刚开始选择的时候由于int T::*匹配优先级比...高,所以编译器会选择第一个尝试进行展开,但随后编译器会发现错误,这时候编译器只是简单地放弃特化,进而选择第二个。试想一下,假如没有SFINAE的存在,编译器这时候就会报错,导致无法编译通过,进而其余的模板函数得不到被选择的机会,导致很多对重载函数模板的使用都是非法的。

总体说来,SFINAE就是在假如有一个物化可能产生编译错误的情况下,如果还有其他选择,就忽略这个错误,进而选择其他的重载函数,而不会导致报错。

Posted in C/C++ | Tagged , | Leave a comment

进程与线程

简单来说,一个进程就是一个正在执行程序的实例,它有存放程序正文和数据以及其他资源的地址空间。这些资源中包括打开的文件、子进程、即将发生的报警、信号处理程序、帐户信息等。而线程类似于一个迷你进程,线程是进程中活动的主体,之所以说它迷你,是因为它本身并不集中资源,一个进程中的所有资源被它的所有线程共享,它们具有共享同一个地址空间和所有可用数据的能力。可以想像一下,有这样一个工厂,里面有各种制造某种产品的原材料(资源),有制造某种产品的说明书(程序正文),且有一定数量的工人(线程)(这样说明也许不是很准确,因为对于某些不支持多线程的操作系统,进程才是调度的主体)。

也许会奇怪,有了进程,不就可以实现多道程序设计了吗?毕竟系统可以切换执行不同的进程。那为什么还要引进线程这个概念呢?

第一个,很简单,有时候我们需要共享同一个地址空间和所有可用数据的能力。前面说过了,线程具有这种能力,但是每个进程都是隔离开来的,它们具有不同的地址空间。之所以可以让线程拥有这种能力,是因为线程是属于同一个用户进程中的,是可以信赖的,而且线程通常都是为完成同一个目标而工作的。

第二个,前面还说过线程是迷你的进程,由于它比进程更轻量级,所以它们比进程更容易创建(想下上面的例子,重建个工厂比多请个人难得多吧),尤其是用户级线程来说,同一个进程间的线程的切换不需要陷阱,不需要上下文切换,也不需要对内存高速缓存和TLB进行刷新。同样也更容易撤销。

第三个理由是基于性能方面的考虑。若多个线程都是CPU密集型的,那么并不能获得性能上的增加,但是如果存在着大里的计算和大量的I/0处理,拥有多个纯种允许这些活动重叠进行从而会加快应用程序执行的速度。这样的话进程中的一个线程被阻塞时就不会导致整个进程被挂起,因为还有别的线程可执行。再想下上面的例子,假如工厂只有一个工人,那么他在生产一件产品时,只能等待它完成后才去生产另一件。那假如有两个工人的话,就可以同时进行生产,效率也就提高了。当然也不能说越多工人也好,毕竟一个工厂需要的员工数是一定的,不然就会造成资源浪费。进程里面的线程数也是一样,毕竟每个线程需要空间来保存自己的程序计数器,寄存器的值等。且它们也都拥有自己的栈空间,这是供各个被调用但是还没有从中返回的过程使用的。

很多人也许会混淆进程跟线程的使用情况。想像一下一个工厂里面有两条流水线,相当于两个进程,可以同时制造两种产品。但是流水线又分为不同的部分,可以想像成每个部分为一个线程,每个部分可以并行运行,但都是为了完成同一个目标而已工作的。也就是说,操作系统会创建不同的进程来处理不同的作业,但是假发作业里面又有CPU计算跟I/O处理,就可以创建两个线程来分别进行CPU计算跟I/O处理,这样就大大缩减了作业的运行时间。

其实说到底,尽管线程必须在某个进程中执行,但是要清楚线程跟它的进程是不同的概念的,并且可以分别处理。进程用于把资源集中在一起,而线程则是在CPU上被调度执行的实体(类似于前面的工厂与工人的概念,工厂拥有各种资源,但实际上进行工作的是工人)。但线程又具有进程的某些性质,如多个线程共享同一个地址空间和其他资源,而多个进程共享物理内存、磁盘、打印机和其他资源。所以线程有时又被称为轻量级进程。

Posted in 操作系统 | Tagged , | Leave a comment

无题

  中午女友突然很着急地跟我说她师傅在电话里哭着跟她说要辞职,她正赶过去她家里。我安慰了她几句,她就挂了电话。晚上过去接她下班,问起了这件事。原来不是家里有什么事,是她被男友打。更那个的是这个男的是有家室的,三十出头的人,竟然还有个十几岁的孩子了,晕菜。具体怎么样就不说了,这是人家的私事,我也不好意思评头论足。只是突然觉得这事离自己很近,或许应该说是离自己的女友很近吧。这种事每天新闻一抓大把,可是第一次发生得离自己这么近,觉得有点感触。

  送她进了小区,过去公交站等车。突然有两个女的走过来,一老一少,问我要东西吃,但第一句就说明自己不是乞丐,只是没钱回家而已。我说身上只有坐车的散钱,老的说可以把整的找散。我无奈,说可以去附近的士多店买点东西,心想反正也是吃的。我挑了两个面包,老的过来说不要这种,要饼干或是方便面。算了,就让你挑吧,反正也不是什么贵的东西。找钱的时候,公车来了,开走了,我无柰啊!回到公车站想继续等的时候,老的又走近要我的电话,说要还我的饭钱。我苦着脸说不要了,这钱能要回来才怪。然后老的又问我知不知道火车站在哪,她们现在要过去,能不能再多给五块。那时候其实就肯定是骗子了,可是心里还是在想万一是真的那怎么办?唉,反正也就五块,就再给吧。给完后她又说五块不够啊,要转一次车,要多五块。我烦了,说你怎么这样啊,刚刚明明就说五块啊。她还是不死心,厚着脸皮在那说她也是没有办法啊,她年纪跟我妈差不多大(我妈有这么老么?),要不是弄成这样她也不好意思啊(这样还算不好意思啊,汗)。被烦到没办法了,我说,你刚刚的五块还我,我给你十块。给完后,一老一少匆匆离开了公交站。她们没离开前我还抱着她们有可能说真的的念头,她们一离开我就知道我上当了,刚刚不是说要坐车去火车站么,怎么走了?唉,以前还常骂很多中国人没良心,看见什么事都很冷漠。但是假如很多人都让别人利用了自己的好心,那还怎么能热心得起来呢?

  

Posted in 心情 | Tagged | 3 Comments

关于auto_ptr的那些事

先看以下代码:

{

auto_prt<Circle> aCirCle(new Circle);

}

此时会发生内存泄露吗,并没有释放掉内存啊?其实不会的,当auto_ptr对象的生命周期结束后,会自动帮我们delete掉new的内存空间的。

auto_ptr是RAII的一个应用,其对象最美妙之处在于如果它被声明为一个函数的局部变量,或作为函数的参数,甚至是一个静态对象,我们都可以保证其指向对象的析构函数会得到调用从而释放所引用的资源。

在很多时候,我们都可以把指向动态分配的内存空间的指针交给auto_ptr对象,只要定义的auto_ptr对象不是处于堆空间,就可以保证动态分配的空间被释放掉,而不用时时去刻意记得要在new后调用相应的delete操作。

那是不是无论什么时候都把指向动态分配的内存空间的指针交给auto_ptr对象后就万事大吉了?其实不是的,还是有几个问题是需要注意的:

一、上面已经说过auto_ptr对象不能分配在堆空间上,假如是动态分配的话,如果我们忘记delete它,那还是无可奈何,除非等到程序运行结束。

二、 这里先说下auto_ptr的复制操作。对于一般的类而言,复制操作不会影响到参与复制的源值。不过auto_ptr并不“普通”,假定a跟b都是auto_ptr对象,当我们用b对a进行赋值时,如果a是非空的,那么无论它指向什么东西,都将会被delete掉,代之的是b所指向的东西。因此,它们不能被用作容器元素,因为容器中的元素通常会在容器内部被拷来拷去。

三、当auto_ptr所指向的对象被删除时,它使用的是operator delete。所以,假如一个auto_ptr指向一个数组的话,那将会产生错误调用。

Posted in C/C++ | Tagged | Leave a comment

利用ARP协议来获取局域网内活动主机的IP跟MAC地址

6d30144230c93e3d72f05d3c

我们都知道ARP协议可以用来获取已知IP主机的MAC地址,那是不是可以这样说,在同一个 局域网内,我们可以利用ARP回应包来判断局域网内的活动主机?其实是行的,只要局域网内的主机是处于开机状态,那就会对ARP请求包作出回应,通过分析ARP请求包就可以把局域网内所有活动主机的IP地址跟MAC地址提取出来。下面是ARP帧结构和以太网帧结构的定义:

//28字节ARP帧结构
struct arp_head
{
unsigned short hardware_type;    //硬件类型
unsigned short protocol_type;    //协议类型
unsigned char hardware_add_len; //硬件地址长度
unsigned char protocol_add_len; //协议地址长度
unsigned short operation_field; //操作字段
unsigned char source_mac_add[6]; //源mac地址
unsigned long source_ip_add;    //源ip地址
unsigned char dest_mac_add[6]; //目的mac地址
unsigned long dest_ip_add;      //目的ip地址
};


//14字节以太网帧结构
struct ethernet_head
{
unsigned char dest_mac_add[6];    //目的mac地址
unsigned char source_mac_add[6]; //源mac地址
unsigned short type;              //帧类型
};

//arp最终包结构
struct arp_packet
{
ethernet_head ed;
arp_head ah;
};

这里我们是用winpcap开发包来构造相应的数据包的(利用pcap_sendpacket()函数可以发送构造后的数据包),具体内容可以自己去看下winpcap开发包的开发文档。下面是具体代码:

//获得自己主机的MAC地址

int GetSelfMac()
{
unsigned char sendbuf[42];//arp包结构大小
int i = -1;
int res;
ethernet_head eh;
arp_head ah;
struct pcap_pkthdr * pkt_header;
const u_char * pkt_data;

memset(eh.dest_mac_add,0xff,6);
memset(eh.source_mac_add,0x0f,6);

memset(ah.source_mac_add,0x0f,6);
memset(ah.dest_mac_add,0x00,6);

eh.type = htons(ETH_ARP);
ah.hardware_type = htons(ARP_HARDWARE);
ah.protocol_type = htons(ETH_IP);
ah.hardware_add_len = 6;
ah.protocol_add_len = 4;
ah.source_ip_add = inet_addr("219.219.71.230"); //随便设的请求方ip
ah.operation_field = htons(ARP_REQUEST);
// unsigned long ip;
// ip = ntohl(inet_addr("192.168.1.101"));
// ah.dest_ip_add =htonl(ip + loop);
ah.dest_ip_add = localIP;//inet_addr("192.168.1.102");
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,&eh,sizeof(eh));
memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));

if(pcap_sendpacket(adhandle,sendbuf,42)==0)
{
//   printf("\nPacketSend succeed\n");
}
else
{
printf("PacketSendPacket in getmine Error: %d\n",GetLastError());
return 0;
}

while((res = pcap_next_ex(adhandle,&pkt_header,&pkt_data)) >= 0)
{
if(*(unsigned short *)(pkt_data+12) == htons(ETH_ARP)&&
*(unsigned short*)(pkt_data+20) == htons(ARP_REPLY)&&
*(unsigned long*)(pkt_data+3 8) == inet_addr("219.219.71.230"))
{

for(i=0; i<6; i++)
{
selfMac[i] = *(unsigned char*)(pkt_data+22+i);
printf("%02x",selfMac[i]);
}
break;
}
}
if(i==6) return 1;
else return 0;
}

//向局域网内的所有可能的IP地址发送ARP请求包线程

void sendArpPacket()
{

unsigned char sendbuf[42];//arp包结构大小
unsigned long ip;
ethernet_head eh;
arp_head ah;


memset(eh.dest_mac_add,0xff,6);
memcpy(eh.source_mac_add,selfMac,6);

memcpy(ah.source_mac_add,selfMac,6);
memset(ah.dest_mac_add,0x00,6);

eh.type = htons(ETH_ARP);
ah.hardware_type = htons(ARP_HARDWARE);
ah.protocol_type = htons(ETH_IP);
ah.hardware_add_len = 6;
ah.protocol_add_len = 4;
ah.operation_field = htons(ARP_REQUEST);
ah.source_ip_add = inet_addr("192.168.1.101");


for (unsigned long i=0; i<HostNum; i++)
{
ip = iptosendh;
ah.dest_ip_add =htonl(ip + i);
memset(sendbuf,0,sizeof(sendbuf));
memcpy(sendbuf,&eh,sizeof(eh));
memcpy(sendbuf+sizeof(eh),&ah,sizeof(ah));
if(pcap_sendpacket(adhandle,sendbuf,42)==0)
{
// printf("\nRequest Packet succeed\n");
}
else
{
printf("Request Packet in getmine Error: %d\n",GetLastError());
}
Sleep(50);
}
Sleep(1000);
flag = TRUE;
}

//接收ARP响应线程,分析数据包后即可获得活动的主机IP地址等

void GetlivePc()
{
//pcap_t *p=(pcap_t *)lpParameter;
int res;


// arp_head ah;
struct pcap_pkthdr *pkt_header;
const u_char * pkt_data;
unsigned char tempMac[6];
while (true)
{
if(flag)
{
printf("扫描完毕,监瑞脑消金兽听线程退出!\n");
//ExitThread(0);
break;
}

if ((res = pcap_next_ex(adhandle,&pkt_header,&pkt_data)) >= 0)
{

// printf("%x",ntohs(*(unsigned short *)(pkt_data+12)));
if(*(unsigned short *)(pkt_data+12) == htons(ETH_ARP))
{
arp_packet *recv = (arp_packet*)pkt_data;
if(*(unsigned short *)(pkt_data+20) == htons(ARP_REPLY))
{
printf("xx\n");
printf("IP地址:%d.%d.%d.%d---------->mac地址:",\
recv->ah.source_ip_add&255, recv->ah.source_ip_add>>8&255,\
recv->ah.source_ip_add>>16&255, recv->ah.source_ip_add>>24&255);
pcGroup[aliveNum].ip = *(unsigned long *)(pkt_data+2 8) ;
memcpy(pcGroup[aliveNum].mac,(pkt_data+22),6);
aliveNum++;
for(int i=0; i<6; i++)
{
tempMac[i] = *(unsigned char*)(pkt_data+22+i);
printf("%02x",tempMac[i]);
}
printf("\n");
}
}
}
Sleep(50);
}
}

其实这里可以利用对特定主机发送ARP欺骗包就可以进行ARP攻击,让对方上不了网。具体就是构造一个ARP请求包,包的源IP为所在局域网的网关,源MAC就随便设一个即可,这样被攻击的主机就会把原本到发到网关的数据包发到所设的MAC地址那,这样就没办法跟外部网络进行通信了。

Posted in 网络 | Tagged | Leave a comment

VC下ado方式连接sql server 2005

首先要导入ADO库文件,如果是MFC工程的一般在stdafx.h文件中加入import语句即可:

#import "c:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename ("EOF", "adoEOF")

在开始连接数据库时,要用::CoInitialize(NULL)初始化下COM库,用AfxOleInit()的话总会出错,还不知道原因。

这里我是用_ConnectionPtr接口来连接数据库的:

_Connection pConnection;
CString strSQL;
HRESULT hr;
try
{
hr = pConnection.CreateInstance(__uuidof(Connection));
pConnection->CursorLocation = adUseClient;
strSQL = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=zgx;Data Source=71383927D5BC41E\\SQLEXPRESS";
if(SUCCEEDED(hr))
{
hr = m_pConnection->Open(_bstr_t(strSQL),"","",-1);
}
}
catch(_com_error e)///捕捉异常
{
CString errormessage;
errormessage.Format("连接数据库失败!\r\n错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);///显示错误信息
return FALSE;
}

strSQL变量中的Initial Catalog对应的是要连接的数据库名字,而Data Source对应的是服务器名。假如要用sql server的身份验证模式的话,可以用:

Provider = SQLOLEDB.1;Persist Security Info=True;User ID=用户名;Password=密码;Initial Catalog=数据库名;Data Source=SQL服务器名

Posted in 数据库 | Tagged , | Leave a comment

多个资源的银行家算法(附源码)

银行家算法是Dijksta在1965年提出的一种能够避免死锁的调度算法。该模型基于一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度。算法要做的是判断对请求的满足是否会导致进入不安全状态。

可以把银行家算法进行推广以处理多个资源。下图说明了多个资源的银行家算法如何工作。


bbb24f83901b30bef603a694

我们可以看到两个矩阵。左边的矩阵显示出5个进程分别已分配的各种资源数,右边的矩阵显示了使各进程完成运行所需的各种资源数。最右边的三个向量分别表示现有资源E、已分配资源P和可用资源A。P向量可通过将左边矩阵的各列相加获得,可用资源向量可通过从现胡资源中减去已分配资源获得。

检查一个状态是否安全的算法如下:

1)查找右边矩阵中是否有一行,其没有被满足的资源数均小于或等于A。如果不存在这样的行,那么系统将会死锁,因为任何进程都无法运行结束。

2)假若找到这样一行,那么可以假设它获得所需的资源并运行结束,将该进程标记为终止,并将其资源加到向量A上。

3)重复以上两步,直到所有的进程都被标记为终止,其初始状态是安全的;或者直到死锁发生,此刻的状态是不安全的。

如果在第1步中同时存在若进程均符合条件,那么不管挑选哪一个进程运行都没有关系,因为可用资源或者会增多,或者至少保持不变。

上图所示的状态是安全的,若进程B现在再请求一台打印机,可以满足它的请求,因为所得系统状态仍然是安全的(进程D可以结束,然后是A或E结束,剩下的进程相继结束)。

假设进程B获得两台可用打印机中的一台以后,E试图获得最后的一台打印机,假若分配给E,可用资源向量会减到(1000),这时会引起死锁。显然E的请求不能立即满足,必须延迟一段时间。

银行家算法自从被提出 后很多操作系统的专著都会详细地描述它。不过很少有作者指出该算法或然很有意义但却缺乏实用价值,因为很少有进程能够在运行前就知道其所需资源的最大值。而且进程数也不是固定的,往往在不断地变化,况且原本可用的资源也可能突然间变成不可用(硬盘可能突然坏掉什么的)。因此,在实际中,如果有,也只有极少的系统使用银行家算法来避免死锁(如运用在批处理系统中,有的批处理系统在处理作业时要求程序员明确指出所需要资源)。

附上自己写的源码:

#include <vector>
#include <iostream>
#include <algorithm>
#include <numeric>
using namespace std;

class Resource
{
public:
Resource(int n, int *init=NULL):num(n)
{
resArray = new int[n];
if (init==NULL)
{
memset(resArray, 0, n);
}
else
copy(init, init+n, resArray);
}
Resource(Resource &res)
{
num = res.getNum();
resArray = new int[num];
copy(res.getArray(), res.getArray()+num, resArray);
}
~Resource()
{
delete []resArray;
}
//获得数组元素个数
int getNum()
{
return num;
}
//获得数组的const指针
const int* getArray()
{
return resArray;
}
//左边数组的元素全部大于右边数组时才返回真
bool operator>=(Resource &right)
{
if (num!=right.getNum())
{
cout << "资源种类不一样,无法比较。。。" << endl;
return false;
}
for (int loop=0; loop!=num; ++loop)
{
if (resArray[loop]<right.getArray()[loop])
{
return false;
}
}
return true;
}
//假如数组中有一个小于零,则返回真
bool operator<(int)
{
for (int loop=0; loop!=num; ++loop)
{
if (resArray[loop]<0)
{
return true;
}
}
return false;
}
void operator=(Resource &right)
{
if (num!=right.getNum())
{
cout << "资源种类不一样,无法赋值。。。" << endl;
return;//表示赋值失败
}
copy(right.getArray(), right.getArray()+num, resArray);
}
Resource operator+(Resource &right)
{
if (num!=right.getNum())
{
cout << "资源种类不一样,无法相加。。。" << endl;
return 0;//表示相加失败
}
Resource temp(num);
for (int loop=0; loop!=num; ++loop)
{
temp.resArray[loop] = resArray[loop] + right.getArray()[loop];
}
return temp;
}
Resource operator-(Resource &right)
{
{
if (num!=right.getNum())
{
cout << "资源种类不一样,无法相减。。。" << endl;
return 0;//表示相减失败
}
Resource temp(num);
for (int loop=0; loop!=num; ++loop)
{
temp.resArray[loop] = resArray[loop] - right.getArray()[loop];
}
return temp;
}
}
private:
int num;//记录可用资源种数
int *resArray;//指向资源数组
};
//用来释放掉智能指针范围的指针指向的内存空间
template <class Iter>
void DelProxy(Iter begin, Iter end)
{
for (Iter loop=begin; loop!=end; ++loop)
{
delete *loop;
}
}
//将vector容器的元素指向的对象全部相加,并返回相加后的结果
Resource Additive(vector<Resource*>::iterator begin, vector<Resource*>::iterator end)
{
Resource temp((*begin)->getNum());
for (vector<Resource*>::iterator it=begin; it!=end; ++it)
{
temp = temp + **it;
}
return temp;
}
int main()
{
int num, cProCount;
cout << "输入资源种数:";
cin >> num;
cout << "输入进程个数:";
cin >> cProCount;
int *resArray = new int[num];
bool fSafe = false;

bool fState = true;
vector<bool> state(cProCount, true);
vector<Resource*> ownRes(cProCount);
vector<Resource*> neededRes(cProCount);

cout << "请分别输入各个进程原先拥有各个资源的数量:" << endl;
for (int loop1=0; loop1!=cProCount; ++loop1)
{
for (int loop2=0; loop2!=num; ++loop2)
{
cin >> resArray[loop2];
}
ownRes[loop1] = new Resource(num, resArray);
}

cout << "请分别输入各个进程仍需各个资源的数量:" << endl;
for (int loop1=0; loop1!=cProCount; ++loop1)
{
for (int loop2=0; loop2!=num; ++loop2)
{
cin >> resArray[loop2];
}
neededRes[loop1] = new Resource(num, resArray);
}

cout << "请分别输入各个资源的原有数量:" << endl;
for (int loop=0; loop!=num; ++loop)
{
cin >> resArray[loop];
}
Resource intrinsicRes(num, resArray);
Resource assignedRes(num);
//将各个进程拥有的资源数进行相加,则可得到每种资源已分配的数量
assignedRes = Additive(ownRes.begin(), ownRes.end());
//这时原有资源数比已分配资源数少,肯定是错误的
if (intrinsicRes-assignedRes<0)
{
cout << "原有资源数比已分配资源数少,错误。。。" << endl;
//膳后工作
delete []resArray;
DelProxy(ownRes.begin(), ownRes.end());
DelProxy(neededRes.begin(), neededRes.end());
return 1;
}
Resource availRes(num);
//得到每种资源仍可使用的数量
availRes = intrinsicRes - assignedRes;
for (int loop=0, index=0; loop!=num; ++loop, index=0)
{
fSafe = false;
for (vector<Resource*>::iterator it=neededRes.begin(); it!=neededRes.end(); ++it, ++index)
{
//state[index]为false的话则表明对应的进程已经获得所要的资源并结束退出了
if (state[index]&&availRes>=**it)
{
availRes = availRes + *(ownRes[index]);
state[index] = false;
fSafe = true;
break;
}
}
//fSafe此时为false的话表明没有一个进程能获得想要的资源数量
if (!fSafe)
{
cout << "此状态不太妙,会引起死锁。。。" << endl;
fState = false;
break;
}
}
if (fState)
{
cout << "此状态安全。。。" << endl;
}
//膳后工作
delete []resArray;
DelProxy(ownRes.begin(), ownRes.end());
DelProxy(neededRes.begin(), neededRes.end());
return 0;
}

Posted in 算法与数学 | Tagged , | 2 Comments

python实现的翻译脚本

今天突然有一个想法,就是想自己写一个翻译脚本。可惜Google提供的API是供网络应用的。刚好在《dive into python》里面这本书里面看到如何从HTML文档中提取出来自己想要的内容,那这样的话,可不可以模拟浏览器来发送想翻译的句子,然后再接收返回结果后的HTML源码,最后从中提取出翻译的结果呢?  

其实是行的,因为利用python可以模拟浏览器的行为,向Google翻译的主页发送想要翻译的句子。下面是具体的代码:

Python语言Codee#7671

1 import urllib,urllib2
2
3 values={'hl':'zh-CN','ie':'utf8','text':text,'langpair':"en|zh-CN"}
4 url='http://translate.google.cn/translate_t'
5 data = urllib.urlencode(values)
6 req = urllib2.Request(url, data)
7 req.add_header('User-Agent', "Mozilla/5.0+(compatible;+Googlebot/2.1;++http://www.google.com/bot.html)")

8 response = urllib2.urlopen(req) 

上面最关键的是text这个变量,值为想翻译的句子。后面的langpair的值是语言对,这里是英文翻译成简体中文,可以自由改动。下面就要实现一个类来取出我们想要的翻译结果,这个类要从SGMLParser派生出来,SGMLParser是在sgmllib.py中包含的。

Python语言: Codee#7673
01 from sgmllib import SGMLParser
02
03 class URLLister(SGMLParser):
04 def reset(self):
05 SGMLParser.reset(self)
06 self.result = []
07 self.open = False
08 def start_div(self, attrs):
09 id = [v for k, v in attrs if k=='id']
10 if 'result_box' in id:
11 self.open = True
12 def handle_data(self, text):
13 if self.open:
14 self.result.append(text)
15 self.open = False

  当调用feed方法时,就会寻找开始标记为div的片段,当找到时,它会调用一个自身内部的方法,其实最终也就是调用到start_div跟handle_data这两个方法来找出我们想要的翻译结果,具体的就不说了。下面是完整的代码:

Python语言: Codee#7674
01 import urllib,urllib2
02 from sgmllib import SGMLParser
03
04 class URLLister(SGMLParser):
05 def reset(self):
06 SGMLParser.reset(self)
07 self.result = []
08 self.open = False
09 def start_div(self, attrs):
10 id = [v for k, v in attrs if k=='id']
11 if 'result_box' in id:
12 self.open = True
13 def handle_data(self, text):
14 if self.open:
15 self.result.append(text)
16 self.open = False
17
18 while True:
19 text = raw_input("请输入要翻译的英文(退出输入q):")
20 if text=='q':
21 break;
22 values={'hl':'zh-CN','ie':'utf8','text':text,'langpair':"en|zh-CN"}
23 url='http://translate.google.cn/translate_t'
24 data = urllib.urlencode(values)
25 req = urllib2.Request(url, data)
26 req.add_header('User-Agent', "Mozilla/5.0+(compatible;+Googlebot/2.1;++http://www.google.com/bot.html)")
27 response = urllib2.urlopen(req)
28 parser = URLLister()
29 parser.feed(response.read())
30 parser.close()
31 print "翻译结果:"
32 for i in parser.result:
33 i = unicode(i,'utf-8').encode('gbk');
34 print i


Posted in 玩意 | Tagged , | 2 Comments

Ubuntu下字体安装失败后的解决

昨天想给系统安装雅黑字体,就在网上搜索了一种办法,照着它搞,结果傻眼了,系统大部分文字都变成了方框了。重启了一下,更糟的是连登陆界面也进不了了,弹出的错误对话框也是方框字,根本不知道哪里出问题了。
后来想了想,之前有改变过登陆界面的主题,难道是登陆主题有问题?算了,死马当活马医,只能试一下了。就启动到recovery模式下修改/etc/gdm/gdm.conf文件,把Automatic Log Enable选项改为true,把Automatic Login选项加上我的登陆用户名,重启,搞定了。不过文字还是都是方框,连英文也是一样。
真的是折腾得不得了,一点办法也没有,后来在乱点的情况下进入了一个需要有root权限的程序,发现字体显示正常,难道是权限的问题?好了,试一下先。把/usr/share/fonts下所有目录的权限都调高到777,可是还是不行。
最后在网上看到有人说把simsun.ttc文件复制到/usr/share/fonts/truetype下,提升权限,再sudo fc-cache -f下就OK了。试了下,真的恢复正常了,太激动了。这文件是win下的一个字库,应该是我在安装雅黑时没有把这个文件添加进去的问题吧。

Posted in 玩意 | Tagged | Leave a comment

为什么C++编译器不能支持对模板的分离式编译

转载自:刘未鹏(pongba)

C++的罗浮宫(http://blog.csdn.net/pongba)

首先,一个编译单元translation unit)是指一个.cpp文件以及它所#include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个.obj文件(假定我们的平台是win32),后者拥有PEPortable Executable,即windows可执行文件)文件格式,并且本身包含的就已经是二进制码,但是不一定能够执行,因为并不保证其中一定有main函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。

举个例子:

//---------------test.h-------------------//

void f();//这里声明一个函数f

//---------------test.cpp--------------//

#includetest.h

void f()

{

//do something

}  //这里实现出test.h中声明的f函数

//---------------main.cpp--------------//

#includetest.h

int main()

{

f(); //调用ff具有外部连接类型

}

在这个例子中,test. cppmain.cpp各自被编译成不同的.obj文件(姑且命名为test.objmain.obj),在main.cpp中,调用了f函数,然而当编译器编译main.cpp时,它所仅仅知道的只是main.cpp中所包含的test.h文件中的一个关于void f();的声明,所以,编译器将这里的f看作外部连接类型,即认为它的函数实现代码在另一个.obj文件中,本例也就是test.obj,也就是说,main.obj中实际没有关于f函数的哪怕一行二进制代码,而这些代码实际存在于test.cpp所编译成的test.obj中。在main.obj中对f的调用只会生成一行call指令,像这样:

call f [C++中这个名字当然是经过mangling[处理]过的]

在编译时,这个call指令显然是错误的,因为main.obj中并无一行f的实现代码。那怎么办呢?这就是连接器的任务,连接器负责在其它的.obj中(本例为test.obj)寻找f的实现代码,找到以后将call f这个指令的调用地址换成实际的f的函数进入点地址。需要注意的是:连接器实际上将工程里的.obj连接成了一个.exe文件,而它最关键的任务就是上面说的,寻找一个外部连接符号在另一个.obj中的地址,然后替换原来的虚假地址。

这个过程如果说的更深入就是:

call f这行指令其实并不是这样的,它实际上是所谓的stub,也就是一个jmp 0xABCDEF。这个地址可能是任意的,然而关键是这个地址上有一行指令来进行真正的call f动作。也就是说,这个.obj文件里面所有对f的调用都jmp向同一个地址,在后者那儿才真正callf。这样做的好处就是连接器修改地址时只要对后者的call XXX地址作改动就行了。但是,连接器是如何找到f的实际地址的呢(在本例中这处于test.obj中),因为.obj.exe的格式是一样的,在这样的文件中有一个符号导入表和符号导出表(import tableexport table)其中将所有符号和它们的地址关联起来。这样连接器只要在test.obj的符号导出表中寻找符号f(当然C++f作了mangling)的地址就行了,然后作一些偏移量处理后(因为是将两个.obj文件合并,当然地址会有一定的偏移,这个连接器清楚)写入main.obj中的符号导入表中f所占有的那一项即可。

这就是大概的过程。其中关键就是:

编译main.cpp时,编译器不知道f的实现,所以当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f的实现体。这也就是说main.obj中没有关于f的任何一行二进制代码。

编译test.cpp时,编译器找到了f的实现。于是乎f的实现(二进制代码)出现在test.obj里。

连接时,连接器在test.obj中找到f的实现代码(二进制)的地址(通过符号导出表)。然后将main.obj中悬而未决的call XXX地址改成f实际的地址。完成。

然而,对于模板,你知道,模板函数的代码其实并不能直接编译成二进制代码,其中要有一个实例化的过程。举个例子:

//----------main.cpp------//

template<class T>

void f(T t)

{}

int main()

{

//do something

f(10); // call f<int> 编译器在这里决定给f一个f<int>的实例

//do other thing

}

也就是说,如果你在main.cpp文件中没有调用过ff也就得不到实例化,从而main.obj中也就没有关于f的任意一行二进制代码!如果你这样调用了:

f(10); // f<int>得以实例化出来

f(10.0); // f<double>得以实例化出来

这样main.obj中也就有了f<int>f<double>两个函数的二进制代码段。以此类推。

然而实例化要求编译器知道模板的定义,不是吗?

看下面的例子(将模板的声明和实现分离):

//-------------test.h----------------//

template<class T>

class A

{

public:

void f(); // 这里只是个声明

};

//---------------test.cpp-------------//

#includetest.h

template<class T>

void A<T>::f()  // 模板的实现

{

//do something

}

//---------------main.cpp---------------//

#includetest.h

int main()

{

A<int> a;

f(); // #1

}

编译器在#1处并不知道A<int>::f的定义,因为它不在test.h里面,于是编译器只好寄希望于连接器,希望它能够在其他.obj里面找到A<int>::f的实例,在本例中就是test.obj,然而,后者中真有A<int>::f的二进制代码吗?NO!!!因为C++标准明确表示,当一个模板不被用到的时侯它就不该被实例化出来test.cpp中用到了A<int>::f了吗?没有!!所以实际上test.cpp编译出来的test.obj文件中关于A::f一行二进制代码也没有,于是连接器就傻眼了,只好给出一个连接错误。但是,如果在test.cpp中写一个函数,其中调用A<int>::f,则编译器会将其实例化出来,因为在这个点上(test.cpp中),编译器知道模板的定义,所以能够实例化,于是,test.obj的符号导出表中就有了A<int>::f这个符号的地址,于是连接器就能够完成任务。

关键是:在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来,所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。

Posted in C/C++ | Tagged , | 1 Comment