這兒是個關於巨集的問題,我曾用過atl的串轉換巨集,包括w2a,開始有些東西我還不太明白。為了使用這些巨集,必須在函式的開始處用uses_conversion來初始化某些區域性變數。用就用吧,但是看看這個巨集的定義,它有類似下面的**:
// 在atlconv.h檔案中 #define uses_conversion \ int _convert; _convert; \ uint _acp = getacp(); _acp; \ lpcwstr _lpw; _lpw; \ lpcstr _lpa; _lpa為什麼它們用「int x;x;」——這種後面跟著變數的宣告? 很多人都碰到過這個令人困惑的問題,後來發現簡單的答案是:禁止編譯器的警告資訊(warning)。如果單獨有一行**:int x;且從來沒有使用過x,那麼編譯器匯報錯「unreferenced local variable:x」,意思是未引用過的區域性變數x,如果將警告資訊的輸出調到最大。為了避免討厭的警告,uses_conversion引用宣告的變數。
int x; // 宣告x; // 使用這個變數
在c++之前的時代,程式設計師有時在c中用函式形參做同樣的事情來避免「unreferenced formal parameter」或其它的深奧費解的編譯錯誤。
void myfunc(int x, char y)當然,現在用下面的**可以更有效地完成同樣的事情:
// 引數 x 不是用void myfunc(int /* x */)
也就是說宣告引數,但不給它起名,不能這樣使用區域性變數;必須顯式地引用它。這樣做不會增加任何指令到**中。最多可能多增加幾個位元組到堆疊(為x預留空間)。靈巧的編譯器甚至不會操心x從來沒有被使用過——雖然好奇心可能想知道:如果編譯器夠厲害,知道了從沒有使用x,為什麼要抱怨(編譯出錯)呢?答案是因為苛刻的程式設計師(且程式設計能力與個人的記性有關)使用編譯警告提醒自己刪除某部分**時發生變數荒廢。這種警告在c時代很有用,你必須在每個函式的頂部宣告變數,這就遠離了實用它們的**。現在來看看另乙個問題:為什麼在開始位置要用uses_conversion?即為什麼w2a&co之類的巨集還需要另外的巨集宣告自己的變數;為什麼不直接在w2a中宣告這個變數?
#define w2a(x) \int _convert; _convert; \
……etc很明顯這樣做不行,因為如果你使用w2a兩次,得到乙個複製的變數。那為什麼不把整個巨集放進花括弧建立新的範圍?
#define w2a(x) q這樣解決了命名衝突,但不能進行如下編碼:
dosomething(w2a(pwstr));沒有辦法從**塊返回值,所以不能在函式呼叫中傳遞w2a。真笨啊,那麼內聯函式怎麼樣?
inline lpcstr w2a(lpwstr w) 這解決了範圍問題——任何w2a需要的變數多可以在這個函式中,在自己的範圍內宣告,不需要另外的巨集。它也提供了一種返回值的方式,使你可以在函式呼叫和賦值中使用w2a(x)。但是這種方法不靈,也是因為w2a的緣故和其它的巨集更複雜。不管什麼時候進行unicode轉換,都不能就地轉換串,必須分配乙個臨時串容納被轉換的位元組。典型地,通過呼叫new分配乙個串:
int len = multibytetowidechar(...,mystr, null, 0); // 或的長度
lpwstr p = new wchar[nlen]; // 分配記憶體
multibytetowidechar(...,p,len); // 轉換
somecomfunction(p); // 使用之
delete p; // 銷毀這段**不僅令人討厭,而且還沒有效率;必須呼叫multibytetowidechar兩次(一次是計算長度,一次是實際的轉換),你得從堆中分配p,這樣很慢。通過分配2*len個位元組解決第乙個問題,這裡長度len是ascii串的長度——但第二個問題怎麼辦?如果看看a2w是如何展開的,請看:
// 簡化版#define a2w(s) \
_len = 2*strlen(s);
afxa2whelper((lpwstr)alloca(_len);afxa2whelper是乙個呼叫multibytetowidechar的輔助函式。a2w使用2*len巧妙地避免了兩次呼叫multibytetowidechar。但a2w及其它轉換巨集真正聰明的地方是不呼叫new操作分配臨時串,而是呼叫alloca——在棧中分配位元組,而不是在堆中。這樣做非常快,因為編譯器要做的只是增加棧指標。不呼叫函式,不處理記憶體塊。它也避免了記憶體碎片,並且也沒有必要呼叫delete操作,因為當控制離開alloca被呼叫的位址後,記憶體被自動釋放。這正好說明了為什麼a2w不能時內聯函式;如果是的話,alloca建立的臨時串會在返回前被摧毀,並且你會以刪除串的方式終止somecomfunction(使用這個例子)呼叫。a2w必須從alloca被呼叫的相同的位址處呼叫alloca——所以a2w必須是乙個巨集,不是乙個函式;因此它需要另乙個巨集users_conversion來宣告_len以及其它一些用到的變數(為了簡化,我省略了)。當你仔細想想,整個處理告訴我們要想寫一組類似a2w的巨集從棧中的分配記憶體會減少很多不必要的麻煩。另外,任何時候,只要你想要快速地獲取臨時記憶體,都可以呼叫alloca。下面的**是我們常常見到的:
char* p = new char[len];dosomething(p);
delete p;
使用下面的**替代之會效率更高:
char *p = (char*)alloca(len);dosomething(p);
// 不用呼叫delete p!當然,如果棧中的沒那麼多,你還這麼做的話,就會出現乙個討厭的訊息框。這個方法還有一些侷限,詳細內容請參考文件。經驗告訴我,不管什麼時候在mfc、atl或其它什麼地方發現奇怪的事情,最好是鑽進去研究一下,你可能會發現有用的東西。
快排中乙個巨集的bug
今天寫快速排序,碰到乙個問題 使用了下面的巨集 define swap a,b int temp a a b b temp 咋看沒有問題,但是在使用時a,不是乙個值而是乙個表示式 swap v m v i 這樣在編譯器預處理的時候會展開為 int temp v m v m v i v i temp ...
VC中乙個堆破壞的例子
最近除錯乙個 原本執行的不錯,在增加一些功能後突然出現了 堆破壞的錯誤。由於並沒有動態鏈結第三方的dll,所以問題無非是指標越界之類的,但是檢查了很久之後並沒有發現越界的指標,百思不得其解。今天只得回溯版本,一步一步新增 除錯。最後發現問題原來是乙個手誤 output queue t node2 o...
乙個關於巨集使用慘痛的教訓
rt,乙個憨批錯誤 我,作為乙個巨集使用狂熱愛好者,max 函式當然用巨集定義 define max a,b a b a b 於是,我在這道樹剖題中大量使用了此巨集,比如下面 ans max ans,query 1,idx topf a idx a if l mid ans max ans,quer...