摘自:http://www.diybl.com/course/3_program/c++/cppjs/2008426/111619.html
去年11月的MSDN雜志曾刊登過一篇文章 Break Free of Code Deadlocks in Critical Sections Under Windows ,Matt Pietrek 和 Russ Osterlund 兩位對臨界區(Critical Section)的內部實現做了一次簡短的介紹,但點到為止,沒有繼續深入下去,當時給我的感覺就是癢癢的,呵呵,于是用IDA和SoftIce大致分析了一下臨界區的實現,大致弄明白了原理后也就沒有深究?,F在乘著Win2k源碼的東風,重新分析一下這塊的內容,做個小小的總結吧 :P
臨界區(Critical Section)是Win32中提供的一種輕量級的同步機制,與互斥(Mutex)和事件(Event)等內核同步對象相比,臨界區是完全在用戶態維護的,所以僅能在同一進程內供線程同步使用,但也因此無需在使用時進行用戶態和核心態之間的切換,工作效率大大高于其它同步機制。
臨界區的使用方法非常簡單,使用 InitializeCriticalSection 或 InitializeCriticalSectionAndSpinCount 函數初始化一個 CRITICAL_SECTION 結構;使用 SetCriticalSectionSpinCount 函數設置臨界區的Spin計數器;然后使用 EnterCriticalSection 或 TryEnterCriticalSection 獲取臨界區的所有權;完成需要同步的操作后,使用 LeaveCriticalSection 函數釋放臨界區;最后使用 DeleteCriticalSection 函數析構臨界區結構。
以下是MSDN中提供的一個簡單的例子:
以下為引用:
// Global variable
CRITICAL_SECTION CriticalSection;
void main()
{
...
// Initialize the critical section one time only.
if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) )
return;
...
// Release resources used by the critical section object.
DeleteCriticalSection(&CriticalSection)
}
DWORD WINAPI ThreadProc( LPVOID lpParameter )
{
...
// Request ownership of the critical section.
EnterCriticalSection(&CriticalSection);
// Access the shared resource.
// Release ownership of the critical section.
LeaveCriticalSection(&CriticalSection);
...
}
首先看看構造和析構臨界區結構的函數。
InitializeCriticalSection 函數(ntosdll esource.c:1210)實際上是調用 InitializeCriticalSectionAndSpinCount 函數(resource.c:1266)完成功能的,只不過傳入一個值為0的初始Spin計數器;InitializeCriticalSectionAndSpinCount 函數主要完成兩部分工作:初始化 RTL_CRITICAL_SECTION 結構和 RTL_CRITICAL_SECTION_DEBUG 結構。前者是臨界區的核心結構,下面將著重討論;后者是調試用結構,Matt 那篇文章里面分析的很清楚了,我這兒就不羅嗦了 :P
RTL_CRITICAL_SECTION結構在winnt.h中定義如下:
以下為引用:
typedef struct _RTL_CRITICAL_SECTION {
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
//
// The following three fields control entering and exiting the critical
// section for the resource
//
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread; // from the thread''s
ClientId->UniqueThread
HANDLE LockSemaphore;
ULONG_PTR SpinCount; // force size on 64-bit systems when packed
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
InitializeCriticalSectionAndSpinCount 函數中首先對臨界區結構進行了初始化
DebugInfo 字段指向初始化臨界區時分配的RTL_CRITICAL_SECTION_DEBUG結構;
LockCount 字段是臨界區中最重要的字段,初始值為-1,當臨界區被獲取(Hold)時此字段大于等于0;
RecursionCount 字段保存當前臨界區所有者線程的獲取緩沖區嵌套層數,初始值為0;
OwningThread 字段保存當前臨界區所有者線程的句柄,初始值為0;
LockSemaphore 字段實際上是一個auto-reset的事件句柄,用于喚醒等待獲取臨界區的阻塞線程,初始值為0;
SpinCount 字段用于在多處理器環境下完成輕量級的CPU見同步,單處理器時沒有使用(初始值為0),多處理器時設置為SpinCount參數值(最大為MAX_SPIN_COUNT=0x00ffffff)。此外 RtlSetCriticalSectionSpinCount 函數(resource.c:1374)的代碼與這兒設置SpinCount的代碼完全一樣。
初始化臨界區結構后,函數會根據SpinCount參數的一個標志位判斷是否需要預先初始化 LockSemaphore 字段,如果需要則使用NtCreateEvent創建一個具有訪問權限的同步用事件核心對象,初始狀態為沒有激發。這一初始化本來是在 EnterCriticalSection 函數中完成的,將之顯式提前可以進一步優化 EnterCriticalSection 函數的性能。
值得注意的是,這一特性僅對Win2k有效。MSDN里面說明如下:
以下為引用:
Windows 2000: If the high-order bit is set, the function preallocates the event used by the EnterCriticalSection function. Do not set this bit if you are creating a large number of critical section objects, because it will consume a significant amount of nonpaged pool. This flag is not necessary on Windows XP and later, and it is ignored.
與之對應的 DeleteCriticalSection 函數完成關閉事件句柄和是否調試結構的功能。
臨界區真正的核心代碼在win2kprivate tosdlli386critsect.asm里面,包括_RtlEnterCriticalSection、_RtlTryEnterCriticalSection和_RtlLeaveCriticalSection三個函數。
_RtlEnterCriticalSection 函數 (critsect.asm:85) 首先檢查SpinCount是否為0,如果不為0則處理多處理器架構下的問題[分支1];如為0則使用原子操作給LockCount加一,并判斷是否其值為0。如果加一后LockCount大于0,則此臨界區已經被獲取[分支2];如為0則獲取當前線程TEB中的線程句柄,保存在臨界區的OwningThread字段中,并將RecursionCount設置為1。調試版本則調用RtlpCriticalSectionIsOwned函數在調試模式下驗證此緩沖區是被當前線程獲取的,否則在調試模式下激活調試器。最后還會更新TEB的CountOfOwnedCriticalSections計數器,以及臨界區調試結構中的EntryCount字段。
如果此臨界區已經被獲取[分支2],則判斷獲取臨界區的線程句柄是否與當前線程相符。如果是同一線程則直接將RecursionCount和調試結構的EntryCount字段加一;如果不是當前線程,則調用RtlpWaitForCriticalSection函數等待此臨界區,并從頭開始執行獲取臨界區的程序。
多CPU情況的分支處理方式類似,只是多了對SpinCount的雙重檢查處理。
接著的_RtlTryEnterCriticalSection(critsect.asm:343)函數是一個輕量級的嘗試獲取臨界區的函數。偽代碼如下:
以下為引用:
if(CriticalSection->LockCount == -1)
{
// 臨界區可用
CriticalSection->LockCount = 0;
CriticalSection->OwningThread = TEB->ClientID;
CriticalSection->RecursionCount = 1;
return TRUE;
}
else
{
if(CriticalSection->OwningThread == TEB->ClientID)
{
// 臨界區是當前線程獲取
CriticalSection->LockCount++;
CriticalSection->RecursionCount++;
return TRUE;
}
else
{
// 臨界區已被其它線程獲取
return FALSE;
}
}
最后的_RtlLeaveCriticalSection(critsect.asm:271)函數釋放已獲取的臨界區,實現就比較簡單了,實際上就是對嵌套計數器和鎖定計數器進行操作。偽代碼如下:
以下為引用:
if(--CriticalSection->RecursionCount == 0)
{
bsp; // 臨界區已不再被使用
CriticalSection->OwningThread = 0;
if(--CriticalSection->LockCount)
{
// 仍有線程鎖定在臨界區上
_RtlpUnWaitCriticalSection(CriticalSection)
}
}
else
{
--CriticalSection->LockCount
}
posted @
2008-12-01 14:05 Sandy 閱讀(1814) |
評論 (0) |
編輯 收藏
一、宏中“#”和“##”的用法:
一般用法:使用“#”把宏參數變為一個字符串,用”##”把兩個宏參數結合在一起
例子:
#include <iostream>
using namespace std;

#define TEST1(x) (cout<<id##x<<endl);
#define TEST2(p) (cout<<#p<<endl);
int main()


{
int id1 = 1001;
int id2 = 1002;
TEST1(1); // == cout<< id1 << endl;
TEST2(2); // == cout<< "2" << endl;
TEST1(2); // == cout<< id2 << endl;

system("pause");
return 0;
}
二、防止一個頭文件被重復包含
#ifndef COMDEF_H
#define COMDEF_H //頭文件內容
#endif
當你所建的工程有多個源文件組成時,很可能會在多個文件里頭包含了同一個頭文件,如果借用上面的宏定義就能夠避免同一個頭文件被重復包含時進行多次編譯。因為當它編譯第一個頭文件時總是沒有定義#define COMDEF_H,那么它將編譯一遍頭文件中所有的內容,包括定義#define COMDEF_H。這樣編譯再往下進行時如果遇到同樣要編譯的頭文件,那么由于語句#ifndef COMDEF_H的存在它將不再重復的編譯這個頭文件。
三、常用的宏定義
__DATE__
進行預處理的日期(“Mmm dd yyyy”形式的字符串文字)
__FILE__
代表當前源代碼文件名的字符串文字
__LINE__
代表當前源代碼中的行號的整數常量
__TIME__
源文件編譯時間,格式微“hh:mm:ss”
參考文章:
C中的預編譯宏定義 http://blog.readnovel.com/article/htm/tid_900939.html
C標準中一些預定義的宏 http://www.programfan.com/article/2883.html
C語言常用宏定義技巧 http://blog.21ic.com/user1/3074/archives/2008/51567.html
C語言宏定義技巧(常用宏定義) http://blog.21ic.com/user1/69/archives/2006/13695.html
宏定義:http://blog.csdn.net/believefym/archive/2007/10/21/1836162.aspx
好好學習!
posted @
2008-12-01 13:54 Sandy 閱讀(1882) |
評論 (0) |
編輯 收藏
好文章,大家一起分享。
一直對extern "c"不是很明白。今天,看了一篇文章《C++中extern “C”含義深層探索》,對這個問題終于有所認識。轉過來與大家分享。
鏈接:
http://developer.51cto.com/art/200510/9066.htm作者:宋寶華
1.引言
C++語言的創建初衷是“a better C”,但是這并不意味著C++中類似C語言的全局變量和函數所采用的編譯和連接方式與C語言完全相同。作為一種欲與C兼容的語言,C++保留了一部分過程式語言的特點(被世人稱為“不徹底地面向對象”),因而它可以定義不屬于任何類的全局變量和函數。但是,C++畢竟是一種面向對象的程序設計語言,為了支持函數的重載,C++對全局函數的處理方式與C有明顯的不同。
2.從標準頭文件說起
某企業曾經給出如下的一道面試題:
面試題:為什么標準頭文件都有類似以下的結構?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
分析
顯然,頭文件中的編譯宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止該頭文件被重復引用。
那么
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
的作用又是什么呢?我們將在下文一一道來。
3.深層揭密extern "C"
extern "C" 包含雙重含義,從字面上即可得到:首先,被它修飾的目標是“extern”的;其次,被它修飾的目標是“C”的。讓我們來詳細解讀這兩重含義。
被extern "C"限定的函數或變量是extern類型的;
extern是C/C++語言中表明函數和全局變量作用范圍(可見性)的關鍵字,該關鍵字告訴編譯器,其聲明的函數和變量可以在本模塊或其它模塊中使用。記住,下列語句:
extern int a;
僅僅是一個變量的聲明,其并不是在定義變量a,并未為a分配內存空間。變量a在所有模塊中作為一種全局變量只能被定義一次,否則會出現連接錯誤。
通常,在模塊的頭文件中對本模塊提供給其它模塊引用的函數和全局變量以關鍵字extern聲明。例如,如果模塊B欲引用該模塊A中定義的全局變量和函數時只需包含模塊A的頭文件即可。這樣,模塊B中調用模塊A中的函數時,在編譯階段,模塊B雖然找不到該函數,但是并不會報錯;它會在連接階段中從模塊A編譯生成的目標代碼中找到此函數。
與extern對應的關鍵字是static,被它修飾的全局變量和函數只能在本模塊中使用。因此,一個函數或變量只可能被本模塊使用時,其不可能被extern “C”修飾。
被extern "C"修飾的變量和函數是按照C語言方式編譯和連接的;
未加extern “C”聲明時的編譯方式
首先看看C++中對類似C的函數是怎樣編譯的。
作為一種面向對象的語言,C++支持函數重載,而過程式語言C則不支持。函數被C++編譯后在符號庫中的名字與C語言的不同。例如,假設某個函數的原型為:
void foo( int x, int y );
該函數被C編譯器編譯后在符號庫中的名字為_foo,而C++編譯器則會產生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機制,生成的新名字稱為“mangled name”)。
foo_int_int這樣的名字包含了函數名、函數參數數量及類型信息,C++就是靠這種機制來實現函數重載的。例如,在C++中,函數void foo( int x, int y )與void foo( int x, float y )編譯生成的符號是不相同的,后者為_foo_int_float。
同樣地,C++中的變量除支持局部變量外,還支持類成員變量和全局變量。用戶所編寫程序的類成員變量可能與全局變量同名,我們以"."來區分。而本質上,編譯器在進行編譯時,與函數的處理相似,也為類中的變量取了一個獨一無二的名字,這個名字與用戶程序中同名的全局變量名字不同。
未加extern "C"聲明時的連接方式
假設在C++中,模塊A的頭文件如下:
// 模塊A頭文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif
在模塊B中引用該函數:
// 模塊B實現文件 moduleB.cpp
#include "moduleA.h"
foo(2,3);
實際上,在連接階段,連接器會從模塊A生成的目標文件moduleA.obj中尋找_foo_int_int這樣的符號!
加extern "C"聲明后的編譯和連接方式
加extern "C"聲明后,模塊A的頭文件變為:
// 模塊A頭文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo( int x, int y );
#endif
在模塊B的實現文件中仍然調用foo( 2,3 ),其結果是:
(1)模塊A編譯生成foo的目標代碼時,沒有對其名字進行特殊處理,采用了C語言的方式;
(2)連接器在為模塊B的目標代碼尋找foo(2,3)調用時,尋找的是未經修改的符號名_foo。
如果在模塊A中函數聲明了foo為extern "C"類型,而模塊B中包含的是extern int foo( int x, int y ) ,則模塊B找不到模塊A中的函數;反之亦然。
所以,可以用一句話概括extern “C”這個聲明的真實目的(任何語言中的任何語法特性的誕生都不是隨意而為的,來源于真實世界的需求驅動。我們在思考問題時,不能只停留在這個語言是怎么做的,還要問一問它為什么要這么做,動機是什么,這樣我們可以更深入地理解許多問題):
實現C++與C及其它語言的混合編程。
明白了C++中extern "C"的設立動機,我們下面來具體分析extern "C"通常的使用技巧。
4.extern "C"的慣用法
(1)在C++中引用C語言中的函數和變量,在包含C語言頭文件(假設為cExample.h)時,需進行下列處理:
extern "C"
{
#include "cExample.h"
}
而在C語言的頭文件中,對其外部函數只能指定為extern類型,C語言中不支持extern "C"聲明,在.c文件中包含了extern "C"時會出現編譯語法錯誤。
筆者編寫的C++引用C函數例子工程中包含的三個文件的源代碼如下:
/* c語言頭文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif
/* c語言實現文件:cExample.c */
#include "cExample.h"
int add( int x, int y )
{
return x + y;
}
// c++實現文件,調用add:cppFile.cpp
extern "C"
{
#include "cExample.h"
}
int main(int argc, char* argv[])
{
add(2,3);
return 0;
}
如果C++調用一個C語言編寫的.DLL時,當包括.DLL的頭文件或聲明接口函數時,應加extern "C" { }。
(2)在C中引用C++語言中的函數和變量時,C++的頭文件需添加extern "C",但是在C語言中不能直接引用聲明了extern "C"的該頭文件,應該僅將C文件中將C++中定義的extern "C"函數聲明為extern類型。
筆者編寫的C引用C++函數例子工程中包含的三個文件的源代碼如下:
//C++頭文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif
//C++實現文件 cppExample.cpp
#include "cppExample.h"
int add( int x, int y )
{
return x + y;
}
/* C實現文件 cFile.c
/* 這樣會編譯出錯:#include "cExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}
如果深入理解了第3節中所闡述的extern "C"在編譯和連接階段發揮的作用,就能真正理解本節所闡述的從C++引用C函數和C引用C++函數的慣用法。
posted @
2008-12-01 11:43 Sandy 閱讀(326) |
評論 (0) |
編輯 收藏
MUI,其英文全拼為:Multilingual User Interface。這幾天思考如何運用多語言的問題,查到了一些相關資料,貼出來與大家分享。
Multilingual User Interface (MUI)
http://msdn.microsoft.com/en-us/library/aa913592.aspx
The Multilingual User Interface (MUI) allows users to change the language of the user interface (UI). To make this possible, the MUI uses a single core binary that includes the system default language, together with one resource dynamic-link library (DLL) for each additional target language. The target device boots with the system default language and then a new user-selected language goes into effect after a soft reset. This switch requires recreating windows, menus, and dialog boxes with the newly loaded resources. In addition, to be considered successful, the language switch must display these elements with the correct fonts and with the correct locale-specific information.
In This Section
For All Platforms:
Multilingual User Interface (MUI) Application Development
Provides an overview of the MUI architecture. Provides instructions on how to work with fonts in a multilingual user-interface environment and shows how to use MUI with applications.
Multilingual User Interface (MUI) Reference
Provides reference pages for the MUI application programming interface.
For Windows Embedded CE:
Multilingual User Interface (MUI) OS Design Development
Provides information about the MUI support that is helpful when designing and developing a Windows Embedded CE OS. This includes dependency information, the modules and components that implement MUI, and MUI implementation considerations.
How to Create a Multilingual Run-time Image Using MUI
Demonstrates how to create a run-time image in both French and English by using MUI.
Multilingual User Interface (MUI) Registry Settings
Provides registry-related information for MUI.
Multilingual User Interface (MUI) Migration
Provides information about factors to consider when migrating to a newer version of Windows Embedded CE
posted @
2008-11-23 11:07 Sandy 閱讀(2052) |
評論 (0) |
編輯 收藏
總在快樂的時候,感到微微的惶恐
在開懷大笑時,留下感動的淚水
我無法相信單純的幸福
對人生的起伏悲苦,既坦然又不安
————摘自幾米漫畫
摘了這么幾句與大家分享。心情煩惱得時候,會看看漫畫,緩解一些心情。如果你也有心煩意亂的時候,看看漫畫,或者看看喜劇,開懷的一笑,讓你從你煩惱中脫離。
最近心情也相當壓抑,甚至萬分郁悶。周末了,看到這么幾句,心有所感,與大家分享。
posted @
2008-11-22 00:24 Sandy 閱讀(574) |
評論 (0) |
編輯 收藏
如何添加代碼注釋?
這個我一直沒有搞懂。自己隨便涂鴉,也懶得加注釋。但是真正參加項目,有時也會找理由不添注釋。看了一些書,參加一些培訓,有的人說,加注釋好,有人說加注釋不好。好有好的地方,壞也有壞的味道。對于我這個初來乍到之人來說,反而倒是非常迷惑。
最近看到一篇文章“添加代碼注釋13個技巧”,也解除了心中的部分疑團。貼在這里與大家分享,也備以后參考。
鏈接:
http://sunxinhe.yo2.cn/articles/%E3%80%90%E8%BD%AC%E3%80%91%E6%B7%BB%E5%8A%A0%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A13%E4%B8%AA%E6%8A%80%E5%B7%A7.html添加代碼注釋13個技巧
作者:José M. Aguilar(西班牙語)
英文譯者:Timm Martin
中文譯者:numenzq
下面的13個技巧向你展示如何添加代碼注釋,這些技巧都很容易理解和記憶。
1. 逐層注釋
為每個代碼塊添加注釋,并在每一層使用統一的注釋方法和風格。例如:
針對每個類:包括摘要信息、作者信息、以及最近修改日期等
針對每個方法:包括用途、功能、參數和返回值等
在團隊工作中,采用標準化的注釋尤為重要。當然,使用注釋規范和工具(例如C#里的XML,Java里的Javadoc)可以更好的推動注釋工作完成得更好。
2. 使用分段注釋
如果有多個代碼塊,而每個代碼塊完成一個單一任務,則在每個代碼塊前添加一個注釋來向讀者說明這段代碼的功能。例子如下:
// Check that all data records
// are correct
foreach (Record record in records)
{
if (rec.checkStatus()==Status.OK)
{
. . .
}
}
// Now we begin to perform
// transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .
3. 在代碼行后添加注釋
如果多行代碼的每行都要添加注釋,則在每行代碼后添加該行的注釋,這將很容易理解。例如:
const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F; // mask bit TCP
在分隔代碼和注釋時,有的開發者使用tab鍵,而另一些則使用空格鍵。然而由于tab鍵在各編輯器和IDE工具之間的表現不一致,因此最好的方法還是使用空格鍵。
4. 不要侮辱讀者的智慧
避免以下顯而易見的注釋:
if (a == 5) // if a equals 5
counter = 0; // set the counter to zero
寫這些無用的注釋會浪費你的時間,并將轉移讀者對該代碼細節的理解。
5. 禮貌點
避免粗魯的注釋,如:“注意,愚蠢的使用者才會輸入一個負數”或“剛修復的這個問題出于最初的無能開發者之手”。這樣的注釋能夠反映到它的作者是多么的拙劣,你也永遠不知道誰將會閱讀這些注釋,可能是:你的老板,客戶,或者是你剛才侮辱過的無能開發者。
6. 關注要點
不要寫過多的需要轉意且不易理解的注釋。避免ASCII藝術,搞笑,詩情畫意,hyperverbosity的注釋。簡而言之,保持注釋簡單直接。
7. 使用一致的注釋風格
一些人堅信注釋應該寫到能被非編程者理解的程度。而其他的人則認為注釋只要能 被開發人員理解就行了。無論如何,Successful Strategies for Commenting Code已經規定和闡述了注釋的一致性和針對的讀者。就個人而言,我懷疑大部分非編程人員將會去閱讀代碼,因此注釋應該是針對其他的開發者而言。
8. 使用特有的標簽
在一個團隊工作中工作時,為了便于與其它程序員溝通,應該采用一致的標簽集進行注釋。例如,在很多團隊中用TODO標簽表示該代碼段還需要額外的工作。
int Estimate(int x, int y)
{
// TODO: implement the calculations
return 0;
}
注釋標簽切忌不要用于解釋代碼,它只是引起注意或傳遞信息。如果你使用這個技巧,記得追蹤并確認這些信息所表示的是什么。
9. 在代碼時添加注釋
在寫代碼時就添加注釋,這時在你腦海里的是清晰完整的思路。如果在代碼最后再添 加同樣注釋,它將多花費你一倍的時間。而“我沒有時間寫注釋”,“我很忙”和“項目已經延期了”這都是不愿寫注釋而找的借口。一些開發者覺得應該 write comments before code,用于理清頭緒。例如:
public void ProcessOrder()
{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}
10. 為自己注釋代碼
當注釋代碼時,要考慮到不僅將來維護你代碼的開發人員要看,而且你自己也可能要看。用Phil Haack大師的話來說就是:“一旦一行代碼顯示屏幕上,你也就成了這段代碼的維護者”。因此,對于我們寫得好(差)的注釋而言,我們將是第一個受益者(受害者)。
11. 同時更新代碼和注釋
如果注釋沒有跟隨代碼的變化而變化,及時是正確的注釋也沒有用。代碼和注釋應該同步變化,否則這樣的注釋將對維護你代碼的開發者帶來更大的困難。使用重構工具時應特別注意,它只會自動更新代碼而不會修改注釋,因此應該立即停止使用重構工具。
12. 注釋的黃金規則:易讀的代碼
對于開發者的一個基本原則就是:讓你的代碼為己解釋。雖然有些人懷疑這會讓那些不愿意寫注釋的開發者鉆空子,不過這樣的代碼真的會使你容易理解,還不需要額外維護注釋。例如在Fluid Interfaces文章里向你展示的代碼一樣:
Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine( "Result: {0}", calc.Get() );
在這個例子中,注釋是不需要的,否則可能就違反了技巧4。為了使代碼更容易理解,你可以考慮使用適當的名字 (Ottinger's Rules里講解得相當好),確保正確的縮進,并且采用coding style guides,違背這個技巧可能的結果就像是注釋在為不好的代碼apologize。
13. 與同事分享技巧
雖然技巧10已經向我們表明了我們是如何從好的注釋中直接受益,這些技巧將讓所有開發者受益,特別是團隊中一起工作的同事。因此,為了編寫出更容易理解和維護的代碼,嘗試自由的和你的同事分享這些注釋技巧。
好東西拿出來一起分享
posted @
2008-11-20 09:38 Sandy 閱讀(1113) |
評論 (1) |
編輯 收藏
今天突然遇到Active Sync 4.5 setup.msi這個工具在windows vista系統上裝不上這個問題,查了一下,原來在windows vista上,不再采用Active Sync ,而是采用了drvupdate-x86.exe,具體名字叫做Microsoft Windows Mobile 設備中心6.1。微軟對其的描述是:
使用 Windows Mobile 設備中心,您可以建立新的合作關系,與 Windows Mobile 設備(Windows Mobile 2003 或更高版本)同步音樂、圖片和視頻,并對其進行管理。Windows Mobile 設備中心與高效的商務數據同步平臺緊密結合,提供了令人耳目一新的用戶體驗。Windows Mobile 設備中心可以幫助您快速建立新的合作關系,同步重要的商務信息(例如電子郵件、聯系人和日歷約會),輕松管理同步設置,以及在設備與 PC 間傳送商業文檔。
這個主要是用于vista系統的。下載地址是
http://www.microsoft.com/downloads/details.aspx?familyid=46F72DF1-E46A-4A5F-A791-09F07AAA1914&displaylang=zh-cn
也可以建議大家去搜一下,我是在新浪上下載的。
鏈接:http://down1.tech.sina.com.cn/download/down_contents/1183219200/35965.shtml
posted @
2008-11-19 19:37 Sandy 閱讀(1064) |
評論 (0) |
編輯 收藏
在csdn上閑逛,看到一個這樣的問題:如何在PPC上實現多語言的程序列表中的名稱在編譯時自動切換
這個曾經讓我相當苦惱,看到這個自然不會放過。
一位牛人很簡單的回答:
參考MUIHello 例子:
C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\app\app.sln
C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\German_resfile\german_resfile.sln
C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\Resfile\resfile.sln
Samples竟然有多語言的例子,讓我相當的驚訝!
學習了一下,比我自己的方法簡單很多,看來需要好好學習SDK中提供的例子。
這個例子會生成muihello.exe.0409.mui,這其實是一個DLL資源文件,有人說這是一種命名規則。在不同的平臺上就調用不同的資源,實現多語言。
在resfile.cpp文件中
#include "windows.h"


/**//////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)


{
return TRUE;
}
還有一個文件resfile.def
LIBRARY "muihello.exe.0409.mui"

EXPORTS

而且resource.h是共用一個的。
嘗試了一下,蠻好。
posted @
2008-11-18 22:22 Sandy 閱讀(1437) |
評論 (0) |
編輯 收藏
Burning by Maria Arredondo
Passion is sweet
Love makes weak
You said you cherished freedom so
You refused to let it go
Follow your fate
Love and hate
Never failed to seize the day
Don’t give yourself away
Oh when the night falls
And you’re all alone
In your deepest sleep
What are you dreaming of
My skin is still burning from your touch
Oh I just can’t get enough
I said I wouldn’t ask for much
But your eyes are dangerous
So the world keeps spinning in my head
Can we drop this masquerade
I can’t predict where it ends
If you’ re the rock I’ll crush against
Trapped in a crowd
Music is loud
I said I loved my freedom too
Now I’m not so sure I do
All eyes on you
Wings so true
Better quit while you're a head
Now I’m not so sure I am
Oh when the night falls
And you’re all alone
In your deepest sleep
What are you dreaming of
My skin is still burning from your touch
Oh I just can’t get enough
I said I wouldn’t ask for much
But your eyes are dangerous
So the world keeps spinning in my head
Can we drop this masquerade
I can’t predict where it ends
If you’ re the rock I’ll crush against
My soul, my heart
If you’re near or if you’re far
My life, my love
You can have it all
posted @
2008-11-12 09:18 Sandy 閱讀(150) |
評論 (0) |
編輯 收藏
看到他們的在爭論很有意思,我不是很懂。
有利還是有弊呢?
ASSERT在DEBUG程序時候幫了太多太多忙,不過在ASSERT判斷傳入參數后,還需要if再按相同條件判斷一遍,不符合規則return,這樣才是正確的邏輯。但這樣代碼難看,且工作重復無趣,又容易出現差漏。
剛弄了個簡單EXT_ASSERT宏,按我的理解應該可以解決問題,但不確定是否有漏洞,發出來大家一起瞄瞄。
#define RET_VOID
#define EX_ASSERT(exp, ret) {ASSERT(exp);if(!(exp))return(ret);}
BOOL CXXX::FunXXX(const data* p_data)
{
EXT_ASSERT(p_data, FALSE);//---- 返回BOOL型
}
int CXXX::FunXXX(const data* p_data)
{
EXT_ASSERT(p_data, -1);//---- 返回int型
}
const retdata* CXXX::FunXXX(const data* p_data)
{
EXT_ASSERT(p_data, NULL);//---- 返回NULL指針
}
retdata CXXX::FunXXX(const data* p_data)
{
EXT_ASSERT(p_data, retdata());//---- 返回空對象
}
void CXXX::FunXXX(const data* p_data)
{
EXT_ASSERT(p_data, RET_VOID);//---- 僅僅return
}
posted @
2008-11-04 18:28 Sandy 閱讀(394) |
評論 (0) |
編輯 收藏