1。先來介紹REPORT類型的CListCtrl: 首先使用下面的語句設置CListCtrl的style: DWORD SetExtendedStyle( DWORD dwNewStyle ); 其中 LVS_EX_CHECKBOXES 表示添加CheckBox LVS_EX_FULLROWSELECT 表示選擇整行 LVS_EX_GRIDLINES 表示添加表格線
如果設置了LVS_EX_CHECKBOXES屬性,則可以用 BOOL GetCheck( int nItem ) const; 來得到某一行是否Checked。
可以先用下面的語句來刪除以前的東西: for(int k=2;k>=0;k--) //注意要從后往前刪,否則出錯 m_ListCtrl.DeleteColumn(k); m_ListCtrl.DeleteAllItems();
用下面的語句新建列: m_ListCtrl.InsertColumn(0,_T("文件名"),LVCFMT_IMAGE|LVCFMT_LEFT); m_ListCtrl.InsertColumn(1,_T("儀器類別")); m_ListCtrl.InsertColumn(2,_T("項目類別")); 其中LVCFMT_IMAGE表示可以在第一列加入圖標。如果不要圖標可以刪去。
然后設置列寬: for(j=0;j<3;j++) m_ListCtrl.SetColumnWidth(j ,100); 以下為列表加入圖標,如果不需要圖標,可以跳過這一步。注意只在第一次加入,如果多次加入會出錯! 先在頭文件中加入聲明: CImageList m_ImageList; 這是必要的,如果在cpp的某個函數中加入由于生命期結束,CImageList自動釋放,則效果是列表中看不到圖標,只看到一個白方塊。 下面生成CImageList,并將其綁定到CListCtrl中,這是CImageList中還沒有圖標,只是一個容器: static int flag=2; if(flag==2){//只調用一次SetImageList,否則出錯 m_ImageList.Create(128, 128, ILC_COLORDDB|ILC_MASK, 20, 1); m_ListCtrl.SetImageList(&m_ImageList,LVSIL_SMALL); } flag=(flag+1)%2; 如果CListCtrl已經用過,曾經加過圖標進去,這時就要刪除上次放進m_ImageList中的Image for(int kk=0;kk<m_ImageList.GetImageCount();kk++) m_ImageList.Remove(k); 下面介紹如何向CListCtrl里面加入行,并同時為每一行動態加入圖標: 假設m_listRowCount為要加入的行數。 CBitmap* bitmap; bitmap=new CBitmap[m_list1rowCount]; HBITMAP hbitmap; for(int i = 0; i < m_listRowCount; i++) { //為每一行插入相應的縮略圖 CFile f; CFileException e; if( !f.Open(m_FileName, CFile::modeRead, &e )){ //m_FileName為bmp文件名,由你來定 hbitmap = (HBITMAP)LoadImage(NULL,path+"blank.bmp",IMAGE_BITMAP,0,0, LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE); }else{ f.Close(); hbitmap = (HBITMAP)LoadImage(NULL,bmpFile,IMAGE_BITMAP,0,0, LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE); } bitmap[i].Attach(hbitmap); m_ImageList.Add(&bitmap[i], RGB(0, 128, 128)); //插入行 m_ListCtrl.InsertItem(i,m_FileName,i); m_ListCtrl.SetItemText(i,1,type); m_ListCtrl.SetItemText(i,2,m_Path); } //記得刪除已經沒用的臨時文件 if(m_list1rowCount!=0) delete[] bitmap;
2。如果是ICON類型的CListCtrl,則要做一點點改動: 把綁定圖標集的代碼由 SetImageList(&m_ImageList,LVSIL_SMALL); 改為 SetImageList(&m_ImageList,LVSIL_NORMAL);
插入行時只用 InsertItem(i,mainSet.m_FileName,i); 不用 SetItemText(i,1,type); 之類的代碼。
|
設置報表的樣式
選中一整行:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_FULLROWSELECT);
繪制表格:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_GRIDLINES);
帶復選框:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_CHECKBOXES);
自動切換:
m_list_ctrl.SetExtendedStyle(m_list_ctrl.GetExtendedStyle()|LVS_EX_TRACKSELECT);
選定一行:
設置CListCtrl的Show selection always選項
SetItemState (iIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED)
選中一個或多個項目時,會發送LVN_ITEMCHANGED消息,可以使用
GetSelectedCount()方法得到被選定的項的數目。
點擊列頭的消息響應:
ON_NOTIFY(HDN_ITEMCLICKW, 0, ResponseFunc)
消息,需要自己添加
或者:
ON_NOTIFY(LVN_COLUMNCLICK, ID_yourCtrl, ResponseFunc)//向導添加
前者后響應,后者先響應
響應函數:
ResponseFunc(NMHDR *pNMHDR, LRESULT *pResult)
雙擊CListCtrl中的ITEM的消息是及消息函數:
ON_NOTIFY(NM_DBLCLK, ID_yourCtrl, ResponseFunc)
單擊ITEM的消息響應:
ON_NOTIFY(NM_CLICK, ID_yourCtrl, ResponseFunc)
ResponseFunc(NMHDR *pNMHDR, LRESULT *pResult)
HDN_ITEMCLICK 就是Header control Notify message for mouse left click on the Header control!
而HDN_ITEMCLICK是當List View中存在一個Header Contrl時,Header Ctrl通知父窗口List View的!
CListCtrl中的Item被選中觸發LBN_SELCHANGE(通過WM_COMMAND)消息!
刪除CListCtrl中選定的項:
POSITION pos;
int nIndex;
for(; pos= GetFirstSelectedItemPosition();)
{
nIndex = GetNextSelectedItem(pos);
DeleteItem(nIndex);
}
在ListCtrl中進行排序
列表控件(CListCtrl)的頂部有一排按鈕,用戶可以通過選擇不同的列來對記錄進行排序。但是 CListCtrl并沒有自動排序的功能,我們需要自己添加一個用于排序的回調函數來比較兩個數據的大小,此外還需要響應排序按鈕被點擊的消息。下面講述一下具體的做法。
CListCtrl提供了用于排序的函數,函數原型為:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD dwData )。其中第一個參數為全局排序函數的地址,第二個參數為用戶數據,你可以根據你的需要傳遞一個數據或是指針。該函數返回-1代表第一項排應在第二項前面,返回1代表第一項排應在第二項后面,返回0代表兩項相等。
用于排序的函數原形為:int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort),其中第三個參數為調用者傳遞的數據(即調用SortItems時的第二個參數dwData)。第一和第二個參數為用于比較的兩項的ItemData,你可以通過DWORD CListCtrl::GetItemData( int nItem )/BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )來對每一項的ItemData進行存取。在添加項時選用特定的CListCtrl::InsertItem也可以設置該值。由于你在排序時只能通過該值來確定項的位置所以你應該比較明確的確定該值的含義。
最后一點,我們需要知道什么時候需要排序,實現這點可以在父窗口中對LVN_COLUMNCLICK消息進行處理來實現。
下面我們看一個例子,這個例子是一個派生類,并支持順序/倒序兩種方式排序。為了簡單我對全局數據進行排序,而在實際應用中會有多組需要排序的數據,所以需要通過傳遞參數的方式來告訴派序函數需要對什么數據進行排序。
//全局數據
struct DEMO_DATA
{
char szName[20];
int iAge;
}strAllData[5]={{"王某",30},{"張某",40},{"武某",32},{"陳某",20},{"李某",36}};
//CListCtrl派生類定義
class CSortList : public CListCtrl
{
// Construction
public:
CSortList();
BOOL m_fAsc;//是否順序排序
int m_nSortedCol;//當前排序的列
protected:
//{{AFX_MSG(CSortList)
//}}AFX_MSG
...
};
//父窗口中包含該CListCtrl派生類對象
class CSort_in_list_ctrlDlg : public CDialog
{
// Construction
public:
CSort_in_list_ctrlDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CSort_in_list_ctrlDlg)
enum { IDD = IDD_SORT_IN_LIST_CTRL_DIALOG };
CSortList m_listTest;
//}}AFX_DATA
}
//在父窗口中定義LVN_COLUMNCLICK消息映射
BEGIN_MESSAGE_MAP(CSort_in_list_ctrlDlg, CDialog)
//{{AFX_MSG_MAP(CSort_in_list_ctrlDlg)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//初始化數據
BOOL CSort_in_list_ctrlDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//初始化ListCtrl中數據列表
m_listTest.InsertColumn(0,"姓名");
m_listTest.InsertColumn(1,"年齡");
m_listTest.SetColumnWidth(0,80);
m_listTest.SetColumnWidth(1,80);
for(int i=0;i<5;i++)
{
m_listTest.InsertItem(i,strAllData[i].szName);
char szAge[10];
sprintf(szAge,"%d",strAllData[i].iAge);
m_listTest.SetItemText(i,1,szAge);
//設置每項的ItemData為數組中數據的索引
//在排序函數中通過該ItemData來確定數據
m_listTest.SetItemData(i,i);
}
return TRUE; // return TRUE unless you set the focus to a control
}
//處理消息
void CSort_in_list_ctrlDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//設置排序方式
if( pNMListView->iSubItem == m_listTest.m_nSortedCol )
m_listTest.m_fAsc = !m_listTest.m_fAsc;
else
{
m_listTest.m_fAsc = TRUE;
m_listTest.m_nSortedCol = pNMListView->iSubItem;
}
//調用排序函數
m_listTest.SortItems( ListCompare, (DWORD)&m_listTest );
*pResult = 0;
}
//排序函數實現
int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
//通過傳遞的參數來得到CSortList對象指針,從而得到排序方式
CSortList* pV=(CSortList*)lParamSort;
//通過ItemData來確定數據
DEMO_DATA* pInfo1=strAllData+lParam1;
DEMO_DATA* pInfo2=strAllData+lParam2;
CString szComp1,szComp2;
int iCompRes;
switch(pV->m_nSortedCol)
{
case(0):
//以第一列為根據排序
szComp1=pInfo1->szName;
szComp2=pInfo2->szName;
iCompRes=szComp1.Compare(szComp2);
break;
case(1):
//以第二列為根據排序
if(pInfo1->iAge == pInfo2->iAge)
iCompRes = 0;
else
iCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;
break;
default:
ASSERT(0);
break;
}
//根據當前的排序方式進行調整
if(pV->m_fAsc)
return iCompRes;
else
return iCompRes*-1;
}
排序最快:
CListCtrl::SortItems
Example
// Sort the item in reverse alphabetical order.
static int CALLBACK
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
// lParamSort contains a pointer to the list view control.
// The lParam of an item is just its index.
CListCtrl* pListCtrl = (CListCtrl*) lParamSort;
CString strItem1 = pListCtrl->GetItemText(lParam1, 0);
CString strItem2 = pListCtrl->GetItemText(lParam2, 0);
return strcmp(strItem2, strItem1);
}
void snip_CListCtrl_SortItems()
{
// The pointer to my list view control.
extern CListCtrl* pmyListCtrl;
// Sort the list view items using my callback procedure.
pmyListCtrl->SortItems(MyCompareProc, (LPARAM) pmyListCtrl);
}
If you don’t want to allow the users to sort the list by clicking on the header, you can use the style LVS_NOSORTHEADER. However, if you do want to allow sorting, you do not specify the LVS_NOSORTHEADER. The control, though, does not sort the items. You have to handle the HDN_ITEMCLICK notification from the header control and process it appropriately. In the code below, we have used the sorting function SortTextItems() developed in a previous section. You may choose to sort the items in a different manner.
Step 1: Add two member variables
Add two member variables to the CListCtrl. The first variable to track which column has been sorted on, if any. The second variable to track if the sort is ascending or descending.
int nSortedCol;
BOOL bSortAscending;
Step 2: Initialize them in the constructor.
Initialize nSortedCol to -1 to indicate that no column has been sorted on. If the list is initially sorted, then this variable should reflect that.
nSortedCol = -1;
bSortAscending = TRUE;
Step 3: Add entry in message map to handle HDN_ITEMCLICK
Actually you need to add two entries. For HDN_ITEMCLICKA and HDN_ITEMCLICKW. Do not use the class wizard to add the entry. For one, you need to add two entries whereas the class wizard will allow you only one. Secondly, the class wizard uses the wrong macro in the entry. It uses ON_NOTIFY_REFLECT() instead of ON_NOTIFY(). Since the HDN_ITEMCLICK is a notification from the header control to the list view control, it is a direct notification and not a reflected one.
ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)
ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
Note that we specify the same function for both the notification. Actually the program will receive one or the other and not both. What notification it receives will depend on the OS. The list view control on Windows 95 will send the ANSI version and the control on NT will send the UNICODE version.
Also, note that the second argument is zero. This value filters for the id of the control and we know that header control id is zero.
Step 4: Write the OnHeaderClicked() function
Here’s where you decide what to do when the user clicks on a column header. The expected behaviour is to sort the list based on the values of the items in that column. In this function we have used the SortTextItems() function developed in a previous section. If any of the columns displays numeric or date values, then you would have to provide custom sorting for them.
void CMyListCtrl::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
if( phdn->iButton == 0 )
{
// User clicked on header using left mouse button
if( phdn->iItem == nSortedCol )
bSortAscending = !bSortAscending;
else
bSortAscending = TRUE;
nSortedCol = phdn->iItem;
SortTextItems( nSortedCol, bSortAscending );
}
*pResult = 0;
}
讓CListCtrl的SubItem也具有編輯功能:
要重載一個文本框,然后在LVN_BEGINLABELEDIT時改變文本框位置。
CInEdit m_InEdit;
if( ( GetStyle() & LVS_TYPEMASK ) == LVS_REPORT && ( m_nEditSubItem != 0 ) )
{
HWND hwndEdit;
CRect rtBound;
CString strText;
hwndEdit = (HWND)SendMessage( LVM_GETEDITCONTROL );
GetSubItemRect( pDispInfo->item.iItem, m_nEditSubItem, LVIR_LABEL, rtBound );
m_InEdit.SubclassWindow( hwndEdit );
m_InEdit.m_left = rtBound.left;
strText = GetItemText( pDispInfo->item.iItem, m_nEditSubItem );
m_InEdit.SetWindowText( strText );
}
void CInEdit::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
CRect rtClient;
lpwndpos->x = m_left; // m_left在LVN_BEGINLABELEDIT中設置
CEdit::OnWindowPosChanging(lpwndpos);
// TODO: Add your message handler code here
}
posted @
2008-06-14 02:48 幽幽 閱讀(6547) |
評論 (0) |
編輯 收藏
預處理器(Preprocessor)
1. 用預處理指令#define 聲明一個常數,用以表明1年中有多少秒(忽略閏年問題)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在這想看到幾件事情:
1). #define 語法的基本知識(例如:不能以分號結束,括號的使用,等等)
2). 懂得預處理器將為你計算常數表達式的值,因此,直接寫出你是如何計算一年中有多少秒而不是計算出實際的值,是更清晰而沒有代價的。
3). 意識到這個表達式將使一個16位機的整型數溢出-因此要用到長整型符號L,告訴編譯器這個常數是的長整型數。
4). 如果你在你的表達式中用到UL(表示無符號長整型),那么你有了一個好的起點。記住,第一印象很重要。
2. 寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個。
#define MIN(A,B) ((A) <= (B) (A) : (B))
這個測試是為下面的目的而設的:
1). 標識#define在宏中應用的基本知識。這是很重要的,因為直到嵌入(inline)操作符變為標準C的一部分,宏是方便產生嵌入代碼的唯一方法,對于嵌入式系統來說,為了能達到要求的性能,嵌入代碼經常是必須的方法。
2). 三重條件操作符的知識。這個操作符存在C語言中的原因是它使得編譯器能產生比if-then-else更優化的代碼,了解這個用法是很重要的。
3). 懂得在宏中小心地把參數用括號括起來
4). 我也用這個問題開始討論宏的副作用,例如:當你寫下面的代碼時會發生什么事?
least = MIN(*p++, b);
3. 預處理器標識#error的目的是什么?
如果你不知道答案,請看參考文獻1。這問題對區分一個正常的伙計和一個書呆子是很有用的。只有書呆子才會讀C語言課本的附錄去找出象這種
問題的答案。當然如果你不是在找一個書呆子,那么應試者最好希望自己不要知道答案。
死循環(Infinite loops)4. 嵌入式系統中經常要用到無限循環,你怎么樣用C編寫死循環呢?
這個問題用幾個解決方案。我首選的方案是:
while(1) { }
一些程序員更喜歡如下方案:
for(;;) { }
這個實現方式讓我為難,因為這個語法沒有確切表達到底怎么回事。如果一個應試者給出這個作為方案,我將用這個作為一個機會去探究他們這樣做的
基本原理。如果他們的基本答案是:“我被教著這樣做,但從沒有想到過為什么。”這會給我留下一個壞印象。
第三個方案是用 goto
Loop:
...
goto Loop;
應試者如給出上面的方案,這說明或者他是一個匯編語言程序員(這也許是好事)或者他是一個想進入新領域的BASIC/FORTRAN程序員。
數據聲明(Data declarations) 5. 用變量a給出下面的定義
a) 一個整型數(An integer)
b) 一個指向整型數的指針(A pointer to an integer)
c) 一個指向指針的的指針,它指向的指針是指向一個整型數(A pointer to a pointer to an integer)
d) 一個有10個整型數的數組(An array of 10 integers)
e) 一個有10個指針的數組,該指針是指向一個整型數的(An array of 10 pointers to integers)
f) 一個指向有10個整型數數組的指針(A pointer to an array of 10 integers)
g) 一個指向函數的指針,該函數有一個整型參數并返回一個整型數(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數并返回一個整型數( An array of ten pointers to functions that take an integer argument and return an integer )
答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
人們經常聲稱這里有幾個問題是那種要翻一下書才能回答的問題,我同意這種說法。當我寫這篇文章時,為了確定語法的正確性,我的確查了一下書。
但是當我被面試的時候,我期望被問到這個問題(或者相近的問題)。因為在被面試的這段時間里,我確定我知道這個問題的答案。應試者如果不知道
所有的答案(或至少大部分答案),那么也就沒有為這次面試做準備,如果該面試者沒有為這次面試做準備,那么他又能為什么出準備呢?
Static6. 關鍵字static的作用是什么?
這個簡單的問題很少有人能回答完全。在C語言中,關鍵字static有三個明顯的作用:
1). 在函數體,一個被聲明為靜態的變量在這一函數被調用過程中維持其值不變。
2). 在模塊內(但在函數體外),一個被聲明為靜態的變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
3). 在模塊內,一個被聲明為靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地范圍內使用。
大多數應試者能正確回答第一部分,一部分能正確回答第二部分,同是很少的人能懂得第三部分。這是一個應試者的嚴重的缺點,因為他顯然不懂得本地化數據和代碼范圍的好處和重要性。
Const 7.關鍵字const是什么含意?
我只要一聽到被面試者說:“const意味著常數”,我就知道我正在和一個業余者打交道。去年Dan Saks已經在他的文章里完全概括了const的所有用法,因此ESP(譯者:Embedded Systems Programming)的每一位讀者應該非常熟悉const能做什么和不能做什么.如果你從沒有讀到那篇文章,只要能說出const意味著“只讀”就可以了。盡管這個答案不是完全的答案,但我接受它作為一個正確的答案。(如果你想知道更詳細的答案,仔細讀一下Saks的文章吧。)如果應試者能正確回答這個問題,我將問他一個附加的問題:下面的聲明都是什么意思?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;
前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指針(也就是,整型數是不可修改的,但指針可以)。第四個意思a是一個指向整型數的常指針(也就是說,指針指向的整型數是可以修改的,但指針是不可修改的)。最后一個意味著a是一個指向常整型數的常指針(也就是說,指針指向的整型數是不可修改的,同時指針也是不可修改的)。如果應試者能正確回答這些問題,那么他就給我留下了一個好印象。順帶提一句,也許你可能會問,即使不用關鍵字const,也還是能很容易寫出功能正確的程序,那么我為什么還要如此看重關鍵字const呢?我也如下的幾下理由:
1). 關鍵字const的作用是為給讀你代碼的人傳達非常有用的信息,實際上,聲明一個參數為常量是為了告訴了用戶這個參數的應用目的。如果你曾花很多時間清理其它人留下的垃圾,你就會很快學會感謝這點多余的信息。(當然,懂得用const的程序員很少會留下的垃圾讓別人來清理的。)
2). 通過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。
3). 合理地使用關鍵字const可以使編譯器很自然地保護那些不希望被改變的參數,防止其被無意的代碼修改。簡而言之,這樣可以減少bug的出現。
Volatile 8. 關鍵字volatile有什么含意 并給出三個不同的例子。
一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精確地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用保存在寄存器里的備份。下面是volatile變量的幾個例子:
1). 并行設備的硬件寄存器(如:狀態寄存器)
2). 一個中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
3). 多線程應用中被幾個任務共享的變量
回答不出這個問題的人是不會被雇傭的。我認為這是區分C程序員和嵌入式系統程序員的最基本的問題。嵌入式系統程序員經常同硬件、中斷、RTOS等等打交道,所用這些都要求volatile變量。不懂得volatile內容將會帶來災難。
假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這家伙是不是直正懂得volatile完全的重要性。
1). 一個參數既可以是const還可以是volatile嗎?解釋為什么。
2). 一個指針可以是volatile 嗎?解釋為什么。
3). 下面的函數有什么錯誤:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一個例子是只讀的狀態寄存器。它是volatile因為它可能被意想不到地改變。它是const因為程序不應該試圖去修改它。
2). 是的。盡管這并不很常見。一個例子是當一個中服務子程序修該一個指向一個buffer的指針時。
3). 這段代碼的有個惡作劇。這段代碼的目的是用來返指針*ptr指向值的平方,但是,由于*ptr指向一個volatile型參數,編譯器將產生類似下面的代碼:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段代碼可能返不是你所期望的平方值!正確的代碼如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)9. 嵌入式系統總是要用戶對變量或寄存器進行位操作。給定一個整型變量a,寫兩段代碼,第一個設置a的bit 3,第二個清除a 的bit 3。在以上兩個操作中,要保持其它位不變。
對這個問題有三種基本的反應
1). 不知道如何下手。該被面者從沒做過任何嵌入式系統的工作。
2). 用bit fields。Bit fields是被扔到C語言死角的東西,它保證你的代碼在不同編譯器之間是不可移植的,同時也保證了的你的代碼是不可重用的。我最近不幸看到Infineon為其較復雜的通信芯片寫的驅動程序,它用到了bit fields因此完全對我無用,因為我的編譯器用其它的方式來實現bit fields的。從道德講:永遠不要讓一個非嵌入式的家伙粘實際硬件的邊。
3). 用 #defines 和 bit masks 操作。這是一個有極高可移植性的方法,是應該被用到的方法。最佳的解決方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
一些人喜歡為設置和清除值而定義一個掩碼同時定義一些說明常數,這也是可以接受的。我希望看到幾個要點:說明常數、|=和&=~操作。
訪問固定的內存位置(Accessing fixed memory locations) 10. 嵌入式系統經常具有要求程序員去訪問某特定的內存位置的特點。在某工程中,要求設置一絕對地址為0x67a9的整型變量的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫代碼去完成這一任務。
這一問題測試你是否知道為了訪問一絕對地址把一個整型數強制轉換(typecast)為一指針是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似代碼如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
一個較晦澀的方法是:
*(int * const)(0x67a9) = 0xaa55;
即使你的品味更接近第二種方案,但我建議你在面試時使用第一種方案。
中斷(Interrupts) 11. 中斷是嵌入式系統中重要的組成部分,這導致了很多編譯開發商提供一種擴展—讓標準C支持中斷。具代表事實是,產生了一個新的關鍵字__interrupt。下面的代碼就使用了__interrupt關鍵字去定義了一個中斷服務子程序(ISR),請評論一下這段代碼的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
這個函數有太多的錯誤了,以至讓人不知從何說起了:
1). ISR 不能返回一個值。如果你不懂這個,那么你不會被雇用的。
2). ISR 不能傳遞參數。如果你沒有看到這一點,你被雇用的機會等同第一項。
3). 在許多的處理器/編譯器中,浮點一般都是不可重入的。有些處理器/編譯器需要讓額處的寄存器入棧,有些處理器/編譯器就是不允許在ISR中做浮點運算。此外,ISR應該是短而有效率的,在ISR中做浮點運算是不明智的。
4). 與第三點一脈相承,printf()經常有重入和性能上的問題。如果你丟掉了第三和第四點,我不會太為難你的。不用說,如果你能得到后兩點,那么你的被雇用前景越來越光明了。
代碼例子(Code examples)12 . 下面的代碼輸出是什么,為什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) puts("> 6") : puts("<= 6");
}
這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表達式中存在有符號類型和無符號類型時所有的操作數都自動轉換為無符號類型。 因此-20變成了一個非常大的正整數,所以該表達式計算出的結果大于6。這一點對于應當頻繁用到無符號數據類型的嵌入式系統來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。
13. 評價下面的代碼片斷:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */
對于一個int型不是16位的處理器為說,上面的代碼是不正確的。應編寫如下:
unsigned int compzero = ~0;
這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在我的經驗里,好的嵌入式程序員非常準確地明白硬件的細節和它的局限,然而PC機程序往往把硬件作為一個無法避免的煩惱。
到了這個階段,應試者或者完全垂頭喪氣了或者信心滿滿志在必得。如果顯然應試者不是很好,那么這個測試就在這里結束了。但如果顯然應試者做得不錯,那么我就扔出下面的追加問題,這些問題是比較難的,我想僅僅非常優秀的應試者能做得不錯。提出這些問題,我希望更多看到應試者應付問題的方法,而不是答案。不管如何,你就當是這個娛樂吧…
動態內存分配(Dynamic memory allocation)14. 盡管不像非嵌入式計算機那么常見,嵌入式系統還是有從堆(heap)中動態分配內存的過程的。那么嵌入式系統中,動態分配內存可能發生的問題是什么?
這里,我期望應試者能提到內存碎片,碎片收集的問題,變量的持行時間等等。這個主題已經在ESP雜志中被廣泛地討論過了(主要是 P.J. Plauger, 他的解釋遠遠超過我這里能提到的任何解釋),所有回過頭看一下這些雜志吧!讓應試者進入一種虛假的安全感覺后,我拿出這么一個小節目:下面的代碼片段的輸出是什么,為什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
這是一個有趣的問題。最近在我的一個同事不經意把0值傳給了函數malloc,得到了一個合法的指針之后,我才想到這個問題。這就是上面的代碼,該代碼的輸出是“Got a valid pointer”。我用這個來開始討論這樣的一問題,看看被面試者是否想到庫例程這樣做是正確。得到正確的答案固然重要,但解決問題的方法和你做決定的基本原理更重要些。
Typedef
15. Typedef 在C語言中頻繁用以聲明一個已經存在的數據類型的同義字。也可以用預處理器做類似的事。例如,思考一下下面的例子:
#define dPS struct s *
typedef struct s * tPS;
以上兩種情況的意圖都是要定義dPS 和 tPS 作為一個指向結構s指針。哪種方法更好呢?(如果有的話)為什么?
這是一個非常微妙的問題,任何人答對這個問題(正當的原因)是應當被恭喜的。答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一個擴展為
struct s * p1, p2;
上面的代碼定義p1為一個指向結構的指,p2為一個實際的結構,這也許不是你想要的。第二個例子正確地定義了p3 和p4 兩個指針。
16. C語言同意一些令人震驚的結構,下面的結構是合法的嗎,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
這個問題將做為這個測驗的一個愉快的結尾。不管你相不相信,上面的例子是完全合乎語法的。問題是編譯器如何處理它?水平不高的編譯作者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理盡可能所有合法的用法。因此,上面的代碼被處理成:
c = a++ + b;
因此, 這段代碼持行后a = 6, b = 7, c = 12。
posted @
2008-06-11 02:46 幽幽 閱讀(530) |
評論 (0) |
編輯 收藏
轉自: http://hi.baidu.com/jenfmo/blog/item/b000c50a7acb8e3ab1351dfd.html
前言
ActiveX控件是一種實現了一系列特定接口而使其在使用和外觀上更象一個控件的COM組件。ActiveX控件這種技術涉及到了幾乎所有的COM和OLE的技術精華,如可鏈接對象、統一數據傳輸、OLE文檔、屬性頁、永久存儲以及OLE自動化等。
ActiveX控件作為基本的界面單元,必須擁有自己的屬性和方法以適合不同特點的程序和向包容器程序提供功能服務,其屬性和方法均由自動化服務的IDispatch接口來支持。除了屬性和方法外,ActiveX控件還具有區別于自動化服務的一種特性--事件。事件指的是從控件發送給其包容程序的一種通知。與窗口控件通過發送消息通知其擁有者類似,ActiveX控件是通過觸發事件來通知其包容器的。事件的觸發通常是通過控件包容器提供的IDispatch接口來調用自動化對象的方法來實現的。在設計ActiveX控件時就應當考慮控件可能會發生哪些事件以及包容器程序將會對其中的哪些事件感興趣并將這些事件包含進來。與自動化服務不同,ActiveX控件的方法、屬性和事件均有自定義(custom)和庫存(stock)兩種不同的類型。自定義的方法和屬性也就是是普通的自動化方法和屬性,自定義事件則是自己選取名字和Dispatch ID的事件。而所謂的庫存方法、屬性和事件則是使用了ActiveX控件規定了名字和Dispatch ID的"標準"方法、屬性和事件。
ActiveX控件可以使COM組件從外觀和使用上能與普通的窗口控件一樣,而且還提供了類似于設置Windows標準控件屬性的屬性頁,使其能夠在包容器程序的設計階段對ActiveX控件的屬性進行可視化設置。ActiveX控件提供的這些功能使得對其的使用將是非常方便的。本文下面即以MFC為工具對ActiveX控件的開發進行介紹。
posted @
2008-06-11 00:58 幽幽 閱讀(605) |
評論 (0) |
編輯 收藏
Description: 前言 本文全面的介紹了RSA算法的概念、原理、證明和實現。我在寫作本文之前在網上查閱過相關資料,可這些資料不是含糊其辭就是滿篇謬誤。所以我力求用通俗易懂的文字將算法深入剖析,用最嚴謹的步驟進行論相關的各項算法,以降低文章的閱讀難度。讀者只要學過初中代數就可以理解全文,我衷心希望更多讀者能認識到加密算法其實并不難。文中的算法均為偽代碼,由于偽代碼沒有辦法進行測試,再加上我個人數學功底比較薄弱,所以錯漏之處在所難免,還請各位老師給予指教。質疑或指正請發送電子郵件到fireseed1949@hotmail.com,我會認真閱讀并回復的!感謝北航數學系(畢業)李楨老師、西工大計算機系(畢業)張小寧老師在數學上對我的指點。另注:文中mod就是求余的符號,X mod Y表示X除以Y所得的余數。 ·概述 RSA算法是世界上第一個既能用于數據加密也能用于數字簽名的非對稱性加密算法。它易于理解和操作,所以流行甚廣。算法的名字以發明者的名字命名,他們是:Ron Rivest,Adi Shamir 和Leonard Adleman。雖然RSA的安全性一直未能得到理論上的證實,但它經歷了各種攻擊,至今未被完全攻破。為了讓讀者更容易的理解RSA加密,先大概講述一下信息加密技術的相關概念和原理。我們對于在數字媒體上進行交換的數據進行加密的方法稱為信息交換加密技術,它分為兩類,即對稱加密和非對稱加密。在對稱加密技術中,對信息的加密和解密都使用相同的鑰,也就是說一把鑰匙開一把鎖。這種加密方法可簡化加密處理過程,信息交換雙方都不必彼此研究和交換專用的加密算法。如果在交換階段私有密鑰未曾泄露,那么機密性和報文完整性就可以得以保證。對稱加密技術也存在一些不足,如果交換一方有N個交換對象,那么他就要維護N個私有密鑰,對稱加密存在的另一個問題是雙方共享一把私有密鑰,交換雙方的任何信息都是通過這把密鑰加密后傳送給對方的。如三重DES是DES(數據加密標準)的一種變形,這種方法使用兩個獨立的56為密鑰對信息進行3次加密,從而使有效密鑰長度達到112位。在非對稱加密(或稱公開密鑰加密)體系中,密鑰被分解為一對,即公開密鑰(公鑰)和私有密鑰(私鑰)。這對密鑰中任何一把都可以作為公開密鑰,通過非保密方式向他人公開,而另一把作為私有密鑰,加以妥善保存。公開密鑰用于加密,私有密鑰用于解密,私有密鑰只能由生成密鑰的交換方掌握,公開密鑰可廣泛公布,但它只對應于生成密鑰的交換方。非對稱加密方式可以使通信雙方無須事先交換密鑰就可以建立安全通信,廣泛應用于身份認證、數字簽名等信息交換領域。非對稱加密體系一般是建立在某些已知的數學難題之上,是計算機復雜性理論發展的必然結果。最具有代表性是RSA公鑰密碼體制。在RSA算法中,我們先要獲得兩個不同的質數P和Q做為算法因子,再找出一個正整數E,使得E與 ( P - 1 ) * ( Q - 1 ) 的值互質,這個E就是私鑰。找到一個整數D,使得( E * D ) mod ( ( P - 1 ) * ( Q - 1 ) ) = 1成立,D就是公鑰1。設N為P和Q的乘積,N則為公鑰2。加密時先將明文轉換為一個或一組小于N的整數I,并計算ID mod N的值M,M就密文。解密時將密文ME mod N,也就是M的E次方再除以N所得的余數就是明文。因為私鑰E與( P - 1 ) * ( Q - 1 )互質,而公鑰D使( E * D ) mod ( ( P - 1 ) * ( Q - 1 ) ) = 1成立。破解者可以得到D和N,如果想要得到E,必須得出( P - 1 ) * ( Q - 1 ),因而必須先對N進行因數分解。如果N很大那么因數分解就會非常困難,所以要提高加密強度P和Q的數值大小起著決定性的因素。一般來講當P和Q都大于2128時,按照目前的機算機處理速度破解基本已經不大可能了。 ·證明下面將會開始討論RSA算法的原理及其算法證明。如果您只關心RSA算法的實現,則可以略過這一步。我把每一個有用的定理都用粗標標記了,對于數學不很在行的朋友可以只了解一下相關定理的說明而不需要驗證求證過程了。一、 費馬小定理[1]的轉化費馬小定理:有N為任意正整數,P為素數,且N不能被P整除,則有: NP mod P = N 費馬小定理可變形為: NP - N mod P = 0 ( N ( NP - 1 - 1 ) ) mod P = 0 因為 ( N ( NP - 1 - 1 ) ) mod N = 0 所以N和P的公倍數為: N ( NP - 1 - 1 ) (1)又因為N與P互質,而互質數的最小公倍數為它們的乘積,所以一定存在正整數M使得:N ( NP - 1 - 1 ) = MNP成立。并化簡為: NP - 1 - 1 = MP ( NP - 1 - 1 ) mod P = 0 可以變形為: NP - 1 mod P = 1 (2)(2)就是費馬小定理的轉化定理,為方便敘述,下文簡稱為定理一。小提示,可能很多人認為費馬小定理本來就是(2),實際上不是這樣,因為費馬小定理的轉化非常容易,而轉化定理又是一個無論在數學上還是計算機程序上都很常用的公式,所以人們就普遍認為(2)就是費馬小定理了。二、 積模分解公式有X、Y和Z三個正整數,且X * Y大于Z,則有: ( X * Y ) mod Z = ( ( X mod Z ) * ( Y mod Z ) ) mod Z 證明如下當X和Y都比Z大時,可以將X和Y表示為: X = ZI + A (1) Y = ZJ + B (2)將(1)和(2)代入( X * Y ) mod Z得: ( ( ZI + A )( ZJ + B ) ) mod Z ( Z( ZIJ + IA + IB ) + AB ) mod Z (3)因為Z( ZIJ + IA + IB )是Z的整數倍,所以(3)式可化簡為: AB mod Z 因為A和B實際上是X和Y分別除以Z的余數,所以有: ( X * Y ) mod Z = ( ( X mod Z ) * ( Y mod Z ) ) mod Z成立。當X比Z大而Y比Z小時 X = ZI + A 代入( X * Y ) mod Z得: ( ZIY + AY ) mod Z AY mod Z 因為A = X mod Z, 又因為Y mod Z = Y,所以有: ( X * Y ) mod Z = ( ( X mod Z ) * ( Y mod Z ) ) mod Z成立。同理,當X比Z小而Y比Z大時,上式也成立。當X和Y都比Z小時,X = X mod Z,Y = Y mod Z所以有: ( X * Y ) mod Z = ( ( X mod Z ) * ( Y mod Z ) ) mod Z成立。積模分解公式成立。三、 定理二有P和Q兩個互質數,如果有X mod P = 0,X mod Q = 0,則有:X mod PQ = 0 證明:因為P和Q互質,所以它們的公倍數為KPQ(K為整數),最小公倍數為PQ。又因為X為P和Q的公倍數,所以X / PQ = K,所以X mod PQ = 0。四、 定理三有P和Q兩個互質數,設有整數X和Y滿足Y mod P = X,Y mod Q = X,則有:Y mod PQ = X 證明: X = Y mod P 可以表示為: Y = X + kP Y - X = kP 即Y - X可以被P整除,同理Y - X可以被Q整除。又因為P、Q互質,根據定理二可得: ( Y - X ) mod PQ = 0 即 Y mod PQ = X 五、 定理三的逆定理有P和Q兩個互質數,設有整數X和Y滿足Y mod PQ = X ,則有:Y mod P = X,Y mod Q = X 證明: Y mod PQ = X 可以表示為: ( Y – X ) mod PQ = 0 顯然 ( Y – X ) mod P = 0且 ( Y – X ) mod Q = 0 所以原命題成立。六、 RSA定理若P和Q是兩個相異質數,另有正整數R和M,其中M的值與( P - 1 )( Q - 1 )的值互質,并使得( RM ) mod ( P - 1 )( Q - 1 ) = 1。有正整數A,且A
PQ,且A不是P的倍數也不是Q的倍數時,(2)可變形為: B = ( AAK ( P - 1 )( Q - 1 ) ) mod PQ 根據積模分解公式可變形為: B = ( ( A mod PQ )( AK ( P - 1 )( Q - 1 ) mod PQ ) ) mod PQ (3)根據定理三的逆定理: AK ( P - 1 )( Q - 1 ) mod PQ = ( AK ( P - 1 ) ) ( Q - 1 ) mod Q 根據費馬小定理可得: ( AK ( P - 1 ) ) ( Q - 1 ) mod Q = 1,則 AK ( P - 1 )( Q - 1 ) mod PQ = 1 故( 3 )可轉化為: B = ( A mod PQ ) mod PQ 因為A
PQ,且A不是P的倍數而是Q的倍數時,A可表示為A = NQ,N為一小于A的整數。那么(2)式可變形為: B = ( NQ )K ( P - 1 )( Q - 1 ) + 1 mod PQ B = ( NK ( P - 1 )( Q - 1 ) + 1 )( QK ( P - 1 )( Q - 1 ) + 1 ) mod PQ 把Q作為公因子提出來,得: B = ( ( NNK ( P - 1 )( Q - 1 ) ) ( QK ( P - 1 )( Q - 1 ) mod P ) ) Q 用積模分解公式進行分解,得: B = ( ( NNK ( P - 1 )( Q - 1 ) mod P )( QK ( P - 1 )( Q - 1 ) mod P ) mod P ) Q 跟據定理四,NK ( P - 1 )( Q - 1 )和QK ( P - 1 )( Q - 1 )的值都為1,所以有: B = ( ( ( N mod P ) mod P ) mod P ) Q B = NQ mod PQ mod PQ mod PQ B = A mod PQ mod PQ mod PQ 因為A
1 E := E / 2,余數存入M IF M = 1 K := R * K END IF R := R * R NEXT R := R * K 再回到我們剛才討論的冪模運算。事實上在(1)式中,我們需要求出的就是( N mod D )R的值,那么只要令上面偽代碼中參量N的值為N mod D,并對結果R求R mod D就可以了,下面是基于上面求乘方算法的冪模運算的偽代碼。算法二:計算N的E次方再取D的模,令R為計算結果。 R := N mod D R := R ^ E ;調用算法一 R := R % D 如果再利用上文過程中提到積模分解公式對算法做進一步優化,直接把取余的運算代入到乘方中,就成為了著名的蒙格馬利快速冪模運算法,偽代碼如下。算法三:蒙格馬利法計算N的E次方再取D的模,令R為計算結果。 R := 1 A := N B := E WHILE B != 0 IF B & 1 ;判斷是否為奇數 B := B - 1 R := R * A X := X % D ELSE B := B / 2 A := A * A A := A % D END IF NEXT 蒙格馬利快速冪模運算,是目前世界上效率最高的冪模運算,很多硬件芯片在處理類似算法時都采用的這種方法。 ·尋找大素數為了有效防止破解,必要須找到兩個很大的素數作為算法因子。而尋找大素數,是數學家們一個永恒的話題。素數的定義是只能被自己和1整除的自然數,按照常規的理解,使用計算機對一個很大的數進行素數測試時,需要遍歷所有小于它且大于1的自然數,并逐個判斷是否能被該數整除。這個過程對于非常大的素數而言是非常緩慢的。但是根據費馬小定理,我們可以設計一種算法來快速測試素數。當A和Q互質時,有:AQ - 1 mod Q = 1,那么,我們可以通過判斷AQ - 1 mod Q的值是否等于1對Q進行素數測試。如果取了很多個A,Q仍未測試失敗,那么則認為Q是素數。當然,測試次數越多越準確,但一般來講50次就足夠了。另外,預先用常歸算法構造一個包括500個素數的數組,先對Q進行整除測試,將會大大提高通過率,方法如下:算法四:費馬定理測試可能素數P C := 500 ;素數表大小 S[ 0 TO C ] ;素數表 B := P - 1 T := 50 ;表示進行測試的次數 A := 0 FOR I := 0 TO C ;進行素數表初步測試 IF P mod S[I] = 0 RETURN FAILE END IF IF P 1 RETURN FAILE END IF NEXT I RETURN PASS 這個算法看起來很完美,但實際上從一開始它就犯了一個很大的錯,那就是對于任意與Q互質的A都有AQ - 1 mod Q = 1,這是素數的性質,是素數成立的一個必要條件,但不是充分條件!讓我們來看一下29341這個數,它等于13 * 37 * 61,但任何與它互質的A都有A29341 - 1 mod 29341 = 1成立。這種數字還有不少,數學上把它們稱為卡爾麥克數,現在數學家們已經找到所有1016以內的卡爾麥克數,最大的一個是9585921133193329。我們必須尋找更為有效的測試方法。數學家們通過對費馬小定理的研究,并加以擴展,總結出了多種快速有效的素數測試方法,目前最快的算法是拉賓米勒測試算法,其過程如下:首先確定N是否為奇數,不是奇數的判斷失敗。選擇T個隨機整數A,并且有 0 50那么測試失誤的機率就會小于10-30,這對于目前的計算機硬件來說已經足夠證明N就是素數了。下面是偽代碼。算法五:拉賓米勒測試法測試P是否為素數。 C := 500 ;素數表大小 S[ 0 TO C ] ;素數表 B := P - 1 T := 50 ;表示進行測試的次數 A := 0 ;用來測試通過的隨機整數 FOR I := 0 TO C ;進行素數表初步測試 IF P mod S[I] = 0 RETURN FAILE END IF IF P > 1 R := R + 1 ;計算R NEXT X := 0 Y := 0 FOR I := 0 TO T A := S[ RAND() mod C ] ;先進行費馬測試 IF A ^ ( P - 1 ) mod P <> 1 RETURN FAILE END IF X := A Y := A ^ ( M * R * 2 ) WHILE X <= Y IF X ^ M mod P = 1 BREAK END IF X := X ^ 2 NEXT IF X > Y RETURN FAILE END IF NEXT RETURN PASS ·二元一次不定方程在算法概述的章節里我們曾經討論過公鑰1的求法:找一個數D,使得( E * D ) mod ( ( P - 1 ) * ( Q - 1 ) ) = 1成立。為了求D,我們先對這個方程變形。實際上這個方程可以看做AX mod B = 1,即: AX = BY + 1,Y為一整數。 AX - BY = 1 這就是一個二元一次不定方程,有已知數A、B,未知數X、Y。我們現在需要求的是X,那么就是求這個方程對于X的最小整數解。由于方程有兩個未知數,所以必須化簡方程,使得一個未知數的系數為0時才能得解。設B > A時有: AX - BY = 1 那么可以認為B = AN + M,則有: AX - ( AN + M )Y = 1 AX - ANY - MY = 1 A( X - NY ) - MY = 1 實際上M就是B mod A的值,設X’ = X - NY,B’ = B mod A則有AX’ - B’Y = 1,且A > M成立。接著可以用同樣的方法來化簡A,最終必能將一個系數化為0。此時求出另一個未知數的解,再按逆序代入上一步的方程,求出另一個未知數的解,再代入上一步的方程,一直遞推的第一個方程,最終即可獲得X和Y的最小整數解。因為每一步遞推的方程的余數相同,所以我們稱這些方程為“一次同余式”。這個算法被稱為歐幾里德擴展算法,而歐幾里德算法其實就是求公因式的輾轉相除法,大多數朋友在中學時就學過了,但是我們下面會用到,所以我這里簡單的用偽代碼來描述一下歐幾里德算法。算法六:求A和B兩相異自然數的最大公因數,另R為結果。 IF A P。并且有正整數D使ND mod P = 1成立,求D。 IF N和P的最大公因數 <> 1 ;調用算法六 RETURN FAILE END IF LT := 1 ;左上 RT := N mod P ;右上 LD := 0 ;左下 RD := P ;右下 X := 0 ;中間變量 WHILE RT <> 1 X := RD / RT RD := RD % RT IF RD = 0 RD := RT LD := ( X - 1 ) * LT + LD ELSE LD := X * LT + 1 END IF X := RT / RD RT := RT % RD IF RT = 0 RT := RD LT := ( X - 1 ) * LD + LT ELSE LT := X * LD + 1 END IF NEXT D := LT ·結語到現在,RSA算法中所涉及到的所有算法我們都已經討論過了。實際還有一個運算,就是私鑰的獲得辦法:計算得到與( P - 1 ) * ( Q - 1 )的值互質的整數E。我情愿不把它稱之為算法,因為只需要一個循環和一個判斷就可以完成,所以這里也就沒有必要對它多加論述了。 ·附錄費馬小定理的證明:引理1:設M,A的最大公約數( M,A ) = 1,且M整除AB,即M mod AB = 0,則M mod B = 0。引理2:設P是素數,
表示組合數,即從P個數中選出J個數的組合種數,且1 ≤ J ≤ P - 1,則P mod
。證明:已知組合數
= P! / ( J! * ( P - J )! )是整數,即J! * ( P - J )! mod P! = 0。由于P是素數,所以對任意1 ≤ I ≤ P-1有( P,I ) = 1。因此由引理1有( P,j! * ( P - J )! ) = 1,1 ≤ J ≤ P - 1。進而由引理1推出:當1 ≤ J ≤ P - 1時J! * ( P - J )! mod ( P - 1 )! = 0,得證。
posted @
2008-05-30 01:47 幽幽 閱讀(1418) |
評論 (0) |
編輯 收藏
1003 1004 1005 1009 1016 1019 1020 1021 1023 1040 1042 1049 1058 1070 1071 1076 1084 1097 1108 1128 1130 1134 1157 1161 1163 1164 1170 1176 1177 1178 1196 1197 1200 1201 1202 1212 1215 1219 1225 1228 1229 1231 1234 1235 1236 1239 1248 1249 1250 1257 1262 1263 1266 1276 1279 1280 1282 1283 1284 1286 1290 1297 1303 1321 1326 1328 1337 1395 1405 1406 1412 1420 1425 1491 1517 1536 1555 1562 1563 1597 1715 1720 1785 1846 1847 1848 1849 1850 2028 2029 2031 2032 2034 2039 2040 2043 2044 2047 2048 2049 2052 2053 2057 2064 2067 2071 2072 2074 2076 2079 2080 2083 2085 2086 2087 2088 2090 2091 2092 2097 2098 2100 2101 2106 2107 2109 2111 2114 2115 2117 2123 2124 2130 2131 2132 2134 2135 2139 2140 2147
HDOJ過兩百,先拿這些題下手
posted @
2008-05-26 03:56 幽幽 閱讀(381) |
評論 (1) |
編輯 收藏
說實話,我剛開始看見Tab Control的時候,覺得很簡單。哪知道用了一下,才發現自己錯了。
要用好它,還是需要一些技巧的。經過網上搜索資料,以及我自己的摸索,把一些要點記錄在這里。
Tab Control的運行效果有點像Property Sheet,但兩者還是有一些區別。我的理解就是Property Sheet主要用在對話框中,對數據進行進行分類管理。而Tab Control使用范圍更廣一些,既可以用在對話框,也可以用在視圖中,除了可以管理配置數據外,還可以對軟件的組織進行規劃,比如可以通過它來切換不同的視圖等等。
當然這不是沒有代價的,Tab Control的編程就比Property Sheet的復雜很多。
我最初有點搞不懂,如何在Tab Control中使用不同的Page,就象Property Page一樣,Tab Control并沒有提供便利的機制讓你輕松做到這一點。還好,VC是最棒的,撒花~通過變通的方法還是可以做到這一點。
不羅嗦了,上代碼。
假如我現在有個SDI程序,View是Form View,想在上面放個Tab Control,包含兩個Page。現在讓我們來看看應該怎樣處理。
首先當然要增加一個Tab Control資源,然后利用Class Wizard,在View中增加一個Control變量。
接著建立兩個對話框資源,別忘了把Style改為Child,Border改為None。然后就可以在上面加其他控件了。
接著利用Class Wizard,分別為這兩個對話框建立兩個類,比如CPage1和CPage2。
然后在View類頭文件中,加入這兩個對話框對象。同時增加一個變量int m_CurSelTab,用了表明是哪個Page即將被切換。
為了避免用戶在切換Tab時,程序對Tab Index的枚舉,可以利用數組來做這個事情。
在View的初始化函數中需要把CPage1、CPage2和Tab Control關聯起來,并保存頁面地址,設置初始頁面,等等。
void CTab_testView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
//為Tab Control增加兩個頁面
m_tab.InsertItem(0, _T("First"));
m_tab.InsertItem(1, _T("Second"));
//創建兩個對話框
m_page1.Create(IDD_DIALOG1, &m_tab);
m_page2.Create(IDD_DIALOG2, &m_tab);
//設定在Tab內顯示的范圍
CRect rc;
m_tab.GetClientRect(rc);
rc.top += 20;
rc.bottom -= 8;
rc.left += 8;
rc.right -= 8;
m_page1.MoveWindow(&rc);
m_page2.MoveWindow(&rc);
//把對話框對象指針保存起來
pDialog[0] = &m_page1;
pDialog[1] = &m_page2;
//顯示初始頁面
pDialog[0]->ShowWindow(SW_SHOW);
pDialog[1]->ShowWindow(SW_HIDE);
//保存當前選擇
m_CurSelTab = 0;
}
這里面需要注意的是,我用了一個CDialog指針數組來進行保存,數組的大小是Tab Control頁面的個數,數組下標對應著每個頁面的索引(這樣方便快速存取)。
用戶切換時,需要響應相關的消息。
void CTab_testView::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)
{
// TODO: Add your control notification handler code here
pDialog[m_CurSelTab]->ShowWindow(SW_HIDE);
m_CurSelTab = m_tab.GetCurSel();
pDialog[m_CurSelTab]->ShowWindow(SW_SHOW);
*pResult = 0;
}
首先我們先把當前的頁面隱藏起來,然后得到新的頁面索引,最后就把相關頁面顯示出來即可。這比一個個去枚舉簡單多了。
還有一點比較有意思,那就是DDX/DDV機制的運用。要想獲得Tab Control各個頁面的數據,可以利用DDX/DDV機制,但需要注意,因為這是多個頁面,所以需要顯式調用多次。
void CTab_testView::OnButton1()
{
// TODO: Add your control notification handler code here
m_page1.UpdateData();
m_page2.UpdateData();
CString str1 = m_page1.m_str1;
CString str2 = m_page2.m_str2;
AfxMessageBox(str1);
AfxMessageBox(str2);
}
經過這幾步處理,基本上我們就可以利用Tab Control的強大功能了。
posted @
2008-05-19 02:18 幽幽 閱讀(1681) |
評論 (0) |
編輯 收藏
抨擊匈牙利命名法
匈牙利命名法是一種編程時的命名規范。命名規范是程序書寫規范中最重要也是最富爭議的地方,自古乃兵家必爭之地。命名規范有何用?四個字:名正言順。用二分法,命名規范分為好的命名規范和壞的命名規范,也就是說名正言順的命名規范和名不正言不順的命名規范。好的舞鞋是讓舞者感覺不到其存在的舞鞋,壞的舞鞋是讓舞者帶著鐐銬起舞。一個壞的命名規范具有的破壞力比一個好的命名規范具有的創造力要大得多。
本文要證明的是:匈牙利命名法是一個壞的命名規范。本文的作用范圍為靜態強類型編程語言。本文的分析范本為C語言和C++語言。下文中的匈法為匈牙利命名法的簡稱。
一 匈牙利命名法的成本
匈法的表現形式為給變量名附加上類型名前綴,例如:nFoo,szFoo,pFoo,cpFoo分別表示整型變量,字符串型變量,指針型變量和常指針型變量。可以看出,匈法將變量的類型信息從單一地點(聲明變量處)復制到了多個地點(使用變量處),這是冗余法。冗余法的成本之一是要維護副本的一致性。這個成本在編寫和維護代碼的過程中需要改變變量的類型時付出。冗余法的成本之二是占用了額外的空間。一個優秀的書寫者會自覺地遵從一個法則:代碼最小組織單位的長度以30個自然行以下為宜,如果超過50行就應該重新組織。一個變量的書寫空間會給這一法則添加不必要的難度。
二 匈牙利命名法的收益
這里要證明匈牙利命名法的收益是含糊的,無法預期的。
范本1:strcpy(pstrFoo,pcstrFoo2) Vs strcpy(foo,foo2)
匈法在這里有什么收益呢?我看不到。沒有一個程序員會承認自己不知道strcpy函數的參數類型吧。
范本2:unknown_function(nFoo) Vs unknown_function(foo)
匈法在這里有什么收益呢?我看不到。對于一個不知道確定類型的函數,程序員應該去查看該函數的文檔,這是一種成本。使用匈法的唯一好處是看代碼的人知道這個函數要求一個整型參數,這又有什么用處呢?函數是一種接口,參數的類型僅僅是接口中的一小部分。諸如函數的功能、出口信息、線程安全性、異常安全性、參數合法性等重要信息還是必須查閱文檔。
范本3:nFoo=nBar Vs foo=bar
匈法在這里有什么收益呢?我看不到。使用匈法的唯一好處是看代碼的人知道這里發生了一個整型變量的復制動作,聽起來沒什么問題,可以安心睡大覺了。如果他看到的是nFoo=szBar,可能會從美夢中驚醒。且慢,事情真的會是這樣嗎?我想首先被驚醒的應該是編譯器。另一方面,nFoo=nBar只是在語法上合法而已,看代碼的人真正關心的是語義的合法性,匈法對此毫無幫助。另一方面,一個優秀的書寫者會自覺地遵從一個法則:代碼最小組織單位中的臨時變量以一兩個為宜,如果超過三個就應該重新組織。結合前述第一個法則,可以得出這樣的結論:易于理解的代碼本身就應該是易于理解的,這是代碼的內建高質量。好的命名規范對內建高質量的助益相當有限,而壞的命名規范對內建高質量的損害比人們想象的要大。
三 匈牙利命名法的實施
這里要證明匈牙利命名法在C語言是難以實施的,在C++語言中是無法實施的。從邏輯上講,對匈法的收益做出否定的結論以后,再來論證匈法的可行性,是畫蛇添足。不過有鑒于小馬哥曾讓已射殺之敵死灰復燃,我還是再踏上一支腳為妙。
前面講過,匈法是類型系統的冗余,所以實施匈法的關鍵是我們是否能夠精確地對類型系統進行復制。這取決于類型系統的復雜性。
先來看看C語言:
1.內置類型:int,char,float,double 復制為 n,ch,f,d?好像沒有什么問題。不過誰來告訴我void應該怎么表示?
2.組合類型:array,union,enum,struct 復制為 a,u,e,s?好象比較別扭。
這里的難點不是為主類型取名,而是為副類型取名。an表示整型數組?sfoo,sbar表示結構foo,結構bar?ausfoo表示聯合結構foo數組?累不累啊。
3.特殊類型:pointer。pointer在理論上應該是組合類型,但是在C語言中可以認為是內置類型,因為C語言并沒有非常嚴格地區分不同的指針類型。下面開始表演:pausfoo表示聯合結構foo數組指針?ppp表示指針的指針的指針?
噩夢還沒有結束,再來看看類型系統更阿為豐富的C++語言:
1.class:如果說C語言中的struct還可以用stru搪塞過去的話,不要夢想用cls來搪塞C++中的class。嚴格地講,class根本就并不是一個類型,而是創造類型的工具,在C++中,語言內置類型的數量和class創造的用戶自定義類型的數量相比完全可以忽略不計。stdvectorFoo表示標準庫向量類型變量Foo?瘋狂的念頭。
2.命名空間:boostfilesystemiteratorFoo,表示boost空間filesystem子空間遍歷目錄類型變量Foo?程序員要崩潰了。
3.模板:你記得std::map<std::string,std::string>類型的確切名字嗎?我是記不得了,好像超過255個字符,還是饒了我吧。
4.模板參數:template <class T, class BinaryPredicate>const T& max(const T& a, const T& b, BinaryPredicate comp) 聰明的你,請用匈法為T命名。上帝在發笑。
5.類型修飾:static,extern,mutable,register,volatile,const,short,long,unsigned 噩夢加上修飾是什么?還是噩夢。
posted @
2008-05-02 02:26 幽幽 閱讀(2546) |
評論 (10) |
編輯 收藏
摘要: 首先,制作交叉工具鏈的目的是為了給我的手機--MOTO ROKR E2編譯程序。然后,順便學習一下嵌入式軟件的開發先說一下,搞這個需要很大的耐心。我用的硬件是Sempron3100+, 512MB內存, 編譯環境是windowsXP + vmware5.5 + gentoo, 在CUI下,花了大概20個小時才編譯完(我從晚上八點一直弄到第二天下午四點)。1. 準備源碼: binuitls... 閱讀全文
posted @
2008-02-10 16:18 幽幽 閱讀(1976) |
評論 (0) |
編輯 收藏
摘要: 問題描述:在一個N * N的棋盤上放N個皇后, 并使皇后們不能互相攻擊,皇后可以橫著、豎著、斜著吃子,即棋盤上任意兩個皇后不能同行、同列或在一條對角線上。以下是我寫的代碼:
1#include <iostream> 2#include <stack> 3using namespace std;&nbs... 閱讀全文
posted @
2008-02-10 15:26 幽幽 閱讀(762) |
評論 (0) |
編輯 收藏
取得程序的名字
#include <windows.h>

int main()


{
void *PEB, *Ldr, *Flink, *FullImagePath;
wchar_t *Name = NULL;

__asm

{
mov eax,fs:[0x30]
mov PEB,eax
}
Ldr = *( ( void ** )( ( unsigned char * )PEB + 0x0c ) );
Flink = *( ( void ** )( ( unsigned char * )Ldr + 0x0c ) );

FullImagePath = *( ( void ** )( ( unsigned char * )Flink + 0x28 ) );

Name = wcsrchr((wchar_t*)FullImagePath, 0x5C) + 1;

MessageBoxW(NULL, Name, L"應用程序的名字為:", MB_OK );

return(0);
}

posted @
2008-02-07 17:38 幽幽 閱讀(248) |
評論 (0) |
編輯 收藏