?
Richard zeng? 3/19/2006 10:50:36 AM
?
這幾天又把以前的
C
課程翻了出來
,
因為自己對
C
的指針和數組不是很
DEV.
模擬
C
庫函數中的轉化大小寫函數
.
?
//
轉換成大寫
,
函數參數為字符數組
//
利用字符串數組的結尾都是
\0
void
ToUpper(chars[])
{
??????
int
i=0;
??????
while(s[i++]!='\0' )
?????? {
????????????? //
判斷是否是小寫字母
?????????????
if(s[i]>='a' && s[i]<='z' )
????????????????????
s[i] -= 32;???? //
小寫字母比大寫字母的
ASCII
大
32
?????? }
}
?
//
轉換成大寫
,
函數參數為字符指針
void
ToUpperPtr(char* s)
{
??????
while(*s != '\0')
?????? {
//
判斷是否是小寫字母
?????????????
if(*s >='a' && *s <='z')
???????????????????? *s -= 32; ???? //
小寫字母比大寫字母的
ASCII
大
32
?????????????
s++; //
指針的地址
++
?????? }
}
封裝了一下波形顯示,發現不是很難
WaveShow_src.rar
ASCII碼表大家都很熟悉了吧,利用碼的排列規律,我們可以很容易的實現一些操作,比如判斷是否是數字、大小寫轉換等。
這里寫大小寫轉換的函數:
char toUpper(const char& ch) { return ch & 0x5F; } char toLower(const char& ch) { return ch | 0x20; } |
函數原理:大小寫字母的差是32,比如大寫的A是65,小寫的A是97,所以我們把右邊數第6位置0或者1就能實現大小寫轉換。轉換成大寫時,把第6位置0,用ch & 0x5F實現。轉換成小寫時置1,用ch | 0x20實現。怎么樣,相當的簡單吧,由此,我們可以寫string類的toUpper和toLower函數了。^_^,更多技巧盡在探索中。
CMPP協議的全稱是中國移動通信互聯網短信網關接口協議,它是聯想亞信公司根據SMMP協議為中國移動量身定做的,是符合中國國情的一個短信協議,閑話不多說了,說說CMPP的主要功能吧。(1)短信發送(short message mobile originate)MO,就是手機給SP發短信;(2)短信接受(short message mobile terminated)MT,這個就是SP給手機發的短信了,通常我們手機上收到的不良短信就是SP給我們的MT。CMPP協議的通信基礎是TCP/IP為底層通信承載的,連接方式是長連接方式。SP與ISMG之間,SMSC和ISMG之間的交互過程中均采用異步方式,即任一個網元在收到請求消息后應立即回應。
下面看看它的消息定義:CMPP中的消息分為消息頭和消息體。消息頭定義如下
字段名 |
字節數 |
類型 |
描述 |
Total_Length |
4 |
Unsigned Integer |
消息總長度(含消息頭及消息體) |
Command_Id |
4 |
Unsigned Integer |
命令或響應類型 |
Sequence_Id |
4 |
Unsigned Integer |
消息流水號,順序累加,步長為1,循環使用(一對請求和應答消息的流水號必須相同) |
那么下面就是SP連接到ISMG上了,看它的Bind連接消息定義
字段名 |
字節數 |
屬性 |
描述 |
Source_Addr |
6 |
Octet String |
源地址,此處為SP_Id,即SP的企業代碼。 |
AuthenticatorSource |
16 |
Octet String |
用于鑒別源地址。其值通過單向MD5 hash計算得出,表示如下:
AuthenticatorSource =
MD5(Source_Addr+9 字節的0 +shared secret+timestamp)
Shared secret 由中國移動與源地址實體事先商定,timestamp格式為:MMDDHHMMSS,即月日時分秒,10位。 |
Version |
1 |
Unsigned Integer |
雙方協商的版本號(高位4bit表示主版本號,低位4bit表示次版本號),對于3.0的版本,高4bit為3,低4位為0 |
Timestamp |
4 |
Unsigned Integer |
時間戳的明文,由客戶端產生,格式為MMDDHHMMSS,即月日時分秒,10位數字的整型,右對齊 。 |
根據上的定義我們可以寫出的代碼,如下,在VC環境下編寫的
/*
*函數功能:建立和CMPP網關的直接通路
*輸入條件:SP用戶名const char *UserName,SP密碼const char *PWD
*/
void Ccmpp_API::CmppConnect(const char *UserName, const char *PWD)
{
char netbuf[100];
CMPP_CONNECT *bufer;
bufer=(CMPP_CONNECT*)netbuf;
memset(bufer, 0, 100);
bufer->nTotalLength = htonl(39);//CMPP_CONNECT消息總長度
bufer->nCommandId = htonl(CMPP_CONNECT_tag);//消息標志
//自動產生SeqId號
if (sequenceid == 123456789i32)
{
sequenceid = 1;
}else{
sequenceid++;
}
bufer->nSeqId = htonl(sequenceid);
int MD5Len;
MD5_CTX md5;//MD5源字串
CTime TimeData = CTime::GetCurrentTime();
CString timestamp = TimeData.Format("%m%d%H%M%S");
unsigned char md5source[29];
int Len1 = strlen(UserName);
int Len2 = strlen(PWD);
MD5Len = Len1 + 9 +Len2 + timestamp.GetLength();
memset(md5source, 0, MD5Len);
memcpy(bufer->sSourceAddr, UserName, Len1);
memcpy(md5source, UserName, Len1);
for (int j = 0; j<Len2; j++)
{
md5source[j + Len1 + 9] = PWD[j];
}
for (int i=0;i<timestamp.GetLength();i++)
{
md5source[i + Len2 + Len1 + 9]=timestamp[i];
}
//進行md5加密轉換
md5.MD5Update(md5source, MD5Len);
md5.MD5Final(md5source);
memcpy(bufer->sAuthSource, md5source, 29);
bufer->cVersion = 0x30;
bufer->nTimeStamp = htonl(atoi(timestamp));
CmppSocket.Send(bufer, 39, 0);//把消息打包發送
return;
}
今天就到這,下次再寫,歡迎交流!
posted on 2006-03-15 07:58
炙熱的太陽 閱讀(38)
評論(2) 編輯 收藏 收藏至365Key
FeedBack:
2006-03-15 10:38 |
you say:
SP與ISMG之間,SMSC和ISMG之間的交互過程中均采用異步方式,即任一個網元在收到請求消息后應立即回應。
既然是異步方式,就不是收到請求后立即回應,否則就是同步方式了
據我所知,CMPP采用的基于滑動窗口的異步方式,默認情況下可以發最多16的CMPP package,而不必等待他們的resp.
回復
2006-03-15 11:21 |
是的,你說的沒有錯。
消息是采用并發方式發送,加以滑動窗口流量控制,窗口大小參數W可配置,現階段的配置為16,即接收方在應答前一次收到的消息最多不超過16條。這是它們之間的通信方式。
而SP與ISMG之間,SMSC和ISMG之間的交互過程中均采用異步方式,即任一個網元在收到請求消息后應立即回應。這是它們交互過程中的應答方式。即收到一個消息就應該回一個回應消息,而不管對方是否收到,所以上面講的并沒有錯喲。
回復
摘要: 最近在VS2005下實現一個模版堆棧時,想重載一下輸出運算符。結果老是遇到問題,如何都過不去,想不想去都不明白。還望高手指教。 一開始同樣的程序在VC2005和VC6.0下編譯都沒問題,但是一到鏈接的時候就出現問題了。都提示如下錯誤:
error LNK2019: 無法解析的外部符號 "cla...
閱讀全文
對于回調函數的編寫始終是寫特殊處理功能程序時用到的技巧之一。先介紹一下回調的使用基本方法與原理。
1、在這里設:回調函數為A()(這是最簡單的情況,不帶參數,但我們應用的實際情況常常很會復雜),使用回調函數的操作函數為B(), 但B函數是需要參數的,這個參數就是指向函數A的地址變量,這個變量一般就是函數指針。使用方法為:
int A(char *p); // 回調函數
typedef int(*CallBack)(char *p) ; // 聲明CallBack 類型的函數指針
CallBack myCallBack ; // 聲明函數指針變量
myCallBack = A; // 得到了函數A的地址
B函數一般會寫為 B(CallBack lpCall,char * P,........); // 此處省略了p后的參數形式 。
所以回調機制可解為,函數B要完成一定功能,但他自己是無法實現全部功能的。 需要借助于函數A來完成,也就是回調函數。B的實現為:
B(CallBack lpCall,char *pProvide)
{
........... // B 的自己實現功能語句
lpCall(PpProvide); // 借助回調完成的功能 ,也就是A函數來處理的。
........... // B 的自己實現功能語句
}
// -------------- 使用例子 -------------
char *p = "hello!";
CallBack myCallBack ;
myCallBack = A ;
B(A, p);
以上就是回調的基本應用,本文所說的變身,其實是利用傳入不同的函數地址,實現調用者類與回調函數所在類的不同轉換。
1、問題描述
CUploadFile 類完成數據上傳,與相應的界面進度顯示。
主要函數Send(...) 和回調函數 GetCurState() ;
class CUploadFile : public CDialog
{
......
int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ;
static int GetCurState(int nCurDone, int nInAll, void * pParam) ;
......
}
int CUploadFile ::Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath)
{
... // 導出傳輸數據的函數
int ret = Upload( (LPSTR)(LPCTSTR)m_strData,
GetCurState, // 在這個回調函數中處理界面
this, // CUploadFile 的自身指針 ,也就是pParam 所接受的參數
(LPSTR)(LPCTSTR)UploadFilePath,
"",
"",
);
}
int CUploadFile ::GetCurState(int nCurData, int nInAll, void * pParam)
{
.........
UploadFile *pThis = (UploadFile *)pParam; // nCurData 當前以傳出的數據量
// nInAll 總的數據量
// 有了pThis可以對界面進行各種操作了。
.............
}
但大家仔細觀察就可以發現,這個類把數據傳送和界面顯示聚和到了一起,不容易得到復用。而且在復用過程中需要改動較多的地方 。
請大家記住現在的回調函數傳入的類本身的靜態成員函數。
現在我們把數據的傳送和界面的顯示分離。回調則要傳入的是界面處理類的靜態函數。
界面處理類 CShowGUI,數據上傳類 CUploadData
class CUploadData
{
......
typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam);
int UploadFile(LPCTSTR lpFileNamePath,LPVOID lparam,SetUploadCaller Caller );
// 接受外界出入的參數,主要是回調函數的地址通過參數Caller,
int Send(LPCTSTR lpServerIP, LPCTSTR lpServerPort, LPCTSTR UploadFilePath) ;
...... // 注意此時不在需要GetCurState 函數了 。
}
class CShowGUI: public CDialog
{
.......
typedef int(*SetUploadCaller)(int nCurData, int nInAll, void * pParam);
void SetCallBack(LPCTSTR strPath);
static int GetCurState(int nCurData, int nInAll, void * pParam) ;
CUploadData m_Uploa
d ; // 數據上傳類是界面顯示類的一個成員變量。
.......
}
void CShowGUI :: SetCallBack(LPCTSTR strPath)
{
CUploadData myUploadData ;
SetUploadCaller myCaller; // 聲明一個函數指針變量
myCaller = CurState ; // 取得界面處理函數的地址
myUploadData .UploadFile(strPath,this,myCaller); // 界面處理類的函數傳入,實現了數據傳入與界面處理的分離 .
}
通過上面的演示做到了界面與數據的分離,回調函數分別扮演了不同角色,所以隨著處理問題的不同應靈活應用,但同樣因為處理數據類不知道界面處理類或外部調用類的類型,而更無法靈活地處理界面的不同顯示方式。這方面還希望喜歡鉆研技術的朋友繼續研究。
指針,在C/C++語言中一直是很受寵的;幾乎找不到一個不使用指針的C/C++應用。用于存儲數據和程序的地址,這是指針的基本功能。用于指向整型數,用整數指針(int*);指向浮點數用浮點數指針(float*);指向結構,用對應的結構指針(struct xxx *);指向任意地址,用無類型指針(void*)。
有時候,我們需要一些通用的指針。在C語言當中,(void*) 可以代表一切;但是在C++中,我們還有一些比較特殊的指針,無法用(void*)來表示。事實上,在C++中,想找到一個通用的指針,特別是通用的函數指針簡直是一個“不可能任務”。
C++是一種靜態類型的語言,類型安全在C++中舉足輕重。在C語言中,你可以用void*來指向一切;但在C++中,void*并不能指向一切,就算能,也失去了類型安全的意義了。類型安全往往能幫我們找出程序中潛在的一些BUG。
下面我們來探討一下,C++中如何存儲各種類型數據的指針。
C++指針探討 (一)數據指針 沐楓網志 1. 數據指針
數據指針分為兩種:常規數據指針和成員數據指針
1.1 常規數據指針
這個不用說明了,和C語言一樣,定義、賦值是很簡單明了的。常見的有:int*, double* 等等。
如:
int value = 123;
int * pn = &value;
1.2 成員數據指針
有如下的結構:
struct MyStruct
{
int key;
int value;
};
現在有一個結構對象:
MyStruct me;
MyStruct* pMe = &me;
我們需要 value 成員的地址,我們可以:
int * pValue = &me.value;
//或
int * pValue = &pMe->value;
當然了,這個指針仍然是屬于第一種范籌----常規數據指針。
好了,我們現在需要一種指針,它指向MyStruct中的任一數據成員,那么它應該是這樣的子:
int MyStruct::* pMV = &MyStruct::value;
//或
int MyStruct::* pMK = &MyStruct::key;
這種指針的用途是用于取得結構成員在結構內的地址。我們可以通過該指針來訪問成員數據:
int value = pMe->*pMV; // 取得pMe的value成員數據。
int key = me.*pMK; // 取得me的key成員數據。
那么,在什么場合下會使用到成員數據指針呢?
確實,成員指針本來就不是一種很常用的指針。不過,在某些時候還是很有用處的。我們先來看看下面的一個函數:
int sum(MyStruct* objs, int MyStruct::* pm, int count)
{
int result = 0;
for(int i = 0; i < count; ++i)
result += objs[i].*pm;
return result;
}
這個函數的功能是什么,你能看明白嗎?它的功能就是,給定count個MyStruct結構的指針,計算出給定成員數據的總和。有點拗口對吧?看看下面的程序,你也許就明白了:
MyStruct me[10] =
{
{1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16},{17,18},{19,20}
};
int sum_value = sum(me, &MyStruct::value, 10);
//計算10個MyStruct結構的value成員的總和: sum_value 值 為 110 (2+4+6+8+
+20)
int sum_key = sum(me, &MyStruct::key, 10);
//計算10個MyStruct結構的key成員的總和: sum_key 值 為 100 (1+3+5+7+
+19)
也許,你覺得用常規指針也可以做到,而且更易懂。Ok,沒問題:
int sum(MyStruct* objs, int count)
{
int result = 0;
for(int i = 0; i < count; ++i)
result += objs[i].value;
return result;
}
你是想這么做嗎?但這么做,你只能計算value,如果要算key的話,你要多寫一個函數。有多少個成員需要計算的話,你就要寫多少個函數,多麻煩啊。
C語言的指針相當的靈活方便,但也相當容易出錯。許多C語言初學者,甚至C語言老鳥都很容易栽倒在C語言的指針下。但不可否認的是,指針在C語言中的位置極其重要,也許可以偏激一點的來說:沒有指針的C程序不是真正的C程序。
然而C++的指針卻常常給我一種束手束腳的感覺。C++比C語言有更嚴格的靜態類型,更加強調類型安全,強調編譯時檢查。因此,對于C語言中最容易錯用的指針,更是不能放過:C++的指針被分成數據指針,數據成員指針,函數指針,成員函數指針,而且不能隨便相互轉換。而且這些指針的聲明格式都不一樣:
數據指針 |
T * |
成員數據指針 |
T::* |
函數指針 |
R (*)(...) |
成員函數指針 |
R (T::*)(...) |
盡管C++中仍然有萬能指針void*,但它卻屬于被批斗的對象,而且再也不能“萬能”了。它不能轉換成成員指針。
這樣一來,C++的指針就變得很尷尬:我們需要一種指針能夠指向同一類型的數據,不管這個數據是普通數據,還是成員數據;我們更需要一種指針能夠指向同一類型的函數,不管這個函數是靜態函數,還是成員函數。但是沒有,至少從現在的C++標準中,還沒有看到。
沐楓網志 C++指針探討(三)成員函數指針
自從有了類,我們開始按照 數據+操作 的方式來組織數據結構;自從有了模板,我們又開始把 數據 和 算法 分離,以便重用,實在夠折騰人的。但不管怎么折騰,現在大多數函數都不再單身,都嫁給了類,進了圍城。可是我們仍然需要能夠自由調用這些成員函數。
考慮一下windows下的定時調用。SetTimer函數的原型是這樣的:
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);

其中,參數就不解釋了,這個函數估計大多數windows開發人員都知道。lpTimerFunc是個會被定時調用的函數指針。假如我們不通過WM_TIMER消息來觸發定時器,而是通過lpTimerFunc來定時工作,那么我們就只能使用普通函數或靜態函數,而無論如何都不能使用成員函數,哪怕通過靜態函數轉調也不行。
再考慮一下線程的創建:
uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
start_address仍然只支持普通函數。不過這回好了,它允許回調函數一個void*參數,它將會arglist作為參數來調用start_address。于是,聰明的C++程序員,就利用arglist傳遞this指針,從而利用靜態函數成功的調用到了成員函數了:
class mythread


{
public:
static void doit(void* pThis)

{
((mythread*)pThis)->doit();
}

void doit()
{
}
};

main()


{

mythread* pmt = new mythread;
_beginthread(&mythread::doit, 0, (void*)pmt);

}
但是顯然,C++程序員肯定不會因此而滿足。這里頭有許多被C++批判的不安定因素。它使用了C++中被認為不安全的類型轉換,不安全的void*指針,等等等等。但這是系統為C語言留下的調用接口,這也就認了。那么假如,我們就在C++程序中如何來調用成員函數指針呢?
如下例,我們打算對vector中的所有類調用其指定的成員函數:
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;

class A


{
int value;
public:

A(int v)
{value = v;}

void doit()
{ cout << value << endl;};
static void call_doit(A& rThis)

{
rThis.doit();
}
};


int main()


{
vector<A> va;
va.push_back(A(1));
va.push_back(A(2));
va.push_back(A(3));
va.push_back(A(4));
//方法1:
//for_each(va.begin(), va.end(), &A::doit); //error
//方法2:
for_each(va.begin(), va.end(), &A::call_doit);
//方法3:
for_each(va.begin(), va.end(), mem_fun_ref<void, A>(&A::doit));

system("Pause");

return 0;
}

方法1,編譯不能通過。for_each只允許具有一個參數的函數指針或函數對象,哪怕A::doit默認有一個this指針參數也不行。不是for_each沒考慮到這一點,而是根本做不到!
方法2,顯然是受到了beginthread的啟發,使用一個靜態函數來轉調用,哈哈成功了。但是不爽!這不是C++。
方法3,呼,好不容易啊,終于用mem_fun_ref包裝成功了成員函數指針。
似乎方法3不錯,又是類型安全的,又可以通用--慢著,首先,它很丑,哪有調用普通C函數指針那么漂亮啊(見方法2),用了一大串包裝,又是尖括號又是圓括號,還少不了&號!其次,它只能包裝不超過一個參數的函數!盡管它在for_each中夠用了,但是你要是想用在超過一個參數的場合,那只有一句話:不可能的任務。
是的,在標準C++中,這是不可能的任務。但事情并不總是悲觀的,至少有許多第三方庫提供了超越mem_fun的包裝。如boost::function等等。但是它也有限制:它所支持的參數仍然是有限的,只有十多個,盡管夠你用的了;同樣,它也是丑陋的,永遠不要想它能夠簡單的用&來搞定。
也許,以失去美麗的代價,來換取質量上的保證,這也是C++對于函數指針的一種無奈吧……
期待C++0x版本。它通過可變模板參數,能夠讓mem_fun的參數達到無限個……
--------
BTW: C++Builder擴展了一個關鍵字 closure ,允許成員函數指針如同普通函數指針一樣使用。也許C++0x能考慮一下……
摘要: 1 2 #ifndef _BITMAP_H 3 #define _BITMAP_H 4 5 #include <windows.h> 6 7 void SaveImage(const char ...
閱讀全文