??xml version="1.0" encoding="utf-8" standalone="yes"?>
自从Symbian OS?/span>EKA2提供了强大的CTelephonyQ这之后很多跟网l相关的参数都通过q个获取Q像识别目前手机是注册的是移动还是联通等信息|络上传的比较多的也是通过该方法,特别是啸天兄?/span>nokia论坛分n了代码之后,|络上大多采用其代码Q当然也包括我这L懒h在内Q而且往往很多人都没有注意最关键的原理即“国际Ud用户识别码(IMSIQ?/span>International Mobile Subscriber Identification NumberQ是区别Ud用户的标志,储存?/span>SIM卡中Q可用于区别Ud用户的有效信息?/span>IMSI?/span>MCC?/span>MNC?/span>MSINl成Q其?/span>MCC为移动国家号码,?/span>3位数字组成,唯一地识别移动客h属的国家Q我国ؓ460Q?/span>MNC为网l?/span>idQ由2位数字组成,用于识别Ud客户所归属的移动网l,中国Ud?/span>00Q中国联通ؓ01Q?/span>MSIN为移动客戯别码Q采用等?/span>11位数字构?#8221;。具体详?/span>http://wiki.forum.nokia.com/index.php/%E5%8C%BA%E5%88%86%E5%BD%93%E5%89%8D%E7%94%A8%E6%88%B7SIM%E5%8D%A1%E6%98%AF%E7%A7%BB%E5%8A%A8%E8%BF%98%E6%98%AF%E8%81%94%E9%80%9A
最q在使用该代码时Q发现假如当手机处于ȝ状态下Q则不论有无?/span>SIM卡,使用啸天兄的ҎQ就识别不出来了Q这个应该跟CTelephony的实现有养I本h试着通过其源码去了解了,但是貌似跟踪到底层没有完全公开Q或者说个h看源码能力还太弱了些。由于采用啸天兄Ҏ实现不了了,所以只能从上面的红头文Ӟ即红体字Q寻找解x法,虽然CTelephony::GetCurrentNetworkInfo在离U模式下失效Q但?/span>CTelephony::GetSubscriberId仍然可用Qؓ此我们就可以通过直接分析IMSI来实现对q营商网l的识别Q至?/span>MNC的信息,大家可以查询http://en.wikipedia.org/wiki/Mobile_network_codeQ在国内的情况如下截图:
既然知道了如上信息,我们可以简单的?/span>IMSI可行分析了Q小可对啸天兄的代码q行单修改,当然该代码也是?/span>CTelephonyQ只能用在EKA2q_上,EKA1可以采用RMobilePhone::GetSubscriberId的方法来获取IMSIQ在q里也就不做展开了,具体代码如下Q?/span>
头文?/span>
/*
* TelephonyAO.h
*
* Created on:
* Author: frank
*/
#ifndef TELEPHONYAO_H_
#define TELEPHONYAO_H_
#include <e32base.h>
#include <Etel3rdParty.h>
typedef enum
{
ENetWorkUnKnow,
ENetWorkCM,
ENetWorkUN,
ENetWorkTC,
ENetWorkTT,
}TNetWorkType;
class CTelephonyAO : public CActive
{
public:
static CTelephonyAO* NewL();
TNetWorkType GetNetWorkId();
public:
~CTelephonyAO();
protected:
void DoCancel();
void RunL();
private:
CTelephonyAO();
void ConstructL();
void GetNetWorkInfo();
private:
CActiveSchedulerWait* iActiveSchedulerWait;
CTelephony* pTelephony_;
CTelephony::TSubscriberIdV1 iSubscribId;
CTelephony::TSubscriberIdV1Pckg iSubscriberIdPckg;
};
#endif /* TELEPHONYAO_H_ */
实现文g
/*
* TelephonyAO.cpp
*
* Created on:
* Author: frank
*/
#include "TelephonyAO.h"
CTelephonyAO::CTelephonyAO() : CActive(EPriorityStandard), iSubscriberIdPckg(iSubscribId)
{
}
CTelephonyAO::~CTelephonyAO()
{
delete pTelephony_;
pTelephony_ = NULL;
delete iActiveSchedulerWait;
iActiveSchedulerWait = NULL;
}
void CTelephonyAO::ConstructL()
{
pTelephony_ = CTelephony::NewL();
iActiveSchedulerWait = new (ELeave)CActiveSchedulerWait;
CActiveScheduler::Add(this);
}
CTelephonyAO* CTelephonyAO::NewL()
{
CTelephonyAO* pSelf = new(ELeave) CTelephonyAO;
CleanupStack::PushL(pSelf);
pSelf->ConstructL();
CleanupStack::Pop();
return pSelf;
}
void CTelephonyAO::RunL()
{
if (iActiveSchedulerWait->IsStarted())
{
iActiveSchedulerWait->AsyncStop();
}
}
void CTelephonyAO::DoCancel()
{
pTelephony_->CancelAsync(CTelephony::EGetSubscriberIdCancel);
if (iActiveSchedulerWait->IsStarted())
{
iActiveSchedulerWait->AsyncStop();
}
}
void CTelephonyAO::GetNetWorkInfo()
{
Cancel();
pTelephony_->GetSubscriberId(iStatus, iSubscriberIdPckg);
SetActive();
iActiveSchedulerWait->Start();
}
TNetWorkType CTelephonyAO::GetNetWorkId()
{
GetNetWorkInfo();
TNetWorkType vNetWorkType = ENetWorkUnKnow;
if(iSubscribId.iSubscriberId.Length() < 15)
{
vNetWorkType = ENetWorkUnKnow;
}
else
{
TBuf<6> vHeader;
vHeader.Copy(iSubscribId.iSubscriberId.Left(5));
TBuf<3> vPtrTemp;
vPtrTemp.Copy(vHeader.Right(2));
TInt vNetWorkId = 0;
TLex vLex(vPtrTemp);
vLex.Val(vNetWorkId);
if((vNetWorkId == 1) ||(vNetWorkId == 6))
{
vNetWorkType = ENetWorkUN;
}
else if((vNetWorkId == 0) ||(vNetWorkId == 2))
{
vNetWorkType = ENetWorkCM;
}
else if((vNetWorkId == 3) ||(vNetWorkId == 5))
{
vNetWorkType = ENetWorkTC;
}
else if(vNetWorkId == 20)
{
vNetWorkType = ENetWorkTT;
}
else
{
vNetWorkType = ENetWorkUnKnow;
}
}
return vNetWorkType;
}
至于如何调用Q就可以通过如下单获取了Q不用再自己L较了?/span>
CTelephonyAO* pTelephony = CTelephonyAO::NewL();
CleanupStack::PushL(pTelephony);
TNetWorkType vNetWorkType = pTelephony->GetNetWorkId();
CleanupStack::PopAndDestroy(pTelephony);
好了Q暂时小l如下吧Q感谢啸天兄前h植树?/span>
调用pȝ的浏览器来实现网|览可以根据系l浏览器的状态而决定调用的ҎQ例如当pȝ览器正在用的时候可以用TApaTask::SendMessage ()ҎQ当pȝ览器没有被使用的时候可以用RapaLsSession::StartDocument() Ҏ?/span>
下面是实C码:
TBool CinternetAppUi::ConnectL(const TDesC& addr)
{
const TInt KBrowserUid = 0x10008D39;
TUid id( TUid::Uid( KBrowserUid ) );
TApaTaskList taskList( CEikonEnv::Static()->WsSession() );
TApaTask task = taskList.FindApp( id );
// the system browser is in use
if ( task.Exists() )
{
HBufC8* param8 = HBufC8::NewLC( addr.Length() );
param8->Des().Append( addr );
task.SendMessage( TUid::Uid( 0 ), *param8 ); // Uid is not used
CleanupStack::PopAndDestroy();
}
// the system browser is not in use
else
{
RApaLsSession appArcSession;
User::LeaveIfError(appArcSession.Connect()); // connect to AppArc server
TThreadId id;
appArcSession.StartDocument( addr, TUid::Uid( KBrowserUid ), id );
appArcSession.Close();
}
return ETrue;
}
//其中入口参数addr的格式是“
其中采用以上Ҏ不仅仅可以用于开启网,q可以用于启动安?/span>sis/sisxQ具体示例代码如下:
RApaLsSession installSession;
TThreadId threadId;
TUid uid;
uid.iUid = 0x
installSession.Connect();
installSession.StartDocument(aFileName, uid, threadId);
installSession.Close();
该代码自己没有亲过Q但是从理论上说应该可行Q而且有大牛说uid都不用传q去?/span>
另外播放音乐文gQ网上也说可以通过该方法来实现Q?/span>Uid分别如下Q?/span>
0x
0x
RapaLsSession::StartDocument()功能q是很强大的Q在q里只做摘录Q以后有Z再亲,不过用其打开|页的确可行?br>
Z么要引入虚拟l承Q?/span>
虚拟l承在一般的应用中很用刎ͼ所以也往往被忽视,q也主要是因为在C++中,多重l承是不推荐的,也ƈ不常用,而一旦离开了多重承,虚拟l承完全失M存在的必要(因ؓq样只会降低效率和占用更多的I间Q关于这一点,我自p没有太多深刻的理解,有兴的可以看网l上白杨的作?/span>?/span>RTTI、虚函数和虚?span lang=EN-US>cȝ开销分析及用指?span lang=EN-US>?/span>Q说实话我目前还没看得很明白Q高人可以指点下我)?/span>
以下面的一个例子ؓ例:
#include <iostream.h>
#include <memory.h>
class CA
{
int k; //如果基类没有数据成员Q则在这里多重承编译不会出C义?/span>
public:
void f() {cout << "CA::f" << endl;}
};
class CB : public CA
{
};
class CC : public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
当编译上qC码时Q我们会收到如下的错误提C:
error C2385: 'CD::f' is ambiguous
即编译器无法定你在d.f()中要调用的函?/span>f到底是哪一个。这里可能会让h觉得有些奇怪,命名只定义了一?/span>CA::fQ既然大安z?/span>CAQ那自然是调用?/span>CA::fQؓ什么还无法定呢?
q是因ؓ~译器在q行~译的时候,需要确定子cȝ函数定义Q如CA::f是确定的Q那么在~译CB?/span>CC时还需要在~译器的语法树中生成CB::fQ?/span>CC::f{标识,那么Q在~译CD的时候,׃CB?/span>CC都有一个函?/span>fQ此Ӟ~译器将试图生成q两?/span>CD::f标识Q显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果Ld.f()一句,E序顺利通过~译Q?/span>
要解册个问题,有两个方法:
1、重载函?/span>f()Q此时由于我们明定义了CD::fQ编译器查到CD::f()调用时就无需再像上面一样去逐生成CD::f标识了;
此时CD的元素结构如下:
|CB(CA)|
|CC(CA)|
故此时的sizeof(CD) = 8;Q?/span>CB?/span>CC各有一个元?/span>kQ?/span>
2、用虚拟承:虚拟l承又称作共享承,q种׃n其实也是~译期间实现的,当用虚拟承时Q上面的E序变成下面的形式Q?/span>
#include <iostream.h>
#include <memory.h>
class CA
{
int k;
public:
void f() {cout << "CA::f" << endl;}
};
class CB : virtual public CA //也有一U写法是class CB : public virtual CA
{ //实际上这两种Ҏ都可?/span>
};
class CC : virtual public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
此时Q当~译器确?/span>d.f()调用的具体含义时Q将生成如下?/span>CDl构Q?/span>
|CB|
|CC|
|CA|
同时Q在CB?/span>CC中都分别包含了一个指?/span>CA的虚基类指针列表vbptrQ?/span>virtual base table pointerQ,其中记录的是?/span>CB?/span>CC的元素到CA的元素之间的偏移量。此Ӟ不会生成各子cȝ函数f标识Q除非子c重载了该函敎ͼ从而达?#8220;׃n”的目的(q里的具体内存布局Q可以参看钻矛_l承内存布局Q在白杨的那文章中也有Q?/span>
也正因此Q此时的sizeof(CD) = 12Q两?/span>vbptr + sizoef(int)Q?/span>;
另注Q?/span>
如果CBQ?/span>CC中各定义一?/span>int型变量,?/span>sizeof(CD)变?/span>20(两个vbptr + 3?/span>sizoef(int)
如果CA中添加一?/span>virtual void f1(){}Q?/span>sizeof(CD) = 16Q两?/span>vbptr + sizoef(int)+vptrQ?/span>;
再添?/span>virtual void f2(){}Q?/span>sizeof(CD) = 16不变。原因如下所C:带有虚函数的c,其内存布局上包含一个指向虚函数列表的指针(vptrQ,q跟有几个虚函数无关?/span>
以上内容涉及到类对象内存布局问题Q本难以做过多展开Q先贴这么多Q本文章只是考虑对于虚拟l承q行入门Q至于效率、应用等未作展开。本文在|上文章基础上修改了下而得此篇Q原文蝲?/span>http://blog.csdn.net/billdavid/archive/2004/06/23/24317.aspx
另外关于虚承和虚基cȝ讨论Q博客园有篇文章?/span>虚承与虚基cȝ本质》,ȝ得更l一炏V?/span>
如何?/span>C++中调?/span>C的代?/span>
以前曄ȝq一?http://www.shnenglu.com/franksunny/archive/2007/11/29/37510.html)Q关于在C中如何调?/span>C++的代码,当时q未做完全的展开Q只是简单的做了下调试,最q看C个题目要求实?/span>C?/span>C++中代码的互相调用Q其l果虽然都是通过extern “C”来实?/span>Q但是具体还是有些差别的?/span>
先对C中调?/span>C++代码作个单回:
1?span style="FONT: 7pt 'Times New Roman'"> 对于C++中非cȝ成员函数Q可以简单的?span style="COLOR: red">函数声明前面?/span>extern “C”Q通常函数声明位于头文件中Q当然也可以声明和函数定义一h?/span>cpp?/span>Q在没有声明的情况下Q直接在定义前添?/span>extern “C”也可
2?span style="FONT: 7pt 'Times New Roman'"> 对于C++cȝ成员函数Q则需要另外做一?/span>cpp文gQ将需要调用的函数q行包装?/span>
以上两项的实例参看前?/span>C中如何调?/span>C++代码的文章?/span>
要实?/span>C++中调?/span>C的代码,具体操作Q?/span>
对于C中的函数代码Q要?span style="COLOR: red">?/span>C代码的头文gq行修改Q在其被含入C++代码时在声明中加?/span>extern “C”或?span style="COLOR: red">?/span>C++代码中重新声明一?/span>C函数Q重新声明时d?/span>extern “C”?/span>?/span>
通过以上的说明,我明白一点,那就?span style="COLOR: red">?/span>extern “C”头一定是加在C++的代码文件中才能起作用的?/span>
下面分析一下这个现象的实质原因Q?/span>
C~译器编译函数时不带函数的类型信息,只包含函数符号名字,?/span>C~译器把函数int a(float x)~译成类?/span>_aq样的符PCq接器只要找C调用函数的符P可以连接成功,它假讑֏数类型信息是正确的,q是C~译q接器的~点。?/span>C++~译器ؓ了实现函数重载,~译时会带上函数的类型信息,如他把上面的a函数可能~译?/span>_a_floatq样的符号ؓ了实现重载,注意它还是没有带q回值得信息Q这也是Z?/span>C++不支持采用函数返回值来区别函数重蝲的原因之一Q当Ӟ函数的用者对函数q回值的处理方式Q如忽略Q也是重要原因?/span>
Z以上Q?/span>C调用C++Q首先需要用装函数把对C++的类{的调用装?/span>C函数以便C调用Q于?/span>extern "C"的作用是Q让~译器知道这件事Q然?span style="COLOR: red">?/span>C语言的方式编译和q接装函数Q?span style="COLOR: red">通常是把装函数?/span>C++~译器按C++方式~译Q用?/span>extern "C" 后,~译器便?/span>C的方式编译封装接口,当然接口函数里面?/span>C++语法q是?/span>C++方式~译Q对?/span>C语言部分--调用者,q是?/span>C语言~译Q分别对C++接口部分?/span>C部分~译后,再连接就可以实现C调用C++?/span>Q。相?/span>,C++调用C函数Q?/span>extern "C" 的作用是Q让C++q接器找调用函数的符h采用C的方?/span>Q即使用_a而不?/span>_a_float来找调用函数?/span>
具体CZ误http://www.shnenglu.com/Files/franksunny/CCallCpp.rar
注:如果你用VC6.0~译附g旉到类?/span>“fatal error C1010: unexpected end of file while looking for precompiled header directive”报错的话Q请?/span>bb.c文g做如下处理右键点击项目工E中的该文gQ选择settingQ在c/c++栏,选择PreCompiled headersQ然后设|第一选项Q选择不用预~译头?/span>
今天接到电话面试Q被问到几个问题Q汗颜之余,结一?/span>
1?span style="FONT: 7pt 'Times New Roman'"> 多态是如何实现l定?/span>
多态的l定可以分ؓq行是多态和~译时多?/span>
?/span> ~译时的多态?/span>
~译时的多态性是通过重蝲来实现的。对于非虚的成员来说Q系l在~译ӞҎ传递的参数、返回的cd{信息决定实CU操作?/span>
?/span> q行时的多态?/span>
q行时的多态性就是指直到pȝq行Ӟ才根据实际情况决定实CU操作?/span>C#中,q行时的多态性通过虚成员实现?/span>
~译时的多态性ؓ我们提供了运行速度快的特点Q而运行时的多态性则带来了高度灵zd抽象的特炏V?/span>
今天才正式弄清楚原来虚函数是可以实现q行时多态的Q以前只知道虚函数可以得基cd象的的方法调用派生类的方法?/span>
2?span style="FONT: 7pt 'Times New Roman'"> 析构函数是虚函数的优Ҏ什?/span>
?/span>C++开发的时候,用来做基cȝcȝ析构函数一般都是虚函数。可是,Z么要q样做呢Q下面用一个小例子来说明:
有下面的两个c:
class ClxBase
{
public:
ClxBase() {};
virtual ~ClxBase() {};
virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};
class ClxDerived : public ClxBase
{
public:
ClxDerived() {};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};
代码
ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
输出l果是:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
q个很简单,非常好理解?/span>
但是Q如果把c?/span>ClxBase析构函数前的virtualLQ那输出l果是下面的样子了Q?/span>
Do something in class ClxDerived!
也就是说Q类ClxDerived的析构函数根本没有被调用Q一般情况下cȝ析构函数里面都是释放内存资源Q而析构函C被调用的话就会造成内存泄漏。我x有的C++E序员都知道q样的危险性。当Ӟ如果在析构函C做了其他工作的话Q那你的所有努力也都是白费力气?/span>
所以,文章开头的那个问题的答案就是-Q这样做是ؓ了当用一个基cȝ指针删除一个派生类的对象时Q派生类的析构函C被调用?/span>
当然Qƈ不是要把所有类的析构函数都写成虚函数。因为当c里面有虚函数的时候,~译器会l类d一个虚函数表,里面来存放虚函数指针Q这样就会增加类的存储空间。所以,只有当一个类被用来作为基cȝ时候,才把析构函数写成虚函数?/span>
说实话,q个也是今天才深刻认识到的?/span>
当然q问到很多数据结构和法斚wQ空间复杂度和时间复杂度之类的东东,说真的也是基性的Q的问题Q至于那些东西,自己说实话抛开没用他们已经很长旉了,真可以说忘的差不多了Q考这U真的很怕,也怪^时没怎么用到。不知道大家用的多不Q?/span>
好久没有正式参加q面试了Q今天突然来一ơ觉得自己基q是不够扎实?/span>
C中如何调?/span>C++函数?
前阵子被问及一个在C中如何调?/span>C++函数的问?/span>Q当时简单回{是函?/span>?span lang=EN-US style="COLOR: black">extern "C"声明Q当被问及如何将cd成员函数声明Ӟ一时语塞,后来|上查了下,|上有一译C++之父的文章可以作{,遂拿?span lang=EN-US>Mark一下?span lang=EN-US>
?/span> C++ 函数声明?/span>``extern "C"''Q在你的 C++ 代码里做q个声明Q,然后调用它(在你?/span> C 或?/span> C++ 代码里调用)。例如:
// C++ code:
extern "C" void f(int);
void f(int i)
{
// ...
}
然后Q你可以q样使用 f()Q?/span>
/* C code: */
void f(int);
void cc(int i)
{
f(i);
/* ... */
}
当然Q这招只适用于非成员函数。如果你惌?/span> C 里调用成员函敎ͼ包括虚函敎ͼQ则需要提供一个简单的包装Q?/span>wrapperQ。例如:
// C++ code:
class C
{
// ...
virtual double f(int);
};
extern "C" double call_C_f(C* p, int i) // wrapper function
{
return p->f(i);
}
然后Q你可以这栯?/span> C::f()Q?/span>
/* C code: */
double call_C_f(struct C* p, int i);
void ccc(struct C* p, int i)
{
double d = call_C_f(p,i);
/* ... */
}
如果你想?/span> C 里调用重载函敎ͼ则必L供不同名字的包装Q这h能被 C 代码调用。例?/span>Q?/span>
// C++ code:
void f(int);
void f(double);
extern "C" void f_i(int i) { f(i); }
extern "C" void f_d(double d) { f(d); }
然后Q你可以q样使用每个重蝲?/span> f()Q?/span>
/* C code: */
void f_i(int);
void f_d(double);
void cccc(int i,double d)
{
f_i(i);
f_d(d);
/* ... */
}
注意Q这些技巧也适用于在 C 里调?/span> C++ cdQ即使你不能Q或者不惻I修改 C++ 头文件?/span>
http://www.research.att.com/~bs/bs_faq2.html#callCpp
本来贴出来以后受到很多C/C++朋友的关注,非常荣幸Q在“梦在天”的提醒下Q本人后来又完成了一个Demo工程Q发现和BJ说的有点出入Q希望有高手指点QDemo工程下蝲链接如下Q?/o:p>http://www.shnenglu.com/Files/franksunny/cCallCppDemo.rar
//Ҏl果也就是说Q无W号W号数据是可以存储在有符号型变量内存中的,
//而且有例子在内存块长度一hQ不用强转,直接赋给无符号变量时也可?br>//上述事实可以解释为内存块不变Q采用不同的解码方式解出不同的数?br>//但是d来的时候要注意Q如果有W号转无W号一定要{
//之所以上例unsigned int输出-1Q我q不是很清楚
Reserved String | Meaning | |
Date | $DATE$ | Year/month/day formatted as %04d/%02d/%02d |
$DAY$ | Day of month formatted as %d | |
$DAY_02$ | Day of month formatted as %02d | |
$DAYNAME$ | Three-character abbreviation of day | |
$DAYLONGNAME$ | Full name of day | |
$MONTH$ | Month formatted as %d | |
$MONTH_02$ | Month formatted as %02d | |
$MONTHNAME$ | Three-character abbreviation of month | |
$MONTHLONGNAME$ | Full name of month | |
$YEAR$ | Year formatted as %d | |
$YEAR_02$ | Year formatted as %02d | |
File | $FILE$ | Full filename with path* |
$FILE_UPPER$ | Full filename with path in uppercase* | |
$FILE_BASE$ | Filename without path or extension* | |
$FILE_BASE_UPPER$ | Filename without path or extension in upper case* | |
$FILE_EXT$ | Filename extension* | |
$FILE_EXT_UPPER$ | Filename extension in upper case* | |
$FILE_PATH$ | Path of file* | |
$FILE_PATH_UPPER$ | Path of file in upper case* | |
General | $clipboard$ | Current clipboard |
$end$ | Position of caret after expansion | |
$selected$ | Current selection** | |
$$ | $ | |
GUID | $GUID_DEFINITION$ | Generated GUID formatted for use in a definition |
$GUID_STRING$ | Generated GUID formatted for use in a string | |
$GUID_STRUCT$ | Generated GUID formatted for use in a struct | |
(Note that all instances of GUID reserved words will use a singe generated GUID.) | ||
Refactor | $GeneratedPropertyName$ | Property name generated during Encapsulate Field |
$MethodArg$ | One parameter of the method and its type | |
$MethodArgName$ | One parameter of the method | |
$MethodArgType$ | Type of one parameter of the method | |
$MethodBody$ | Body of implementation | |
$MethodQualifier$ | Optional qualifiers of method | |
$ParameterList$ | Parameters separated by commas | |
$SymbolContext$ | Context and name of method | |
$SymbolName$ | Name of method | |
$SymbolPrivileges$ | Access of method | |
$SymbolStatic$ | Keyword static or blank | |
$SymbolType$ | Return type of method | |
$SymbolVirtual$ | Keyword virtual or blank | |
Time | $HOUR$ | Hour formatted as %d |
$HOUR_02$ | Hour formatted as %02d | |
$MINUTE$ | Minute formatted as %02d | |
$SECOND$ | Second formatted as %02d |
可重入函C不可重入函数
主要用于多Q务环境中Q一个可重入的函数简单来说就是可以被中断的函敎ͼ也就是说Q可以在q个函数执行的Q何时M断它Q{?span lang=EN-US>OS调度下去执行另外一D代码,而返回控制时不会出现什么错误;而不可重入的函数׃使用了一些系l资源,比如全局变量区,中断向量表等Q所以它如果被中断的话,可能会出现问题,q类函数是不能运行在多Q务环境下的?span lang=EN-US>
也可以这L解,重入卌C重复进入,首先它意味着q个函数可以被中断,其次意味着它除了用自己栈上的变量以外不依赖于M环境Q包?span lang=EN-US>staticQ,q样的函数就?span lang=EN-US>purecodeQ纯代码Q可重入Q可以允许有该函数的多个副本在运行,׃它们使用的是分离的栈Q所以不会互相干扰。如果确实需要访问全局变量Q包?span lang=EN-US>staticQ,一定要注意实施互斥手段。可重入函数在ƈ行运行环境中非常重要Q但是一般要问全局变量付出一些性能代h?span lang=EN-US>
~写可重入函数时Q若使用全局变量Q则应通过关中断、信号量Q即P?span lang=EN-US>V操作Q等手段对其加以保护?span lang=EN-US>
说明Q若Ҏ使用的全局变量不加以保护,则此函数׃h可重入性,卛_多个q程调用此函数时Q很有可能有关全局变量变ؓ不可知状态?span lang=EN-US>
CZQ假?span lang=EN-US>Exam?span lang=EN-US>int型全局变量Q函?span lang=EN-US>Squre_Examq回Examqx倹{那么如下函Ch可重入性?span lang=EN-US>
unsigned int example( int para )
{
unsigned int temp;
Exam = para; // Q?span lang=EN-US>**Q?span lang=EN-US>
temp = Square_Exam( );
return temp;
}
此函数若被多个进E调用的话,其结果可能是未知的,因ؓ当(**Q语句刚执行完后Q另外一个用本函数的进E可能正好被Ȁz,那么当新Ȁzȝq程执行到此函数ӞExam赋与另一个不同的para|所以当控制重新回到“temp = Square_Exam( )”后,计算出的temp很可能不是预想中的结果。此函数应如下改q?span lang=EN-US>
unsigned int example( int para ) {
unsigned int temp;
[甌信号量操?span lang=EN-US>] //(1)
Exam = para;
temp = Square_Exam( );
[释放信号量操?span lang=EN-US>]
return temp;
}
(1)若申请不?span lang=EN-US>“信号?span lang=EN-US>”Q说明另外的q程正处于给Exam赋值ƈ计算其^方过E中Q即正在使用此信P,本进E必ȝ待其释放信号后,才可l箋执行。若甌CP则可l箋执行Q但其它q程必须{待本进E释放信号量后,才能再用本信号?span lang=EN-US>
保证函数的可重入性的ҎQ?span lang=EN-US>
在写函数时候尽量用局部变量(例如寄存器、堆栈中的变量)Q对于要使用的全局变量要加以保护(如采取关中断、信号量{方法)Q这h成的函数׃定是一个可重入的函数?span lang=EN-US>
VxWorks中采取的可重入的技术有Q?span lang=EN-US>
* 动态堆栈变量(各子函数有自q立的堆栈I间Q?span lang=EN-US>
* 受保护的全局变量和静态变?span lang=EN-US>
* d变量
--------------------------------------------------
在实时系l的设计中,l常会出现多个Q务调用同一个函数的情况。如果这个函Cq被设计成ؓ不可重入的函数的话,那么不同d调用q个函数时可能修改其他Q务调用这个函数的数据Q从而导致不可预料的后果。那么什么是可重入函数呢Q所谓可重入函数是指一个可以被多个d调用的过E,d在调用时不必担心数据是否会出错。不可重入函数在实时pȝ设计中被视ؓ不安全函数。满下列条件的函数多数是不可重入的Q?span lang=EN-US>
1) 函数体内使用了静态的数据l构Q?span lang=EN-US>
2) 函数体内调用?span lang=EN-US>malloc()或?span lang=EN-US>free()函数Q?span lang=EN-US>
3) 函数体内调用了标?span lang=EN-US>I/O函数?span lang=EN-US>
下面举例加以说明?span lang=EN-US>
A. 可重入函?span lang=EN-US>
void strcpy(char *lpszDest, char *lpszSrc)
{
while(*lpszDest++=*lpszSrc++);
*dest=0;
}
B. 不可重入函数1
charcTemp;//全局变量
void SwapChar1(char *lpcX, char *lpcY)
{
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//讉K了全局变量
}
C. 不可重入函数2
void SwapChar2(char *lpcX,char *lpcY)
{
static char cTemp;//静态局部变?span lang=EN-US>
cTemp=*lpcX;
*lpcX=*lpcY;
lpcY=cTemp;//使用了静态局部变?span lang=EN-US>
}
问题1Q如何编写可重入的函敎ͼ
{:在函C内不讉K那些全局变量Q不使用静态局部变量,坚持只用局部变量,写出的函数就是可重入的。如果必访问全局变量Q记住利用互斥信号量来保护全局变量?span lang=EN-US>
问题2Q如何将一个不可重入的函数改写成可重入的函敎ͼ
{:把一个不可重入函数变成可重入的唯一Ҏ是用可重入规则来重写它。其实很单,只要遵守了几条很Ҏ理解的规则,那么写出来的函数是可重入的?span lang=EN-US>
1) 不要使用全局变量。因为别的代码很可能覆盖q些变量倹{?span lang=EN-US>
2) 在和g发生交互的时候,切记执行cMdisinterrupt()之类的操作,是关闭g中断。完成交互记得打开中断Q在有些pd上,q叫?span lang=EN-US>“q入/退出核?span lang=EN-US>”?span lang=EN-US>
3) 不能调用其它M不可重入的函数?span lang=EN-US>
4) 谨慎使用堆栈。最好先在用前?span lang=EN-US>OS_ENTER_KERNAL?span lang=EN-US>
堆栈操作涉及内存分配Q稍不留就会造成益出D覆盖其他d的数据,所以,误}慎用堆栈!最好别用!很多黑客E序利用了q一点以便系l执行非法代码从而轻松获得系l控制权。还有一些规则,MQ时刻记住一句话Q保证中断是安全的!
实例问题Q曾l设计过如下一个函敎ͼ在代码检视的时候被提醒?span lang=EN-US>bugQ因个函数是不可重入的,Z么?
unsigned int sum_int( unsigned int base )
{
unsigned int index;
static unsigned int sum = 0; // 注意Q是staticcd
for (index = 1; index <= base; index++)
sum += index;
return sum;
}
分析Q所谓的函数是可重入的(也可以说是可预测的)Q即只要输入数据相同应产生相同的输出。这个函C所以是不可预测的,是因ؓ函数中用了static变量Q因?span lang=EN-US>static变量的特征,q样的函数被UCؓQ带“内部存储?span lang=EN-US>”功能的的函数。因此如果需要一个可重入的函敎ͼ一定要避免函数中?span lang=EN-US>static变量Q这U函C?span lang=EN-US>static变量Q用原则是Q能不用量不用?span lang=EN-US>
上面的函数修改为可重入的函敎ͼ只要声?span lang=EN-US>sum变量中的static关键字去掉,变量sum卛_Z?span lang=EN-US>autocd的变量,函数卛_Z个可重入的函数?span lang=EN-US>
当然Q有些时候,在函C是必要使用static变量的,比如当某函数的返回gؓ指针cdӞ则必Lstatic的局部变量的地址作ؓq回|若ؓautocdQ则q回为错指针?/span>
最q{L低层些的单片机程序编E,在一?span lang=EN-US>msp430上要增加一个国际标准时_׃以前?span lang=EN-US>VC中都是拿来用的,没遇到问题,也就不会LI。在单片Z想用标准C里面?span lang=EN-US>timeQ?span lang=EN-US>time_t*Q函数求得系l时_最后结果出不来。后来才知道原来以前是取得的是操作系l的旉Q汗死,单片机没pȝ的啊Q希望能够尽早让我搞嵌入式啊Q呵c?span lang=EN-US>
后来自己弄明白了Q设个时间|然后用单片机晶振累加计数Q还是可以用time.hL实现标准旉计时的,而且方便不用考虑自己d旉转换函数Q以下是具体?span lang=EN-US>time,h的讲解,我就不再展开了?span lang=EN-US>
time.h从头?span lang=EN-US>
本文从介l基概念入手Q探讨了?span lang=EN-US>C/C++中对日期和时间操作所用到的数据结构和函数Qƈ对计时、时间的获取、时间的计算和显C格式等斚wq行了阐q。本文还通过大量的实例向你展CZtime.h头文件中声明的各U函数和数据l构的详l用方法?span lang=EN-US>
关键字:UTCQ世界标准时_Q?span lang=EN-US>Calendar TimeQ日历时_Q?span lang=EN-US>epochQ时间点Q,clock tickQ时钟计时单元)
1Q?/font> 概念
?span lang=EN-US>C/C++中,对字W串的操作有很多值得注意的问题,同样Q?span lang=EN-US>C/C++Ҏ间的操作也有许多值得大家注意的地斏V最q,在技术群中有很多|友也多ơ问到过C++语言中对旉的操作、获取和昄{等的问题。下面,在这文章中Q笔者将主要介绍?span lang=EN-US>C/C++中时间和日期的用方?span lang=EN-US>.
通过学习许多C/C++库,你可以有很多操作、用时间的Ҏ。但在这之前你需要了解一?span lang=EN-US>“旉”?span lang=EN-US>“日期”的概念,主要有以下几个:
Coordinated Universal TimeQ?span lang=EN-US>UTCQ:协调世界Ӟ又称Z界标准时_也就是大家所熟知的格林威L准时_Greenwich Mean TimeQ?span lang=EN-US>GMTQ。比如,中国内地的时间与UTC的时差ؓ+8Q也是UTC+8。美国是UTC-5?span lang=EN-US>
Calendar TimeQ日历时_是用“从一个标准时间点到此时的旉l过的秒?span lang=EN-US>”来表C的旉。这个标准时间点对不同的~译器来说会有所不同Q但对一个编译系l来_q个标准旉Ҏ不变的,该编译系l中的时间对应的日历旉都通过该标准时间点来衡量,所以可以说日历旉?span lang=EN-US>“相对旉”Q但是无Z在哪一个时区,在同一时刻对同一个标准时间点来说Q日历时间都是一L?span lang=EN-US>
epochQ时间点。时间点在标?span lang=EN-US>C/C++中是一个整敎ͼ它用此时的时间和标准旉点相差的U数Q即日历旉Q来表示?span lang=EN-US>
clock tickQ时钟计时单元(而不把它叫做旉滴答ơ数Q,一个时钟计时单元的旉长短是由CPU控制的。一?span lang=EN-US>clock tick不是CPU的一个时钟周期,而是C/C++的一个基本计时单位?span lang=EN-US>
我们可以使用ANSI标准库中?span lang=EN-US>time.h头文件。这个头文g中定义的旉和日期所使用的方法,无论是在l构定义Q还是命名,都具有明昄C语言风格。下面,我将说明?span lang=EN-US>C/C++中怎样使用日期的时间功能?span lang=EN-US>
2Q?计时
C/C++中的计时函数?span lang=EN-US>clock()Q而与其相关的数据cd?span lang=EN-US>clock_t。在MSDN中,查得?span lang=EN-US>clock函数定义如下Q?span lang=EN-US>
clock_t clock( void );
q个函数q回?span lang=EN-US>“开启这个程序进E?span lang=EN-US>”?span lang=EN-US>“E序中调?span lang=EN-US>clock()函数”时之间的CPU旉计时单元Q?span lang=EN-US>clock tickQ数Q在MSDN中称之ؓ挂钟旉Q?span lang=EN-US>wal-clockQ。其?span lang=EN-US>clock_t是用来保存时间的数据cdQ在time.h文g中,我们可以扑ֈ对它的定义:
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif
很明显,clock_t是一个长整Ş数。在time.h文g中,q定义了一个常?span lang=EN-US>CLOCKS_PER_SECQ它用来表示一U钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
可以看到每过千分之一U(1毫秒Q,调用clockQ)函数q回的值就?span lang=EN-US>1。下面D个例子,你可以用公?span lang=EN-US>clock()/CLOCKS_PER_SEC来计一个进E自w的q行旉Q?span lang=EN-US>
void elapsed_time()
{
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);
}
当然Q你也可以用clock函数来计你的机器运行一个@环或者处理其它事件到底花了多时_
#include “stdio.h”
#include “stdlib.h”
#include “time.h”
int main( void )
{
long i =
clock_t start, finish;
double duration;
/* 量一个事件持l的旉*/
printf( "Time to do %ld empty loops is ", i );
start = clock();
while( i-- ) ;
finish = clock();
duration = (double)(finish - start) / CLOCKS_PER_SEC;
printf( "%f seconds\n", duration );
system("pause");
}
在笔者的机器上,q行l果如下Q?span lang=EN-US>
Time to do 10000000 empty loops is 0.03000 seconds
上面我们看到旉计时单元的长度ؓ1毫秒Q那么计时的_ֺ也ؓ1毫秒Q那么我们可不可以通过改变CLOCKS_PER_SEC的定义,通过把它定义的大一些,从而计时_ֺ更高呢?通过试Q你会发现这h不行的。在标准C/C++中,最的计时单位是一毫秒?span lang=EN-US>
3Q与日期和时间相关的数据l构
在标?span lang=EN-US>C/C++中,我们可通过tml构来获得日期和旉Q?span lang=EN-US>tml构?span lang=EN-US>time.h中的定义如下Q?span lang=EN-US>
#ifndef _TM_DEFINED
struct tm {
int tm_sec; /* U?span lang=EN-US> – 取值区间ؓ[0,59] */
int tm_min; /* ?span lang=EN-US> - 取值区间ؓ[0,59] */
int tm_hour; /* ?span lang=EN-US> - 取值区间ؓ[0,23] */
int tm_mday; /* 一个月中的日期 - 取值区间ؓ[1,31] */
int tm_mon; /* 月䆾Q从一月开始,0代表一月) - 取值区间ؓ[0,11] */
int tm_year; /* q䆾Q其值等于实际年份减?/font>1900 */
int tm_wday; /* 星期 – 取值区间ؓ[0,6]Q其?span lang=EN-US>0代表星期天,1代表星期一Q以此类?/font> */
int tm_yday; /* 从每q的
int tm_isdst; /* 夏o时标识符Q实行夏令时的时候,tm_isdst为正。不实行夏o时的q候,tm_isdst?span lang=EN-US>0Q不了解情况Ӟtm_isdst()?/font>*/
};
#define _TM_DEFINED
#endif
ANSI C标准UC?span lang=EN-US>tml构的这U时间表CZؓ分解旉(broken-down time)?span lang=EN-US>
而日历时_Calendar TimeQ是通过time_t数据cd来表C的Q用time_t表示的时_日历旉Q是从一个时间点Q例如:
#ifndef _TIME_T_DEFINED
typedef long time_t; /* 旉?/font> */
#define _TIME_T_DEFINED /* 避免重复定义 time_t */
#endif
大家可能会生疑问:既然time_t实际上是长整型,到未来的某一天,从一个时间点Q一般是
?span lang=EN-US>time.h头文件中Q我们还可以看到一些函敎ͼ它们都是?span lang=EN-US>time_t为参数类型或q回值类型的函数Q?span lang=EN-US>
double difftime(time_t time1, time_t time0);
time_t mktime(struct tm * timeptr);
time_t time(time_t * timer);
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
此外Q?span lang=EN-US>time.hq提供了两种不同的函数将日历旉Q一个用time_t表示的整敎ͼ转换为我们^时看到的把年月日时分U分开昄的时间格?span lang=EN-US>tmQ?span lang=EN-US>
struct tm * gmtime(const time_t *timer); struct tm * localtime(const time_t * timer);
通过查阅MSDNQ我们可以知?span lang=EN-US>Microsoft C/C++ 7.0中时间点的|time_t对象的|是从
4Q与日期和时间相关的函数及应?span lang=EN-US>
在本节,我将向大家展C怎样利用time.h中声明的函数Ҏ间进行操作。这些操作包括取当前旉、计时间间隔、以不同的Ş式显C时间等内容?span lang=EN-US>
4.1 获得日历旉
我们可以通过time()函数来获得日历时_Calendar TimeQ,其原型ؓQ?span lang=EN-US>
time_t time(time_t * timer);
如果你已l声明了参数timerQ你可以从参?span lang=EN-US>timerq回现在的日历时_同时也可以通过q回D回现在的日历旉Q即从一个时间点Q例如:
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
printf("The Calendar Time now is %d\n",lt);
return 0;
}
q行的结果与当时的时间有养I我当时运行的l果是:
The Calendar Time now is 1122707619
其中1122707619是我运行程序时的日历时间。即?st1:chsdate w:st="on" Year="1970" Month="1" Day="1" IsLunarDate="False" IsROCDate="False">1970q?span lang=EN-US>1?span lang=EN-US>1?span lang=EN-US>0?span lang=EN-US>0?span lang=EN-US>0U?/st1:chsdate>到此时的U数?span lang=EN-US>
4.2 获得日期和时?span lang=EN-US>
q里说的日期和时间就是我们^时所说的q、月、日、时、分、秒{信息。从W?span lang=EN-US>2节我们已l知道这些信息都保存在一个名?span lang=EN-US>tm的结构体中,那么如何一个日历时间保存ؓ一?span lang=EN-US>tml构的对象呢Q?span lang=EN-US>
其中可以使用的函数是gmtime()?span lang=EN-US>localtime()Q这两个函数的原型ؓQ?span lang=EN-US>
struct tm * gmtime(const time_t *timer);
struct tm * localtime(const time_t * timer);
其中gmtime()函数是将日历旉转化Z界标准时_x林尼L_Qƈq回一?span lang=EN-US>tml构体来保存q个旉Q?span lang=EN-US>localtime()函数是将日历旉转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *local;
time_t t;
t=time(NUL);
local=localtime(&t);
printf("Local hour is: %d\n",local->tm_hour);
local=gmtime(&t);
printf("UTC hour is: %d\n",local->tm_hour);
return 0;
}
q行l果是:
Local hour is: 15
UTC hour is: 7
4.3 固定的时间格?span lang=EN-US>
我们可以通过asctime()函数?span lang=EN-US>ctime()函数时间以固定的格式显C出来,两者的q回值都?span lang=EN-US>char*型的字符丌Ӏ返回的旉格式为:
星期?月䆾日期 ?span lang=EN-US>:?span lang=EN-US>:U?q?/font>\n\0
例如Q?span lang=EN-US>Wed Jan 02 02:03:55 1980\n\0
其中\n是一个换行符Q?span lang=EN-US>\0是一个空字符Q表C字W串l束。下面是两个函数的原型:
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
其中asctime()函数是通过tml构来生成具有固定格式的保存旉信息的字W串Q?span lang=EN-US>ctime()是通过日历旉来生成时间字W串。这L话,asctimeQ)函数只是?span lang=EN-US>tml构对象中的各个域填到时间字W串的相应位|就行了Q?span lang=EN-US>ctimeQ)函数需要先参照本地的时间设|,把日历时间{化ؓ本地旉Q然后再生成格式化后的字W串。在下面Q如?span lang=EN-US>t是一个非I的time_t变量的话Q那么:
printf(ctime(&t));
{h于:
struct tm *ptr;
ptr=localtime(&t);
printf(asctime(ptr));
那么Q下面这个程序的两条printf语句输出的结果就是不同的了(除非你将本地时区设ؓ世界标准旉所在的时区Q:
#include "time.h"
#include "stdio.h"
int main(void)
{
struct tm *ptr;
time_t lt;
lt =time(NUL);
ptr=gmtime(<);
printf(asctime(ptr));
printf(ctime(<));
return 0;
}
q行l果Q?span lang=EN-US>
Sat Jul 30 08:43:03 2005
Sat Jul 30 16:43:03 2005
4.4 自定义时间格?span lang=EN-US>
我们可以使用strftimeQ)函数时间格式化为我们想要的格式。它的原型如下:
size_t strftime(
char *strDest,
size_t maxsize,
const char *format,
const struct tm *timeptr
);
我们可以Ҏformat指向字符串中格式命o?span lang=EN-US>timeptr中保存的旉信息攑֜strDest指向的字W串中,最多向strDest中存?span lang=EN-US>maxsize个字W。该函数q回?span lang=EN-US>strDest指向的字W串中放|的字符数?span lang=EN-US>
函数strftime()的操作有些类gsprintf()Q识别以癑ֈ?span lang=EN-US>(%)开始的格式命o集合Q格式化输出l果攑֜一个字W串中。格式化命o说明?span lang=EN-US>strDest中各U日期和旉信息的确切表C方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大写的?span lang=EN-US>
%a 星期几的?/font>
%A 星期几的全称
%b 月分的简?/font>
%B 月䆾的全U?/font>
%c 标准的日期的旉?/font>
%C q䆾的后两位数字
%d 十进制表C的每月的第几天
%D ?span lang=EN-US>/?span lang=EN-US>/q?/font>
%e 在两字符域中Q十q制表示的每月的W几?/font>
%F q?span lang=EN-US>-?span lang=EN-US>-?/font>
%g q䆾的后两位数字Q用基于周的年
%G q分Q用基于周的年
%h 写的月䆾?/font>
%H 24时制的时
%I 12时制的时
%j 十进制表C的每年的第几天
%m 十进制表C的月䆾
%M 十时制表C的分钟?/font>
%n 新行W?/font>
%p 本地?span lang=EN-US>AM?span lang=EN-US>PM的等hC?/font>
%r 12时的时?/font>
%R 昄时和分钟:hh:mm
%S 十进制的U数
%t 水^制表W?/font>
%T 昄时分U:hh:mm:ss
%u 每周的第几天Q星期一为第一?Qg0?span lang=EN-US>6Q星期一?span lang=EN-US>0Q?/font>
%U W年的第几周Q把星期日做为第一天(g0?span lang=EN-US>53Q?/font>
%V 每年的第几周Q用基于周的年
%w 十进制表C的星期几(g0?span lang=EN-US>6Q星期天?span lang=EN-US>0Q?/font>
%W 每年的第几周Q把星期一做ؓW一天(g0?span lang=EN-US>53Q?/font>
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十q制q䆾Qg0?span lang=EN-US>99Q?/font>
%Y 带世U部分的十进制年?/font>
%zQ?span lang=EN-US>%Z 时区名称Q如果不能得到时区名U则q回I字W?/font>
%% 癑ֈ?span lang=EN-US>
如果xC现在是几点了,q以12时制显C,p下面q段E序Q?span lang=EN-US>
#include “time.h”
#include “stdio.h”
int main(void)
{
struct tm *ptr;
time_t lt;
char str[80];
lt=time(NUL);
ptr=localtime(<);
strftime(str,100,"It is now %I %p",ptr);
printf(str);
return 0;
}
其运行结果ؓQ?/font>
It is now 4PM
而下面的E序则显C当前的完整日期Q?span lang=EN-US>
#include <stdio.h>
#include <time.h>
void main( void )
{
struct tm *newtime;
char tmpbuf[128];
time_t lt1;
time( <1 );
newtime=localtime(<1);
strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);
printf(tmpbuf);
}
q行l果Q?span lang=EN-US>
Today is Saturday, day 30 of July in the year 2005.
4.5 计算持箋旉的长?span lang=EN-US>
有时候在实际应用中要计算一个事件持l的旉长度Q比如计打字速度。在W?span lang=EN-US>1节计旉分中Q我已经?span lang=EN-US>clock函数举了一个例子?span lang=EN-US>Clock()函数可以_到毫U。同Ӟ我们也可以?span lang=EN-US>difftime()函数Q但它只能精到U。该函数的定义如下:
double difftime(time_t time1, time_t time0);
虽然该函数返回的以秒计算的时间间隔是doublecd的,但这q不说明该时间具有同double一L_度,q是由它的参数觉得的Q?span lang=EN-US>time_t是以Uؓ单位计算的)。比如下面一D늨序:
#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
time_t start,end;
start = time(NUL);
system("pause");
end = time(NUL);
printf("The pause used %f seconds.\n",difftime(end,start));//<-
system("pause");
return 0;
}
q行l果为:
hL键l?/font>. . .
The pause used 2.000000 seconds.
hL键l?span lang=EN-US>. . .
可以惛_Q暂停的旉q不那么巧是整整2U钟。其实,你将上面E序的带?span lang=EN-US>“//<-”注释的一行用下面的一行代码替换:
printf("The pause used %f seconds.\n",end-start);
其运行结果是一L?span lang=EN-US>
4.6 分解旉转化为日历时?span lang=EN-US>
q里说的分解旉是以年、月、日、时、分、秒{分量保存的旉l构Q在C/C++中是tml构。我们可以?span lang=EN-US>mktimeQ)函数用tml构表示的时间{化ؓ日历旉。其函数原型如下Q?span lang=EN-US>
time_t mktime(struct tm * timeptr);
其返回值就是{化后的日历时间。这h们就可以先制定一个分解时_然后对这个时间进行操作了Q下面的例子可以计算?st1:chsdate w:st="on" Year="1997" Month="7" Day="1" IsLunarDate="False" IsROCDate="False">1997q?span lang=EN-US>7?span lang=EN-US>1?/st1:chsdate>是星期几Q?span lang=EN-US>
#include "time.h"
#include "stdio.h"
#include "stdlib.h"
int main(void)
{
struct tm t;
time_t t_of_day;
t.tm_year=1997-1900;
t.tm_mon=6;
t.tm_mday=1;
t.tm_hour=0;
t.tm_min=0;
t.tm_sec=1;
t.tm_isdst=0;
t_of_day=mktime(&t);
printf(ctime(&t_of_day));
return 0;
}
q行l果Q?/font>
Tue Jul 01 00:00:01 1997
现在注意了,有了mktime()函数Q是不是我们可以操作现在之前的Q何时间呢Q你可以通过q种办法出1945q?span lang=EN-US>8?span lang=EN-US>15h星期几吗Q答案是否定的。因个时间在
?/span>switch选择l构理解局部变?/span>
函数体内部自定义变量Q称为局部变量,存储于栈Q?/span>stackQ中Q由~译器自动分配和释放Q局部变量的生存期(或者说作用域)是当前函数内部,使用时必d始化Q否则其值将不定。以前对局部变量的定义也就是这么多Q而且也就那么在用。近期碰到如下一个问题:
void func( void )
{
int x = 2;
switch ( x )
{
int m =0; //initialization skipped by case0,case1,case2,default
case 0 :
int i = 0; //initialization skipped by case1,case2,default
{ int j = 1; } // OK, initialized in enclosing block
break;
case 1 :
break;
case 2:
break;
default:
int k = 1; // OK, initialization not skipped
}
}
遇到q个问题Q网上的解答很多Q很多h觉得switch内不能定义局部变量,q个明显是不对的。因为我把代码改成以下Ş式后完全可以用了?/span>
void func( void )
{
int x = 2;
switch ( x )
{
int m;
m = 0; //without execute;
case 0:
int i;
i = 0;
{ int j = 1; } // OK, initialized in enclosing block
printf("%d %d\n", m, i);
break;
case 1:
i = 1;
printf("%d %d\n", m, i);
break;
case 2:
i = 2;
printf("%d %d\n", m, i);
break;
default:
int k = 1; // OK, initialization not skipped
}
}
~译时有一?/span>warningQ即“local variable 'm' used without having been initialized”Q执行结果ؓQ?/span>-858993460 2
因此switch内不但可以定义变量,而且也不用像很多人所说的?/span>case内遇到要用变量时一定要?/span>{}括v来,不过严格的说不用{}扩v来的变量是是属于整个switch块结构的Qؓ此编E一定要新增变量作用域限定?/span>case内就必须要用{}?/span>
通过switch···casel构Q对局部变量的声明、定义以及初始化{概念可以有一个比较清晰的认识。我的理解就是:声明语句不管是放在哪里,其编译时都是其|顶到块的头部,?/span>int k虽然?/span>default中,但是q个变量的声明就?/span>switch?/span>{}内,其生存期与变?/span>m{同Q只是由于前面没有声明,所?/span>default之前不能用?/span>
昨天ȝ讯(杭州Q笔试了Q做了下W试题,感觉题目都不难,但是自己做的的确不怎么P估计是没ZMQ不q暂时还是先把几道自p记得的题目,写出来,ȝ下,以做复习?/span>
1?span style="FONT: 7pt 'Times New Roman'"> 要求自己实现 String c,l出?/span> String cȝ以下头文件类声明
class String
{
public:
String(const char *m_char = NULL);
String(const String & Str);
String& operator = (const String &Str);
~String();
private:
char * m_Data;
};
关于 String cȝW试题,以前看林锐的随笔时听说他在微软面试时曄到那么一道题目,我自׃没有真的下笔dq,q_都是拿来q的,q次自己到Q才知道会死得那么惨Q反正编得不堪入目(我就不拿出来献丑了)Q下面是我回来后Q自己重新写的答案?/span>
String::String(const char* m_char)
{
int m_nLength = strlen(m_char) + 1;
if (m_Data != NULL)
{
delete [] m_Data;
m_Data = NULL;
}// 以上判断是否必要 ??
m_Data = new char[m_nLength];
memcpy(m_Data, m_char, m_nLength);
}
String::String(const String &Str)
{
int m_nLength = strlen(Str.m_Data) + 1;// 以前真的不知道,原来对象的私有变?/span>
// 在类的实C码中也是可以讉K?/span>
if (m_Data != NULL)
{
delete [] m_Data;
m_Data = NULL;
}// 以上判断是否必要 ??
m_Data = new char[m_nLength];
memcpy(m_Data, Str.m_Data, m_nLength);
}
String& String::operator = (const String& Str)
{
if(this == &Str)
return *this;
int m_nLength = strlen(Str.m_Data) + 1;
if (m_Data != NULL)
{
delete [] m_Data;
m_Data = NULL;
}// 以上判断是否必要 ??
m_Data = new char[m_nLength];
memcpy(m_Data, Str.m_Data, m_nLength);
return *this;
}
String::~String()
{
if (m_Data != NULL)
{
delete [] m_Data;
m_Data = NULL;
}
}
2?span style="FONT: 7pt 'Times New Roman'"> 关于内存分配
q个题目很简单,q了一个函敎ͼ然后问函数内的局部变量存攑֜哪里Q我也不知道Z么当时会选择 heap( ?/span> ) Q下面再把几个概늽列出来:
1. 堆区Q?/span> heap Q:q序员甌分配和释放,属动态内存分配方式,若程序员不释放,E序l束时可能会?/span> OS 回收。不q这个内存分配很Ҏ引v问题Q如果申L内存不释攑ְ会造成内存泄漏Q如果释攄不是所要释攄内存Q则轻者引L序运行结果出错,重者系l崩溃?/span>
2. 栈区Q?/span> stack Q:~译器自动分配释放,存放函数的Ş参倹{局部变量的|也是属于动态内存分配方式,它由pȝ分配Q所以执行效率也高,不过自由度小Q声明时得军_其具体大?/span>
3. 全局区(静态区Q( static Q:全局变量和静态变量的存储是放在一块的Q而且初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在盔R的另一块区域。程序结束后ql释放,所以也不会造成内存问题?/span>
除了以上的变量外Q还有两cd放位|,文字帔R区和E序代码区,两者都是由pȝ分配和释放,且文字常量区和前面三区合成ؓE序数据区,与程序代码区相对应?/span>
3?span style="FONT: 7pt 'Times New Roman'"> 关于cȝ承的构造和析构函数
class Base
{
public:
Base(){cout<< "Base" <<endl;};
~Base(){cout<<"~Base"<<endl;};
protected:
private:
};
class First:public Base
{
public:
First(){cout << "First" << endl;};
~First(){cout << "~First" <<endl;};
};
int main()
{
Base *a = new First;
delete a;
}
问程序的输出会是什么?
l果很简单,也就?/span> Base
First
~Base
其它q有一个关?/span> & 的题目,把我搞的云里N的,q要再看些东西才知道怎么来解释?/span>