• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            精妙SQL

            ★說明:復制表(只復制結構,源表名:a 新表名:b)

            SQL: select * into b from a where 1<>1 

            ★說明:拷貝表(拷貝數據,源表名:a 目標表名:b) 

            SQL: insert into b(a, b, c) select d,e,f from b; 

            ★說明:顯示文章、提交人和最后回復時間 

            SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b 

            ★說明:外連接查詢(表名1:a 表名2:b) 

            SQL: select a.f1, a.f2, a.f3, b.f3, b.f4, b.f5 from a left OUT JOIN b ON a.f1 = b.f3 (左連接)
            SQL: select a.f1, b.f2 from a FULL OUT JOIN b ON a.f1 = b.f1 (全連接)

            ★說明:日程安排提前五分鐘提醒 

            SQL: select * from 日程安排 where datediff('minute',f開始時間,getdate())>5 

            ★說明:兩張關聯表,刪除主表中已經在副表中沒有的信息 

            SQL: 

            delete from info where not exists ( select * from infobz where info.infid=infobz.infid )

            ★說明:四表聯查問題: 

            select f1, (select min(f)-1 from t where f>f1) as f2 from
            (select f 1 as f1 from t where f 1 not in (select f from t) and f <(select max(f) from t)) as cc

            ★說明:-- 

            SQL: 

            select A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE 
            from TABLE1, 
            (select X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE 
            from (select NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND 
            from TABLE2 
            where TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(SYSDATE, 'YYYY/MM')) X, 
            (select NUM, UPD_DATE, STOCK_ONHAND 
            from TABLE2 
            where TO_CHAR(UPD_DATE,'YYYY/MM') = 
            TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, 'YYYY/MM') || '/01','YYYY/MM/DD') - 1, 'YYYY/MM') ) Y, 
            where X.NUM = Y.NUM ( ) 
            and X.INBOUND_QTY   NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND ) B 
            where A.NUM = B.NUM 

            ★說明:-- 

            SQL: 

            select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名稱='"&strdepartmentname&"' and 專業名稱='"&strprofessionname&"' order by 性別,生源地,高考總成績 

            ★說明: 

            從數據庫中去一年的各單位電話費統計(電話費定額賀電化肥清單兩個表來源) 

            SQL: 

            select a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') as telyear, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '01', a.factration)) as JAN, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '02', a.factration)) as FRI, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '03', a.factration)) as MAR, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '04', a.factration)) as APR, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '05', a.factration)) as MAY, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '06', a.factration)) as JUE, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '07', a.factration)) as JUL, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '08', a.factration)) as AGU, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '09', a.factration)) as SEP, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '10', a.factration)) as OCT, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '11', a.factration)) as NOV, 
            sum(decode(TO_CHAR(a.telfeedate, 'mm'), '12', a.factration)) as DEC 
            from (select a.userper, a.tel, a.standfee, b.telfeedate, b.factration 
            from TELFEESTAND a, TELFEE b 
            where a.tel = b.telfax) a 
            group by a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy'

            1.把某個字段重新生起序列(從1到n):  

             

            set IDENTITY_INSERT Table1 ON
            declare @i int
            set @i = 0
            update Table1 set @i = @i   1,Field1 = @i
            set IDENTITY_INSERT Table1 off

            2.按成績排名次
            update 成績表
            set a.名次 = (
            select count(*)   1
            from 成績表 b
            where a.總成績 < b.總成績
            )
            from 成績表 a

            3.查詢外部數據庫
            select a.*
            from OpenRowSet('Microsoft.Jet.OLEDB.4.0','c:\test.mdb';'admin';'',Table1) a

            4.查詢Excel文件
            select *
            from OpenDataSource('Microsoft.Jet.OLEDB.4.0','Data Source="c:\test.xls";user id=Admin;Password=;Extended properties=Excel 8.0')...Sheet1$

            5.在查詢中指定排序規則
            select * from Table1 order by Field1 COLLATE Chinese_PRC_BIN
            為什么要指定排序規則呢?參見:
            http://www.zahui.com/html/8/15480.htm
            例,檢查數據庫中的Pub_Users表中是否存在指定的用戶:
            select count(*) from Pub_Users where [UserName]='admin' and [PassWord]='aaa' COLLATE Chinese_PRC_BIN
            默認比較是不區分大小寫的,如果不加COLLATE Chinese_PRC_BIN,那么密碼aaa與AAA是等效的,這當然與實際不符.注意的是,每個條件都要指定排序規則,上例中用戶名就不區分大小寫.

            6.order by的一個小技巧
            order by可以指定列序而不用指定列名,在下面的例子里說明它的用處(注意,第三列未指定別名)
            select a.id,a.Name,(select count(*) from TableB b where a.id=b.PID) from TableA a order by 3

            7.SQL簡單分頁的存儲過程    ◆常用◆◆常用◆◆常用◆◆常用◆◆常用◆
            /*
            create proc recordpages
            @nowpage int,
            @per int
            as
            declare @s nvarchar(255)
            if @nowpage<1 set @nowpage=1
            if @per<1 set @per=1
            set @s=N'declare @k int select top ' convert(varchar(10),(@nowpage-1)*@per) ' @k=id from table1
            select top ' convert(varchar(10),@per) ' * from table1 where id>@k'
            exec sp_executesql @s
            go
            */

            exec recordpages 3,10

            8:得到表中最小的未使用的id

            SQL: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where ..... 

            9.得到表中自動編號列精心策劃號的起始位置

            SQL: 
            select (case when exists(select * from Handle b where b.HandleID = 1) then MIN(HandleID) + 1 else 1 end) as HandleID 
            from Handle
            where NOT HandleID in (select a.HandleID - 1 from Handle a)

            posted @ 2008-01-13 00:08 李亞 閱讀(203) | 評論 (0)編輯 收藏

            PowerShell入門指南

            一,Windows PowerShell設計目標

                 一)Windows PowerShell是特別為系統管理員設計的、全新的Windows命令行shell。

                二)和大多數接受并返回文本的shell不同,Windows PowerShell建立在.NET公共語言運行時(CLR)和.NET框架之上,它接受并返回.NET對象。

                三)Windows PowerShell引入了cmdlet的概念,這是內建在shell中的一個簡單、單一功能的命令行工具。

                四)Windows PowerShell除了和大多數shell一樣提供了對文件系統的訪問外,還提供了對注冊表、數字簽名證書存儲等其他數據存儲的訪問

            二,Windows PowerShell簡介

                一)Windows PowerShell不處理文本,它處理基于.NET平臺的對象。

                二)Windows PowerShell提供了一大套具有一致接口的內建命令

                三)所有的shell命令使用同樣的命令parser。

                四)可以同時使用傳統的Windows工具

            三,Windows PowerShell Cmdlets

                一)通過命名格式來識別cmdlets:動賓結構——動詞+分隔符“-”+名詞

                二)如何獲得cmdlets的相關幫助:get-help <cmdlet-name> -detailed;該命令顯示以下內容:cmdlet描述,命令語法,參數描述,cmdlet用法舉例

            四,為何需要一種新的腳本語言

                一)Windows PowerShell需要一種語言來管理.NET對象

                二)該語言需要為使用cmdlet提供一致的環境

                三)該語言需要支持復雜任務,而不是使簡單任務復雜化

                四)該語言需要和用于.NET編程的高級語言——如C#——一致。

            五,處理對象

                一)當你在Windows PowerShell中工作時,你在和.NET對象打交道

                二)get-service | get-member:把get-service命令中獲取的對象發送給get-member命令,get-member顯示service對象的相關信息,如對象的TypeName以及對象的屬性和方法列表

                三)有關對象的類的信息,復制并粘貼TypeName到MSDN

                四)要查找特定對象——如schedule——的所有屬性值:get-service schedule | format-list -properti *

            六,對象管道

                一)Windows PowerShell提供了一個新的、基于對象的接口模型——而不是基于文本。例如:ipconfig | findstr "IP Address"

            七,對腳本的支持

                一)Windows PowerShell完全支持腳本,完全支持腳本。

                二)Windows PowerShell腳本后綴為ps1,該后綴是可選項

                三)必須指明腳本文件所在的完整路徑,即使腳本位于當前目錄

                四)Windows PowerShell的安全策略——稱為執行策略(execution policy)——讓你決定腳本是否可以運行、是否必須包含一個數字簽名

                五)Windows PowerShell中沒有任何執行策略允許通過雙擊圖標來運行腳本,更多信息:get-help about_signing
            八,Windows PowerShell常用命令舉例

                1. get-help/help/man

                    get-help get-command

                    get-help about_signing

                    get-help get-command -detailed

                    get-help get-command -full

                    get-help get-command -examples

                    get-help get-command -parameter totalcount

                    get-help get-command -parameter *

                    man get-command

                    help get-command

                    get-help get-*

                    get-help about_wildcard

                    get-help about_*

                    get-help -name get-alias

                    get-help get-alias

                    get-help about_commonparameters

                2. get-command

                    get-command *.exe

                3. get-process

                4. get-service

                    get-service | get-member

                    get-service | get-member -membertype *property

                    (get-service alerter).canpauseandcontinue

                    get-service alerter | format-list -property name, canpauseandcontinue

                    get-service alerter | format-list -property *

                    get-service | format-table -property name, canpauseandcontinue

                    (get-service schedule).stop()

                   

                5. get-eventlog

                6. get-date

                7. get-alias

                    get-alias | where-object {$_.definition -eq "set-location"}

                8. get-wmiobject:絕對有用,因為它讓你察看和更改遠程計算機的組件

                    get-wmiobject win32_bios -computername server01

                9. get-member

                    get-service | get-member

                10. format-table

                11. format-list

                12. format-wide

                13. format-custom

                14. set-location

                    set-location alias:

                15. get-childitem

                    get-childitem alias:

                16. set-alias

                    set-alias gh get-help

                    set-alias np c:\windows\notepad.exe

                17. remove-item

                    remove-item alias:np

                18. function
                   
                    function bi {notepad c:\boot.ini}

                19. get-psdrive

                20. new-psdrive

                21. test-path

            posted @ 2008-01-12 23:40 李亞 閱讀(818) | 評論 (0)編輯 收藏

            PowerShell簡介

            即開發代號為Monad的命令行外殼和腳本系統管理工具。

            PowerShell是微軟公司于2006年第四季度正式發布的. 它的出現標志著, 微軟公司向服務器領域邁出了重要的一步, 拉近了與Unix, Linux等操作系統的距離. PowerShell的前身命名為Monad, 在2006年4月25日正式發布beta版時更名為PowerShell.

            PowerShell是一款基于對象的shell, 建立在.Net框架之上, 目前支持.Net Framework 2.0. 能夠運行在Windows XP SP2, Windows Vista, Windows 2003操作系統上. 能夠同時支持WMI, COM, ADO.NET, ADSI等已有的Windows管理模型.

            根據微軟公司的計劃, 2009年將會實現所有微軟公司的GUI管理工具通過PowerShell作為中間層對服務程序進行管理, 現階段例如Exchange 2007等已經支持PowerShell的管理. 可以預期, 使用PowerShell管理Windows服務器指日可待.

            posted @ 2008-01-12 23:37 李亞 閱讀(300) | 評論 (0)編輯 收藏

            [轉]常用數據類型的使用與轉換

            我們先定義一些常見類型變量借以說明

            int i = 100;
            long l = 2001;
            float f=300.2;
            double d=12345.119;
            char username[]="程佩君";
            char temp[200];
            char *buf;
            CString str;
            _variant_t v1;
            _bstr_t v2;

            一、其它數據類型轉換為字符串

            • 短整型(int)
              itoa(i,temp,10);///將i轉換為字符串放入temp中,最后一個數字表示十進制
              itoa(i,temp,2); ///按二進制方式轉換
            • 長整型(long)
              ltoa(l,temp,10);
            • 浮點數(float,double)
              用fcvt可以完成轉換,這是MSDN中的例子:
              int decimal, sign;
              char *buffer;
              double source = 3.1415926535;
              buffer = _fcvt( source, 7, &decimal, &sign );
              運行結果:source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0
              decimal表示小數點的位置,sign表示符號:0為正數,1為負數
            • CString變量
              str = "2008北京奧運";
              buf = (LPSTR)(LPCTSTR)str;
            • BSTR變量
              BSTR bstrValue = ::SysAllocString(L"程序員");
              char * buf = _com_util::ConvertBSTRToString(bstrValue);
              SysFreeString(bstrValue);
              AfxMessageBox(buf);
              delete(buf);
            • CComBSTR變量
              CComBSTR bstrVar("test");
              char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
              AfxMessageBox(buf);
              delete(buf);
            • _bstr_t變量
              _bstr_t類型是對BSTR的封裝,因為已經重載了=操作符,所以很容易使用
              _bstr_t bstrVar("test");
              const char *buf = bstrVar;///不要修改buf中的內容
              AfxMessageBox(buf);

            • 通用方法(針對非COM數據類型)
              用sprintf完成轉換
              char  buffer[200];char  c = '1';int   i = 35;long  j = 1000;float f = 1.7320534f;sprintf( buffer, "%c",c);sprintf( buffer, "%d",i);sprintf( buffer, "%d",j);sprintf( buffer, "%f",f);

            二、字符串轉換為其它數據類型
            strcpy(temp,"123");

            • 短整型(int)
              i = atoi(temp);
            • 長整型(long)
              l = atol(temp);
            • 浮點(double)
              d = atof(temp);
            • CString變量
              CString name = temp;
            • BSTR變量
              BSTR bstrValue = ::SysAllocString(L"程序員");
              ...///完成對bstrValue的使用
              SysFreeString(bstrValue);
            • CComBSTR變量
              CComBSTR類型變量可以直接賦值
              CComBSTR bstrVar1("test");
              CComBSTR bstrVar2(temp);
            • _bstr_t變量
              _bstr_t類型的變量可以直接賦值
              _bstr_t bstrVar1("test");
              _bstr_t bstrVar2(temp);

            三、其它數據類型轉換到CString
            使用CString的成員函數Format來轉換,例如:

            • 整數(int)
              str.Format("%d",i);
            • 浮點數(float)
              str.Format("%f",i);
            • 字符串指針(char *)等已經被CString構造函數支持的數據類型可以直接賦值
              str = username;
            • 對于Format所不支持的數據類型,可以通過上面所說的關于其它數據類型轉化到char *的方法先轉到char *,然后賦值給CString變量。

            四、BSTR、_bstr_t與CComBSTR

            • CComBSTR 是ATL對BSTR的封裝,_bstr_t是C++對BSTR的封裝,BSTR是32位指針,但并不直接指向字串的緩沖區。
              char *轉換到BSTR可以這樣:
              BSTR b=_com_util::ConvertStringToBSTR("數據");///使用前需要加上comutil.h和comsupp.lib
              SysFreeString(bstrValue);
              反之可以使用
              char *p=_com_util::ConvertBSTRToString(b);
              delete p;
              具體可以參考一,二段落里的具體說明。

              CComBSTR與_bstr_t對大量的操作符進行了重載,可以直接進行=,!=,==等操作,所以使用非常方便。
              特別是_bstr_t,建議大家使用它。

            五、VARIANT 、_variant_t 與 COleVariant

            • VARIANT的結構可以參考頭文件VC98\Include\OAIDL.H中關于結構體tagVARIANT的定義。
              對于VARIANT變量的賦值:首先給vt成員賦值,指明數據類型,再對聯合結構中相同數據類型的變量賦值,舉個例子:
              VARIANT va;
              int a=2001;
              va.vt=VT_I4;///指明整型數據
              va.lVal=a; ///賦值

              對于不馬上賦值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);進行初始化,其本質是將vt設置為VT_EMPTY,下表我們列舉vt與常用數據的對應關系:

              Byte bVal; // VT_UI1.
              Short iVal; // VT_I2.
              long lVal; // VT_I4.
              float fltVal; // VT_R4.
              double dblVal; // VT_R8.
              VARIANT_BOOL boolVal; // VT_BOOL.
              SCODE scode; // VT_ERROR.
              CY cyVal; // VT_CY.
              DATE date; // VT_DATE.
              BSTR bstrVal; // VT_BSTR.
              DECIMAL FAR* pdecVal // VT_BYREF|VT_DECIMAL.
              IUnknown FAR* punkVal; // VT_UNKNOWN.
              IDispatch FAR* pdispVal; // VT_DISPATCH.
              SAFEARRAY FAR* parray; // VT_ARRAY|*.
              Byte FAR* pbVal; // VT_BYREF|VT_UI1.
              short FAR* piVal; // VT_BYREF|VT_I2.
              long FAR* plVal; // VT_BYREF|VT_I4.
              float FAR* pfltVal; // VT_BYREF|VT_R4.
              double FAR* pdblVal; // VT_BYREF|VT_R8.
              VARIANT_BOOL FAR* pboolVal; // VT_BYREF|VT_BOOL.
              SCODE FAR* pscode; // VT_BYREF|VT_ERROR.
              CY FAR* pcyVal; // VT_BYREF|VT_CY.
              DATE FAR* pdate; // VT_BYREF|VT_DATE.
              BSTR FAR* pbstrVal; // VT_BYREF|VT_BSTR.
              IUnknown FAR* FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN.
              IDispatch FAR* FAR* ppdispVal; // VT_BYREF|VT_DISPATCH.
              SAFEARRAY FAR* FAR* pparray; // VT_ARRAY|*.
              VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
              void FAR* byref; // Generic ByRef.
              char cVal; // VT_I1.
              unsigned short uiVal; // VT_UI2.
              unsigned long ulVal; // VT_UI4.
              int intVal; // VT_INT.
              unsigned int uintVal; // VT_UINT.
              char FAR * pcVal; // VT_BYREF|VT_I1.
              unsigned short FAR * puiVal; // VT_BYREF|VT_UI2.
              unsigned long FAR * pulVal; // VT_BYREF|VT_UI4.
              int FAR * pintVal; // VT_BYREF|VT_INT.
              unsigned int FAR * puintVal; //VT_BYREF|VT_UINT.

            • _variant_t是VARIANT的封裝類,其賦值可以使用強制類型轉換,其構造函數會自動處理這些數據類型。
              使用時需加上#include <comdef.h>
              例如:
              long l=222;
              ing i=100;
              _variant_t lVal(l);
              lVal = (long)i;

            • COleVariant的使用與_variant_t的方法基本一樣,請參考如下例子:
              COleVariant v3 = "字符串", v4 = (long)1999;
              CString str =(BSTR)v3.pbstrVal;
              long i = v4.lVal;

            六、其它一些COM數據類型

            • 根據ProgID得到CLSID
              HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
              CLSID clsid;
              CLSIDFromProgID( L"MAPI.Folder",&clsid);
            • 根據CLSID得到ProgID
              WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
              例如我們已經定義了 CLSID_IApplication,下面的代碼得到ProgID
              LPOLESTR pProgID = 0;
              ProgIDFromCLSID( CLSID_IApplication,&pProgID);
              ...///可以使用pProgID
              CoTaskMemFree(pProgID);//不要忘記釋放

            七、ANSI與Unicode
            Unicode稱為寬字符型字串,COM里使用的都是Unicode字符串。

            • 將ANSI轉換到Unicode
              (1)通過L這個宏來實現,例如: CLSIDFromProgID( L"MAPI.Folder",&clsid);
              (2)通過MultiByteToWideChar函數實現轉換,例如:
              char *szProgID = "MAPI.Folder";
              WCHAR szWideProgID[128];
              CLSID clsid;
              long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
              szWideProgID[lLen] = '\0';
              (3)通過A2W宏來實現,例如:
              USES_CONVERSION;
              CLSIDFromProgID( A2W(szProgID),&clsid);
            • 將Unicode轉換到ANSI
              (1)使用WideCharToMultiByte,例如:
              // 假設已經有了一個Unicode 串 wszSomeString...
              char szANSIString [MAX_PATH];
              WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
              (2)使用W2A宏來實現,例如:
              USES_CONVERSION;
              pTemp=W2A(wszSomeString);

            八、其它

            • 對消息的處理中我們經常需要將WPARAM或LPARAM等32位數據(DWORD)分解成兩個16位數據(WORD),例如:
              LPARAM lParam;
              WORD loValue = LOWORD(lParam);///取低16位
              WORD hiValue = HIWORD(lParam);///取高16位

            • 對于16位的數據(WORD)我們可以用同樣的方法分解成高低兩個8位數據(BYTE),例如:
              WORD wValue;
              BYTE loValue = LOBYTE(wValue);///取低8位
              BYTE hiValue = HIBYTE(wValue);///取高8位

            • 兩個16位數據(WORD)合成32位數據(DWORD,LRESULT,LPARAM,或WPARAM)
              LONG MAKELONG( WORD wLow, WORD wHigh );
              WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
              LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
              LRESULT MAKELRESULT( WORD wLow, WORD wHigh );

            • 兩個8位的數據(BYTE)合成16位的數據(WORD)
              WORD MAKEWORD( BYTE bLow, BYTE bHigh );

            • 從R(red),G(green),B(blue)三色得到COLORREF類型的顏色值
              COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );
              例如COLORREF bkcolor = RGB(0x22,0x98,0x34);


            • 從COLORREF類型的顏色值得到RGB三個顏色值
              BYTE Red = GetRValue(bkcolor); ///得到紅顏色
              BYTE Green = GetGValue(bkcolor); ///得到綠顏色
              BYTE Blue = GetBValue(bkcolor); ///得到蘭顏色

            九、注意事項
            假如需要使用到ConvertBSTRToString此類函數,需要加上頭文件comutil.h,并在setting中加入comsupp.lib或者直接加上#pragma comment( lib, "comsupp.lib" )

            posted @ 2008-01-12 23:07 李亞 閱讀(214) | 評論 (0)編輯 收藏

            CString與其他類型的轉換----CString字符串轉換續

            CString與其他類型的轉換
            這里只是介紹了很常見方法,還有其他方法也可以實現相關的功能!
            1、字符串與數的轉換:
            atof(字符串->double,int,long),itoa(int->字符串),ltoa(long int->字符串)
            double->CString的方法:CString::Format("%d", &dX);
             
            2、CString to char*
            //經過類型強制轉換,可以將CString類型轉換成char*,例如:
            CString cStr = "Hello,world!";
            char *zStr = (char*)(LPCTSTR)cStr;
            當然,這只是最快捷的一種方法。因為CString自帶一個函數可以將CString對象轉換成const char*,也就是LPCTSTR。
             
            3、char* to CString
            //char*類型可以直接給CString,完成自動轉換,例如:
            char *zStr = "Hello,world!";
            CString cStr = zStr;
             
            4、CString to LPCSTR 
            //將CString轉換成LPCSTR,需要獲得CString的長度CString cStr=_T("Hello,world!");
            int nLen = cStr.GetLength();
            LPCSTR lpszBuf = cStr.GetBuffer(nLen);
             
            5、CString to LPSTR
            //這個和第4個技巧是一樣的,例如:
            CString cStr = _T("Hello,world!");
            int nLen = str.GetLength();
            LPSTR lpszBuf = str.GetBuffer(nLen);  

            posted @ 2008-01-12 23:00 李亞 閱讀(3793) | 評論 (0)編輯 收藏

            [轉]CString 操作指南

            CString 是一種很有用的數據類型。它們很大程度上簡化了MFC中的許多操作,使得MFC在做字符串操作的時候方便了很多。不管怎樣,使用CString有很多特殊的技巧,特別是對于純C背景下走出來的程序員來說有點難以學習。這篇文章就來討論這些技巧。
              使用CString可以讓你對字符串的操作更加直截了當。這篇文章不是CString的完全手冊,但囊括了大部分常見基本問題。

            這篇文章包括以下內容:
            CString 對象的連接
            格式化字符串(包括 int 型轉化為 CString )
            CString 型轉化成 int 型
            CString 型和 char* 類型的相互轉化
              1.char* 轉化成 CString
              2.CString 轉化成 char* 之一:使用LPCTSTR強制轉化
              3.CString 轉化成 char* 之二:使用CString對象的GetBuffer方法
              4.CString 轉化成 char* 之三: 和控件的接口
              5.CString 型轉化成 BSTR 型;

            BSTR 型轉化成 CString 型;
            VARIANT 型轉化成 CString 型;
            載入字符串表資源;
            CString 和臨時對象;

            CString 的效率;
            總結
            下面我分別討論。

             1、CString 對象的連接

              能體現出 CString 類型方便性特點的一個方面就字符串的連接,使用 CString 類型,你能很方便地連接兩個字符串,正如下面的例子:

            CString gray("Gray");CString cat("Cat");CString graycat = gray + cat;
            要比用下面的方法好得多:

            char gray[] = "Gray";char cat[] = "Cat";char * graycat = malloc(strlen(gray) + strlen(cat) + 1);strcpy(graycat, gray);strcat(graycat, cat);
             2、格式化字符串

              與其用 sprintf() 函數或 wsprintf() 函數來格式化一個字符串,還不如用 CString 對象的Format()方法:

            CString s;s.Format(_T("The total is %d"), total);
              用這種方法的好處是你不用擔心用來存放格式化后數據的緩沖區是否足夠大,這些工作由CString類替你完成。
              格式化是一種把其它不是字符串類型的數據轉化為CString類型的最常用技巧,比如,把一個整數轉化成CString類型,可用如下方法:

            CString s;s.Format(_T("%d"), total);
              我總是對我的字符串使用_T()宏,這是為了讓我的代碼至少有Unicode的意識,當然,關于Unicode的話題不在這篇文章的討論范圍。_T()宏在8位字符環境下是如下定義的:

            #define _T(x) x // 非Unicode版本(non-Unicode version)
            而在Unicode環境下是如下定義的:

            #define _T(x) L##x // Unicode版本(Unicode version)
            所以在Unicode環境下,它的效果就相當于:

            s.Format(L"%d", total);
              如果你認為你的程序可能在Unicode的環境下運行,那么開始在意用 Unicode 編碼。比如說,不要用 sizeof() 操作符來獲得字符串的長度,因為在Unicode環境下就會有2倍的誤差。我們可以用一些方法來隱藏Unicode的一些細節,比如在我需要獲得字符長度的時候,我會用一個叫做DIM的宏,這個宏是在我的dim.h文件中定義的,我會在我寫的所有程序中都包含這個文件:

            #define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )
              這個宏不僅可以用來解決Unicode的字符串長度的問題,也可以用在編譯時定義的表格上,它可以獲得表格的項數,如下:

            class Whatever { ... };Whatever data[] = {   { ... },    ...   { ... },};for(int i = 0; i < DIM(data); i++) // 掃描表格尋找匹配項。
              這里要提醒你的就是一定要注意那些在參數中需要真實字節數的API函數調用,如果你傳遞字符個數給它,它將不能正常工作。如下:
            TCHAR data[20];lstrcpyn(data, longstring, sizeof(data) - 1); // WRONG!lstrcpyn(data, longstring, DIM(data) - 1); // RIGHTWriteFile(f, data, DIM(data), &bytesWritten, NULL); // WRONG!WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RIGHT
            造成以上原因是因為lstrcpyn需要一個字符個數作為參數,但是WriteFile卻需要字節數作為參數。
            同樣需要注意的是有時候需要寫出數據的所有內容。如果你僅僅只想寫出數據的真實長度,你可能會認為你應該這樣做:

            WriteFile(f, data, lstrlen(data), &bytesWritten, NULL); // WRONG
            但是在Unicode環境下,它不會正常工作。正確的做法應該是這樣:

            WriteFile(f, data, lstrlen(data) * sizeof(TCHAR), &bytesWritten, NULL); // RIGHT
              因為WriteFile需要的是一個以字節為單位的長度。(可能有些人會想“在非Unicode的環境下運行這行代碼,就意味著總是在做一個多余的乘1操作,這樣不會降低程序的效率嗎?”這種想法是多余的,你必須要了解編譯器實際上做了什么,沒有哪一個C或C++編譯器會把這種無聊的乘1操作留在代碼中。在Unicode環境下運行的時候,你也不必擔心那個乘2操作會降低程序的效率,記住,這只是一個左移一位的操作而已,編譯器也很樂意為你做這種替換。)
              使用_T宏并不是意味著你已經創建了一個Unicode的程序,你只是創建了一個有Unicode意識的程序而已。如果你在默認的8-bit模式下編譯你的程序的話,得到的將是一個普通的8-bit的應用程序(這里的8-bit指的只是8位的字符編碼,并不是指8位的計算機系統);當你在Unicode環境下編譯你的程序時,你才會得到一個Unicode的程序。記住,CString 在 Unicode 環境下,里面包含的可都是16位的字符哦。

             3、CString 型轉化成 int 型

              把 CString 類型的數據轉化成整數類型最簡單的方法就是使用標準的字符串到整數轉換例程。
              雖然通常你懷疑使用_atoi()函數是一個好的選擇,它也很少會是一個正確的選擇。如果你準備使用 Unicode 字符,你應該用_ttoi(),它在 ANSI 編碼系統中被編譯成_atoi(),而在 Unicode 編碼系統中編譯成_wtoi()。你也可以考慮使用_tcstoul()或者_tcstol(),它們都能把字符串轉化成任意進制的長整數(如二進制、八進制、十進制或十六進制),不同點在于前者轉化后的數據是無符號的(unsigned),而后者相反。看下面的例子:

            CString hex = _T("FAB");CString decimal = _T("4011");ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));
             4、CString 型和 char* 類型的相互轉化

              這是初學者使用 CString 時最常見的問題。有了 C++ 的幫助,很多問題你不需要深入的去考慮它,直接拿來用就行了,但是如果你不能深入了解它的運行機制,又會有很多問題讓你迷惑,特別是有些看起來沒有問題的代碼,卻偏偏不能正常工作。
            比如,你會奇怪為什么不能寫向下面這樣的代碼呢:

            CString graycat = "Gray" + "Cat";
            或者這樣:

            CString graycat("Gray" + "Cat");
              事實上,編譯器將抱怨上面的這些嘗試。為什么呢?因為針對CString 和 LPCTSTR數據類型的各種各樣的組合,“ +” 運算符 被定義成一個重載操作符。而不是兩個 LPCTSTR 數據類型,它是底層數據類型。你不能對基本數據(如 int、char 或者 char*)類型重載 C++ 的運算符。你可以象下面這樣做:

            CString graycat = CString("Gray") + CString("Cat");
            或者這樣:

            CString graycat = CString("Gray") + "Cat";
            研究一番就會發現:“ +”總是使用在至少有一個 CString 對象和一個 LPCSTR 的場合。

            注意,編寫有 Unicode 意識的代碼總是一件好事,比如:

            CString graycat = CString(_T("Gray")) + _T("Cat");
            這將使得你的代碼可以直接移植。

            char* 轉化為 CString

              現在你有一個 char* 類型的數據,或者說一個字符串。怎么樣創建 CString 對象呢?這里有一些例子:

            char * p = "This is a test";
            或者象下面這樣更具有 Unicode 意識:

            TCHAR * p = _T("This is a test")

            LPTSTR p = _T("This is a test");
            你可以使用下面任意一種寫法:

            CString s = "This is a test"; // 8-bit onlyCString s = _T("This is a test"); // Unicode-awareCString s("This is a test"); // 8-bit onlyCString s(_T("This is a test")); // Unicode-awareCString s = p;CString s(p);
              用這些方法可以輕松將常量字符串或指針轉換成 CString。需要注意的是,字符的賦值總是被拷貝到 CString 對象中去的,所以你可以象下面這樣操作:

            TCHAR * p = _T("Gray");CString s(p);p = _T("Cat");s += p;
            結果字符串肯定是“GrayCat”。

            CString 類還有幾個其它的構造函數,但是這里我們不考慮它,如果你有興趣可以自己查看相關文檔。

            事實上,CString 類的構造函數比我展示的要復雜,比如:

            CString s = "This is a test";
              這是很草率的編碼,但是實際上它在 Unicode 環境下能編譯通過。它在運行時調用構造函數的 MultiByteToWideChar 操作將 8 位字符串轉換成 16 位字符串。不管怎樣,如果 char * 指針是網絡上傳輸的 8 位數據,這種轉換是很有用的。

            CString 轉化成 char* 之一:強制類型轉換為 LPCTSTR;

              這是一種略微硬性的轉換,有關“正確”的做法,人們在認識上還存在許多混亂,正確的使用方法有很多,但錯誤的使用方法可能與正確的使用方法一樣多。
              我們首先要了解 CString 是一種很特殊的 C++ 對象,它里面包含了三個值:一個指向某個數據緩沖區的指針、一個是該緩沖中有效的字符記數以及一個緩沖區長度。 有效字符數的大小可以是從0到該緩沖最大長度值減1之間的任何數(因為字符串結尾有一個NULL字符)。字符記數和緩沖區長度被巧妙隱藏。
              除非你做一些特殊的操作,否則你不可能知道給CString對象分配的緩沖區的長度。這樣,即使你獲得了該0緩沖的地址,你也無法更改其中的內容,不能截短字符串,也 絕對沒有辦法加長它的內容,否則第一時間就會看到溢出。
              LPCTSTR 操作符(或者更明確地說就是 TCHAR * 操作符)在 CString 類中被重載了,該操作符的定義是返回緩沖區的地址,因此,如果你需要一個指向 CString 的 字符串指針的話,可以這樣做:


            CString s("GrayCat");LPCTSTR p = s;
              它可以正確地運行。這是由C語言的強制類型轉化規則實現的。當需要強制類型轉化時,C++規測容許這種選擇。比如,你可以將(浮點數)定義為將某個復數 (有一對浮點數)進行強制類型轉換后只返回該復數的第一個浮點數(也就是其實部)。可以象下面這樣:

            Complex c(1.2f, 4.8f);float realpart = c;
            如果(float)操作符定義正確的話,那么實部的的值應該是1.2。
              這種強制轉化適合所有這種情況,例如,任何帶有 LPCTSTR 類型參數的函數都會強制執行這種轉換。 于是,你可能有這樣一個函數(也許在某個你買來的DLL中):

            BOOL DoSomethingCool(LPCTSTR s);
            你象下面這樣調用它:

            CString file("c:\\myfiles\\coolstuff")BOOL result = DoSomethingCool(file);
              它能正確運行。因為 DoSomethingCool 函數已經說明了需要一個 LPCTSTR 類型的參數,因此 LPCTSTR 被應用于該參數,在 MFC 中就是返回的串地址。

            如果你要格式化字符串怎么辦呢?

            CString graycat("GrayCat");CString s;s.Format("Mew! I love %s", graycat);
              注意由于在可變參數列表中的值(在函數說明中是以“...”表示的)并沒有隱含一個強制類型轉換操作符。你會得到什么結果呢?
              一個令人驚訝的結果,我們得到的實際結果串是:

            "Mew! I love GrayCat"。
              因為 MFC 的設計者們在設計 CString 數據類型時非常小心, CString 類型表達式求值后指向了字符串,所以這里看不到任何象 Format 或 sprintf 中的強制類型轉換,你仍然可以得到正確的行為。描述 CString 的附加數據實際上在 CString 名義地址之后。
              有一件事情你是不能做的,那就是修改字符串。比如,你可能會嘗試用“,”代替“.”(不要做這樣的,如果你在乎國際化問題,你應該使用十進制轉換的 National Language Support 特性,),下面是個簡單的例子:

            CString v("1.00"); // 貨幣金額,兩位小數LPCTSTR p = v;p[lstrlen(p) - 3] = '','';
              這時編譯器會報錯,因為你賦值了一個常量串。如果你做如下嘗試,編譯器也會錯:

            strcat(p, "each");
              因為 strcat 的第一個參數應該是 LPTSTR 類型的數據,而你卻給了一個 LPCTSTR。

              不要試圖鉆這個錯誤消息的牛角尖,這只會使你自己陷入麻煩!

              原因是緩沖有一個計數,它是不可存取的(它位于 CString 地址之下的一個隱藏區域),如果你改變這個串,緩沖中的字符計數不會反映所做的修改。此外,如果字符串長度恰好是該字符串物理限制的長度(梢后還會講到這個問題),那么擴展該字符串將改寫緩沖以外的任何數據,那是你無權進行寫操作的內存(不對嗎?),你會毀換壞不屬于你的內存。這是應用程序真正的死亡處方。

            CString轉化成char* 之二:使用 CString 對象的 GetBuffer 方法;

              如果你需要修改 CString 中的內容,它有一個特殊的方法可以使用,那就是 GetBuffer,它的作用是返回一個可寫的緩沖指針。 如果你只是打算修改字符或者截短字符串,你完全可以這樣做:

            CString s(_T("File.ext"));LPTSTR p = s.GetBuffer();LPTSTR dot = strchr(p, ''.''); // OK, should have used s.Find...if(p != NULL)*p = _T(''\0'');s.ReleaseBuffer();
              這是 GetBuffer 的第一種用法,也是最簡單的一種,不用給它傳遞參數,它使用默認值 0,意思是:“給我這個字符串的指針,我保證不加長它”。當你調用 ReleaseBuffer 時,字符串的實際長度會被重新計算,然后存入 CString 對象中。
              必須強調一點,在 GetBuffer 和 ReleaseBuffer 之間這個范圍,一定不能使用你要操作的這個緩沖的 CString 對象的任何方法。因為 ReleaseBuffer 被調用之前,該 CString 對象的完整性得不到保障。研究以下代碼:

            CString s(...);LPTSTR p = s.GetBuffer();//... 這個指針 p 發生了很多事情int n = s.GetLength(); // 很糟D!!!!! 有可能給出錯誤的答案!!!s.TrimRight(); // 很糟!!!!! 不能保證能正常工作!!!!s.ReleaseBuffer(); // 現在應該 OKint m = s.GetLength(); // 這個結果可以保證是正確的。s.TrimRight(); // 將正常工作。
              假設你想增加字符串的長度,你首先要知道這個字符串可能會有多長,好比是聲明字符串數組的時候用:

            char buffer[1024];
            表示 1024 個字符空間足以讓你做任何想做得事情。在 CString 中與之意義相等的表示法:

            LPTSTR p = s.GetBuffer(1024);
              調用這個函數后,你不僅獲得了字符串緩沖區的指針,而且同時還獲得了長度至少為 1024 個字符的空間(注意,我說的是“字符”,而不是“字節”,因為 CString 是以隱含方式感知 Unicode 的)。
              同時,還應該注意的是,如果你有一個常量串指針,這個串本身的值被存儲在只讀內存中,如果試圖存儲它,即使你已經調用了 GetBuffer ,并獲得一個只讀內存的指針,存入操作會失敗,并報告存取錯誤。我沒有在 CString 上證明這一點,但我看到過大把的 C 程序員經常犯這個錯誤。
              C 程序員有一個通病是分配一個固定長度的緩沖,對它進行 sprintf 操作,然后將它賦值給一個 CString:

            char buffer[256];sprintf(buffer, "%......", args, ...); // ... 部分省略許多細節CString s = buffer;
            雖然更好的形式可以這么做:

            CString s;s.Format(_T("%...."), args, ...);
            如果你的字符串長度萬一超過 256 個字符的時候,不會破壞堆棧。

              另外一個常見的錯誤是:既然固定大小的內存不工作,那么就采用動態分配字節,這種做法弊端更大:

            int len = lstrlen(parm1) + 13  lstrlen(parm2) + 10 + 100;char * buffer = new char[len];sprintf(buffer, "%s is equal to %s, valid data", parm1, parm2);CString s = buffer;......delete [] buffer;
            它可以能被簡單地寫成:

            CString s;s.Format(_T("%s is equal to %s, valid data"), parm1, parm2);
              需要注意 sprintf 例子都不是 Unicode 就緒的,盡管你可以使用 tsprintf 以及用 _T() 來包圍格式化字符串,但是基本 思路仍然是在走彎路,這這樣很容易出錯。

            CString to char * 之三:和控件的接口;

              我們經常需要把一個 CString 的值傳遞給一個控件,比如,CTreeCtrl。MFC為我們提供了很多便利來重載這個操作,但是 在大多數情況下,你使用“原始”形式的更新,因此需要將墨某個串指針存儲到 TVINSERTITEMSTRUCT 結構的 TVITEM 成員中。如下:

            TVINSERTITEMSTRUCT tvi;CString s;// ... 為s賦一些值。tvi.item.pszText = s; // Compiler yells at you here// ... 填寫tvi的其他域HTREEITEM ti = c_MyTree.InsertItem(&tvi);
              為什么編譯器會報錯呢?明明看起來很完美的用法啊!但是事實上如果你看看 TVITEM 結構的定義你就會明白,在 TVITEM 結構中 pszText 成員的聲明如下:

            LPTSTR pszText;int cchTextMax;
              因此,賦值不是賦給一個 LPCTSTR 類型的變量,而且編譯器無法知道如何將賦值語句右邊強制轉換成 LPCTSTR。好吧,你說,那我就改成這樣:

            tvi.item.pszText = (LPCTSTR)s; //編譯器依然會報錯。
              編譯器之所以依然報錯是因為你試圖把一個 LPCTSTR 類型的變量賦值給一個 LPTSTR 類型的變量,這種操作在C或C++中是被禁止的。你不能用這種方法 來濫用常量指針與非常量指針概念,否則,會擾亂編譯器的優化機制,使之不知如何優化你的程序。比如,如果你這么做:

            const int i = ...;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff... = a[i]; // usage 2
              那么,編譯器會以為既然 i 是 const ,所以 usage1和usage2的值是相同的,并且它甚至能事先計算好 usage1 處的 a[i] 的地址,然后保留著在后面的 usage2 處使用,而不是重新計算。如果你按如下方式寫的話:

            const int i = ...;int * p = &i;//... do lots of stuff... = a[i]; // usage 1// ... lots more stuff(*p)++; // mess over compiler''s assumption// ... and other stuff... = a[i]; // usage 2
              編譯器將認為 i 是常量,從而 a[i] 的位置也是常量,這樣間接地破壞了先前的假設。因此,你的程序將會在 debug 編譯模式(沒有優化)和 release 編譯模式(完全優化)中反映出不同的行為,這種情況可不好,所以當你試圖把指向 i 的指針賦值給一個 可修改的引用時,會被編譯器診斷為這是一種偽造。這就是為什么(LPCTSTR)強制類型轉化不起作用的原因。
              為什么不把該成員聲明成 LPCTSTR 類型呢?因為這個結構被用于讀寫控件。當你向控件寫數據時,文本指針實際上被當成 LPCTSTR,而當你從控件讀數據 時,你必須有一個可寫的字符串。這個結構無法區分它是用來讀還是用來寫。

            因此,你會常常在我的代碼中看到如下的用法:

            tvi.item.pszText = (LPTSTR)(LPCTSTR)s;
              它把 CString 強制類型轉化成 LPCTSTR,也就是說先獲得改字符串的地址,然后再強制類型轉化成 LPTSTR,以便可以對之進行賦值操作。 注意這只有在使用 Set 或 Insert 之類的方法才有效!如果你試圖獲取數據,則不能這么做。
              如果你打算獲取存儲在控件中的數據,則方法稍有不同,例如,對某個 CTreeCtrl 使用 GetItem 方法,我想獲取項目的文本。我知道這些 文本的長度不會超過 MY_LIMIT,因此我可以這樣寫:

            TVITEM tvi;// ... assorted initialization of other fields of tvitvi.pszText = s.GetBuffer(MY_LIMIT);tvi.cchTextMax = MY_LIMIT;c_MyTree.GetItem(&tvi);s.ReleaseBuffer();
              可以看出來,其實上面的代碼對所有類型的 Set 方法都適用,但是并不需要這么做,因為所有的類 Set 方法(包括 Insert方法)不會改變字符串的內容。但是當你需要寫 CString 對象時,必須保證緩沖是可寫的,這正是 GetBuffer 所做的事情。再次強調: 一旦做了一次 GetBuffer 調用,那么在調用 ReleaseBuffer 之前不要對這個 CString 對象做任何操作。

             5、CString 型轉化成 BSTR 型

              當我們使用 ActiveX 控件編程時,經常需要用到將某個值表示成 BSTR 類型。BSTR 是一種記數字符串,Intel平臺上的寬字符串(Unicode),并且 可以包含嵌入的 NULL 字符。

            你可以調用 CString 對象的 AllocSysString 方法將 CString 轉化成 BSTR:

            CString s;s = ... ; // whateverBSTR b = s.AllocSysString();
              現在指針 b 指向的就是一個新分配的 BSTR 對象,該對象是 CString 的一個拷貝,包含終結 NULL字符。現在你可以將它傳遞給任何需要 BSTR 的接口。通常,BSTR 由接收它的組件來釋放,如果你需要自己釋放 BSTR 的話,可以這么做:

            ::SysFreeString(b);
              對于如何表示傳遞給 ActiveX 控件的字符串,在微軟內部曾一度爭論不休,最后 Visual Basic 的人占了上風,BSTR(“Basic String”的首字母縮寫)就是這場爭論的結果。

             6、BSTR 型轉化成 CString 型

              由于 BSTR 是記數 Unicode 字符串,你可以用標準轉換方法來創建 8 位的 CString。實際上,這是 CString 內建的功能。在 CString 中 有特殊的構造函數可以把 ANSI 轉化成 Unicode,也可以把Unicode 轉化成 ANSI。你同樣可以從 VARIANT 類型的變量中獲得 BSTR 類型的字符串,VARIANT 類型是 由各種 COM 和 Automation (自動化)調用返回的類型。

            例如,在一個ANSI程序中:

            BSTR b;b = ...; // whateverCString s(b == NULL ? L"" : b)
              對于單個的 BSTR 串來說,這種用法可以工作得很好,這是因為 CString 有一個特殊的構造函數以LPCWSTR(BSTR正是這種類型) 為參數,并將它轉化成 ANSI 類型。專門檢查是必須的,因為 BSTR 可能為空值,而 CString 的構造函數對于 NULL 值情況考慮的不是很周到,(感謝 Brian Ross 指出這一點!)。這種用法也只能處理包含 NUL 終結字符的單字符串;如果要轉化含有多個 NULL 字符 串,你得額外做一些工作才行。在 CString 中內嵌的 NULL 字符通常表現不盡如人意,應該盡量避免。
              根據 C/C++ 規則,如果你有一個 LPWSTR,那么它別無選擇,只能和 LPCWSTR 參數匹配。

            在 Unicode 模式下,它的構造函數是:

            CString::CString(LPCTSTR);
            正如上面所表示的,在 ANSI 模式下,它有一個特殊的構造函數:

            CString::CString(LPCWSTR);
              它會調用一個內部的函數將 Unicode 字符串轉換成 ANSI 字符串。(在Unicode模式下,有一個專門的構造函數,該函數有一個參數是LPCSTR類型——一個8位 ANSI 字符串 指針,該函數將它加寬為 Unicode 的字符串!)再次強調:一定要檢查 BSTR 的值是否為 NULL。
              另外還有一個問題,正如上文提到的:BSTRs可以含有多個內嵌的NULL字符,但是 CString 的構造函數只能處理某個串中單個 NULL 字符。 也就是說,如果串中含有嵌入的 NUL字節,CString 將會計算出錯誤的串長度。你必須自己處理它。如果你看看 strcore.cpp 中的構造函數,你會發現 它們都調用了lstrlen,也就是計算字符串的長度。
              注意從 Unicode 到 ANSI 的轉換使用帶專門參數的 ::WideCharToMultiByte,如果你不想使用這種默認的轉換方式,則必須編寫自己的轉化代碼。
              如果你在 UNICODE 模式下編譯代碼,你可以簡單地寫成:


            CString convert(BSTR b){    if(b == NULL)        return CString(_T(""));    CString s(b); // in UNICODE mode    return s;}
              如果是 ANSI 模式,則需要更復雜的過程來轉換。注意這個代碼使用與 ::WideCharToMultiByte 相同的參數值。所以你 只能在想要改變這些參數進行轉換時使用該技術。例如,指定不同的默認字符,不同的標志集等。
            CString convert(BSTR b){    CString s;    if(b == NULL)       return s; // empty for NULL BSTR#ifdef UNICODE    s = b;#else    LPSTR p = s.GetBuffer(SysStringLen(b) + 1);     ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page                          0,                 // no flags                          b,                 // source widechar string                          -1,                // assume NUL-terminated                          p,                 // target buffer                          SysStringLen(b)+1, // target buffer length                          NULL,              // use system default char                          NULL);             // don''t care if default used    s.ReleaseBuffer();#endif    return s;}
              我并不擔心如果 BSTR 包含沒有映射到 8 位字符集的 Unicode 字符時會發生什么,因為我指定了::WideCharToMultiByte 的最后兩個參數為 NULL。這就是你可能需要改變的地方。

             7、VARIANT 型轉化成 CString 型

              事實上,我從來沒有這么做過,因為我沒有用 COM/OLE/ActiveX 編寫過程序。但是我在microsoft.public.vc.mfc 新聞組上看到了 Robert Quirk 的一篇帖子談到了這種轉化,我覺得把他的文章包含在我的文章里是不太好的做法,所以在這里多做一些解釋和演示。如果和他的文章有相孛的地方可能是我的疏忽。
              VARIANT 類型經常用來給 COM 對象傳遞參數,或者接收從 COM 對象返回的值。你也能自己編寫返回 VARIANT 類型的方法,函數返回什么類型 依賴可能(并且常常)方法的輸入參數(比如,在自動化操作中,依賴與你調用哪個方法。IDispatch::Invoke 可能返回(通過其一個參數)一個 包含有BYTE、WORD、float、double、date、BSTR 等等 VARIANT 類型的結果,(詳見 MSDN 上的 VARIANT 結構的定義)。在下面的例子中,假設 類型是一個BSTR的變體,也就是說在串中的值是通過 bsrtVal 來引用,其優點是在 ANSI 應用中,有一個構造函數會把 LPCWCHAR 引用的值轉換為一個 CString(見 BSTR-to-CString 部分)。在 Unicode 模式中,將成為標準的 CString 構造函數,參見對缺省::WideCharToMultiByte 轉換的告誡,以及你覺得是否可以接受(大多數情況下,你會滿意的)。
            VARIANT vaData;vaData = m_com.YourMethodHere();ASSERT(vaData.vt == VT_BSTR);CString strData(vaData.bstrVal);
            你還可以根據 vt 域的不同來建立更通用的轉換例程。為此你可能會考慮:


            CString VariantToString(VARIANT * va){    CString s;    switch(va->vt)      { /* vt */       case VT_BSTR:          return CString(vaData->bstrVal);       case VT_BSTR | VT_BYREF:          return CString(*vaData->pbstrVal);       case VT_I4:          s.Format(_T("%d"), va->lVal);          return s;       case VT_I4 | VT_BYREF:          s.Format(_T("%d"), *va->plVal);       case VT_R8:          s.Format(_T("%f"), va->dblVal);          return s;       ... 剩下的類型轉換由讀者自己完成       default:          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)          return CString("");      } /* vt */}
             8、載入字符串表資源

              如果你想創建一個容易進行語言版本移植的應用程序,你就不能在你的源代碼中直接包含本土語言字符串 (下面這些例子我用的語言都是英語,因為我的本土語是英語),比如下面這種寫法就很糟:
            CString s = "There is an error";
              你應該把你所有特定語言的字符串單獨擺放(調試信息、在發布版本中不出現的信息除外)。這意味著向下面這樣寫比較好:

            s.Format(_T("%d - %s"), code, text);
              在你的程序中,文字字符串不是語言敏感的。不管怎樣,你必須很小心,不要使用下面這樣的串:

            // fmt is "Error in %s file %s"http:// readorwrite is "reading" or "writing"s.Format(fmt, readorwrite, filename);
              這是我的切身體會。在我的第一個國際化的應用程序中我犯了這個錯誤,盡管我懂德語,知道在德語的語法中動詞放在句子的最后面,我們的德國方面的發行人還是苦苦的抱怨他們不得不提取那些不可思議的德語錯誤提示信息然后重新格式化以讓它們能正常工作。比較好的辦法(也是我現在使用的辦法)是使用兩個字符串,一個用 于讀,一個用于寫,在使用時加載合適的版本,使得它們對字符串參數是非敏感的。也就是說加載整個格式,而不是加載串“reading”,“writing”:

            // fmt is "Error in reading file %s"http:// "Error in writing file %s"s.Format(fmt, filename);
              一定要注意,如果你有好幾個地方需要替換,你一定要保證替換后句子的結構不會出現問題,比如在英語中,可以是主語-賓語,主語-謂語,動詞-賓語的結構等等。
              在這里,我們并不討論 FormatMessage,其實它比 sprintf/Format 還要有優勢,但是不太容易和CString 結合使用。解決這種問題的辦法就是我們按照參數出現在參數表中的位置給參數取名字,這樣在你輸出的時候就不會把他們的位置排錯了。
              接下來我們討論我們這些獨立的字符串放在什么地方。我們可以把字符串的值放入資源文件中的一個稱為 STRINGTABLE 的段中。過程如下:首先使用 Visual Studio 的資源編輯器創建一個字符串,然后給每一個字符串取一個ID,一般我們給它取名字都以 IDS_開頭。所以如果你有一個信息,你可以創建一個字符串資源然后取名為 IDS_READING_FILE,另外一個就取名為 IDS_WRITING_FILE。它們以下面的形式出現在你的 .rc 文件中:

            STRINGTABLEIDS_READING_FILE "Reading file %s"IDS_WRITING_FILE "Writing file %s"END
            注意:這些資源都以 Unicode 的格式保存,不管你是在什么環境下編譯。他們在Win9x系統上也是以Unicode 的形式存在,雖然 Win9x 不能真正處理 Unicode。
            然后你可以這樣使用這些資源:
            // 在使用資源串表之前,程序是這樣寫的:


               CString fmt;      if(...)        fmt = "Reading file %s";     else       fmt = "Writing file %s";  ...    // much later  CString s;  s.Format(fmt, filename);
            // 使用資源串表之后,程序這樣寫:
                CString fmt;        if(...)           fmt.LoadString(IDS_READING_FILE);        else           fmt.LoadString(DS_WRITING_FILE);    ...      // much later    CString s;    s.Format(fmt, filename);
              現在,你的代碼可以移植到任何語言中去。LoadString 方法需要一個字符串資源的 ID 作為參數,然后它從 STRINGTABLE 中取出它對應的字符串,賦值給 CString 對象。 CString 對象的構造函數還有一個更加聰明的特征可以簡化 STRINGTABLE 的使用。這個用法在 CString::CString 的文檔中沒有指出,但是在 構造函數的示例程序中使用了。(為什么這個特性沒有成為正式文檔的一部分,而是放在了一個例子中,我記不得了!)——【譯者注:從這句話看,作者可能是CString的設計者。其實前面還有一句類似的話。說他沒有對使用GetBuffer(0)獲得的指針指向的地址是否可讀做有效性檢查 】。這個特征就是:如果你將一個字符串資源的ID強制類型轉換為 LPCTSTR,將會隱含調用 LoadString。因此,下面兩個構造字符串的例子具有相同的效果,而且其 ASSERT 在debug模式下不會被觸發:
            CString s;s.LoadString(IDS_WHATEVER);CString t( (LPCTSTR)IDS_WHATEVER );ASSERT(s == t);//不會被觸發,說明s和t是相同的。
              現在,你可能會想:這怎么可能工作呢?我們怎么能把 STRINGTABLE ID 轉化成一個指針呢?很簡單:所有的字符串 ID 都在1~65535這個范圍內,也就是說,它所有的高位都是0,而我們在程序中所使用的指針是不可能小于65535的,因為程序的低 64K 內存永遠也不可能存在的,如果你試圖訪問0x00000000到0x0000FFFF之間的內存,將會引發一個內存越界錯誤。所以說1~65535的值不可能是一個內存地址,所以我們可以用這些值來作為字符串資源的ID。
              我傾向于使用 MAKEINTRESOURCE 宏顯式地做這種轉換。我認為這樣可以讓代碼更加易于閱讀。這是個只適合在 MFC 中使用的標準宏。你要記住,大多數的方法即可以接受一個 UINT 型的參數,也可以接受一個 LPCTSTR 型的參數,這是依賴 C++ 的重載功能做到的。C++重載函數帶來的 弊端就是造成所有的強制類型轉化都需要顯示聲明。同樣,你也可以給很多種結構只傳遞一個資源名。

            CString s;s.LoadString(IDS_WHATEVER);CString t( MAKEINTRESOURCE(IDS_WHATEVER));ASSERT(s == t);
              告訴你吧:我不僅只是在這里鼓吹,事實上我也是這么做的。在我的代碼中,你幾乎不可能找到一個字符串,當然,那些只是偶然在調試中出現的或者和語言無關的字符串除外。

             9、CString 和臨時對象

              這是出現在 microsoft.public.vc.mfc 新聞組中的一個小問題,我簡單的提一下,這個問題是有個程序員需要往注冊表中寫入一個字符串,他寫道:
              我試著用 RegSetValueEx() 設置一個注冊表鍵的值,但是它的結果總是令我困惑。當我用char[]聲明一個變量時它能正常工作,但是當我用 CString 的時候,總是得到一些垃圾:"&Yacute;&Yacute;&Yacute;&Yacute;...&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;&Yacute;"為了確認是不是我的 CString 數據出了問題,我試著用 GetBuffer,然后強制轉化成 char*,LPCSTR。GetBuffer 返回的值是正確的,但是當我把它賦值給 char* 時,它就變成垃圾了。以下是我的程序段:

            char* szName = GetName().GetBuffer(20);RegSetValueEx(hKey, "Name", 0, REG_SZ,              (CONST BYTE *) szName,             strlen (szName + 1));
            這個 Name 字符串的長度小于 20,所以我不認為是 GetBuffer 的參數的問題。

            真讓人困惑,請幫幫我。

            親愛的 Frustrated,

            你犯了一個相當微妙的錯誤,聰明反被聰明誤,正確的代碼應該象下面這樣:


            CString Name = GetName();RegSetValueEx(hKey, _T("Name"), 0, REG_SZ,                     (CONST BYTE *) (LPCTSTR)Name,                    (Name.GetLength() + 1) * sizeof(TCHAR));
              為什么我寫的代碼能行而你寫的就有問題呢?主要是因為當你調用 GetName 時返回的 CString 對象是一個臨時對象。參見:《C++ Reference manual》§12.2
              在一些環境中,編譯器有必要創建一個臨時對象,這樣引入臨時對象是依賴于實現的。如果編譯器引入的這個臨時對象所屬的類有構造函數的話,編譯器要確保這個類的構造函數被調用。同樣的,如果這個類聲明有析構函數的話,也要保證這個臨時對象的析構函數被調用。
              編譯器必須保證這個臨時對象被銷毀了。被銷毀的確切地點依賴于實現.....這個析構函數必須在退出創建該臨時對象的范圍之前被調用。
              大部分的編譯器是這樣設計的:在臨時對象被創建的代碼的下一個執行步驟處隱含調用這個臨時對象的析構函數,實現起來,一般都是在下一個分號處。因此,這個 CString 對象在 GetBuffer 調用之后就被析構了(順便提一句,你沒有理由給 GetBuffer 函數傳遞一個參數,而且沒有使用ReleaseBuffer 也是不對的)。所以 GetBuffer 本來返回的是指向這個臨時對象中字符串的地址的指針,但是當這個臨時對象被析構后,這塊內存就被釋放了。然后 MFC 的調試內存分配器會重新為這塊內存全部填上 0xDD,顯示出來剛好就是“&Yacute;”符號。在這個時候你向注冊表中寫數據,字符串的內容當然全被破壞了。
              我們不應該立即把這個臨時對象轉化成 char* 類型,應該先把它保存到一個 CString 對象中,這意味著把臨時對象復制了一份,所以當臨時的 CString 對象被析構了之后,這個 CString 對象中的值依然保存著。這個時候再向注冊表中寫數據就沒有問題了。
              此外,我的代碼是具有 Unicode 意識的。那個操作注冊表的函數需要一個字節大小,使用lstrlen(Name+1) 得到的實際結果對于 Unicode 字符來說比 ANSI 字符要小一半,而且它也不能從這個字符串的第二個字符起開始計算,也許你的本意是 lstrlen(Name) + 1(OK,我承認,我也犯了同樣的錯誤!)。不論如何,在 Unicode 模式下,所有的字符都是2個字節大小,我們需要處理這個問題。微軟的文檔令人驚訝地對此保持緘默:REG_SZ 的值究竟是以字節計算還是以字符計算呢?我們假設它指的是以字節為單位計算,你需要對你的代碼做一些修改來計算這個字符串所含有的字節大小。

             10、CString 的效率

              CString 的一個問題是它確實掩藏了一些低效率的東西。從另外一個方面講,它也確實可以被實現得更加高效,你可能會說下面的代碼:
            CString s = SomeCString1;s += SomeCString2;s += SomeCString3;s += ",";s += SomeCString4;
            比起下面的代碼來,效率要低多了:

            char s[1024];lstrcpy(s, SomeString1);lstrcat(s, SomeString2);lstrcat(s, SomeString 3);lstrcat(s, ",");lstrcat(s, SomeString4);
              總之,你可能會想,首先,它為 SomeCString1 分配一塊內存,然后把 SomeCString1 復制到里面,然后發現它要做一個連接,則重新分配一塊新的足夠大的內存,大到能夠放下當前的字符串加上SomeCString2,把內容復制到這塊內存 ,然后把 SomeCString2 連接到后面,然后釋放第一塊內存,并把指針重新指向新內存。然后為每個字符串重復這個過程。把這 4 個字符串連接起來效率多低啊。事實上,在很多情況下根本就不需要復制源字符串(在 += 操作符左邊的字符串)。
              在 VC++6.0 中,Release 模式下,所有的 CString 中的緩存都是按預定義量子分配的。所謂量子,即確定為 64、128、256 或者 512 字節。這意味著除非字符串非常長,連接字符串的操作實際上就是 strcat 經過優化后的版本(因為它知道本地的字符串應該在什么地方結束,所以不需要尋找字符串的結尾;只需要把內存中的數據拷貝到指定的地方即可)加上重新計算字符串的長度。所以它的執行效率和純 C 的代碼是一樣的,但是它更容易寫、更容易維護和更容易理解。
              如果你還是不能確定究竟發生了怎樣的過程,請看看 CString 的源代碼,strcore.cpp,在你 vc98的安裝目錄的 mfc\src 子目錄中。看看 ConcatInPlace 方法,它被在所有的 += 操作符中調用。

            啊哈!難道 CString 真的這么"高效"嗎?比如,如果我創建

            CString cat("Mew!");
              然后我并不是得到了一個高效的、精簡的5個字節大小的緩沖區(4個字符加一個結束字符),系統將給我分配64個字節,而其中59個字節都被浪費了。
              如果你也是這么想的話,那么就請準備好接受再教育吧。可能在某個地方某個人給你講過盡量使用少的空間是件好事情。不錯,這種說法的確正確,但是他忽略了事實中一個很重要的方面。
              如果你編寫的是運行在16K EPROMs下的嵌入式程序的話,你有理由盡量少使用空間,在這種環境下,它能使你的程序更健壯。但是在 500MHz, 256MB的機器上寫 Windows 程序,如果你還是這么做,它只會比你認為的“低效”的代碼運行得更糟。
              舉例來說。字符串的大小被認為是影響效率的首要因素,使字符串盡可能小可以提高效率,反之則降低效率,這是大家一貫的想法。但是這種想法是不對的,精確的內存分配的后果要在程序運行了好幾個小時后才能體現得出來,那時,程序的堆中將充滿小片的內存,它們太小以至于不能用來做任何事,但是他們增加了你程序的內存用量,增加了內存頁面交換的次數,當頁面交換的次數增加到系統能夠忍受的上限,系統則會為你的程序分配更多的頁面,直到你的程序占用了所有的可用內存。由此可見,雖然內存碎片是決定效率的次要因素,但正是這些因素實際控制了系統的行為,最終,它損害了系統的可靠性,這是令人無法接受的。
              記住,在 debug 模式下,內存往往是精確分配的,這是為了更好的排錯。
              假設你的應用程序通常需要連續工作好幾個月。比如,我常打開 VC++,Word,PowerPoint,Frontpage,Outlook Express,Forté Agent,Internet Explorer和其它的一些程序,而且通常不關閉它們。我曾經夜以繼日地連續用 PowerPoint 工作了好幾天(反之,如果你不幸不得不使用像 Adobe FrameMaker 這樣的程序的話,你將會體會到可靠性的重要;這個程序機會每天都要崩潰4~6次,每次都是因為用完了所有的空間并填滿我所有的交換頁面)。所以精確內存分配是不可取的,它會危及到系統的可靠性,并引起應用程序崩潰。
              按量子的倍數為字符串分配內存,內存分配器就可以回收用過的內存塊,通常這些回收的內存塊馬上就可以被其它的 CString 對象重新用到,這樣就可以保證碎片最少。分配器的功能加強了,應用程序用到的內存就能盡可能保持最小,這樣的程序就可以運行幾個星期或幾個月而不出現問題。
              題外話:很多年以前,我們在 CMU 寫一個交互式系統的時候,一些對內存分配器的研究顯示出它往往產生很多內存碎片。Jim Mitchell,現在他在 Sun Microsystems 工作,那時侯他創造了一種內存分配器,它保留了一個內存分配狀況的運行時統計表,這種技術和當時的主流分配器所用的技術都不同,且較為領先。當一個內存塊需要被分割得比某一個值小的話,他并不分割它,因此可以避免產生太多小到什么事都干不了的內存碎片。事實上他在內存分配器中使用了一個浮動指針,他認為:與其讓指令做長時間的存取內存操作,還不如簡單的忽略那些太小的內存塊而只做一些浮動指針的操作。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是對的。
              永遠不要認為所謂的“最優化”是建立在每一行代碼都高速且節省內存的基礎上的,事實上,高速且節省內存應該是在一個應用程序的整體水平上考慮的。在軟件的整體水平上,只使用最小內存的字符串分配策略可能是最糟糕的一種方法。
              如果你認為優化是你在每一行代碼上做的那些努力的話,你應該想一想:在每一行代碼中做的優化很少能真正起作用。你可以看我的另一篇關于優化問題的文章《Your Worst Enemy for some thought-provoking ideas》。
              記住,+= 運算符只是一種特例,如果你寫成下面這樣:

            CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;
            則每一個 + 的應用會造成一個新的字符串被創建和一次復制操作。

             總結

              以上是使用 CString 的一些技巧。我每天寫程序的時候都會用到這些。CString 并不是一種很難使用的類,但是 MFC 沒有很明顯的指出這些特征,需要你自己去探索、去發現。

            posted @ 2008-01-12 22:55 李亞 閱讀(242) | 評論 (0)編輯 收藏

            Windows基本的數據類型

            下面列出較為常用的數據類型,大體包括了字符型、整型、浮點型、布爾型、
            指針型以及Windows應用程程特有的句柄型,表示指針型的數據類型往往以P或LP
            作為前綴,而句柄型總是冠以H。

            類型                    定義
            -------------------------------------------------------------
            ATOM                        原子(字符串在原子表中的參考值)
            BOOL                         布爾型(邏輯型)變量(應為TRUE或FALSE)
            BOOLEAN                 布爾型(邏輯型)變量(應為TRUE或FALSE)
            BYTE                          字節(8位)
            CCHAR                       Windows字符
            CHAR                         Windows字符
            COLORREF                RGB(紅綠藍)顏色值(32位)
            CONST                      在執行時其值保持不變的變量
            DLGPROC                 指向應用程序定義的對話框過程回調過程的指針
            DWORD                     雙字(32位)
            DWORDLONG          雙雙字(64位)
            FARPROC                 指向應用程序定義的指針
            FLOAT                       浮點型變量
            GLOBALHANDLE    全局內存塊句柄
            HACCEL                    加速鍵表句柄
            HANDLE                    對象句柄
            HBITMAP                  位圖句柄
            HBRUSH                    畫刷句柄
            HDC                           設備描述表句柄
            HFILE                         文件句柄
            HFONT                      字體句柄
            HGDIOBJ                   GDI(圖形設備接口)對象句柄
            HGLOBAL                 全局內存塊句柄
            HHOOK                     鉤子句柄
            HICON                       圖標句柄
            HINSTANCE              實例句柄
            HLOCAL                    本地內存句柄
            HMENU                      菜單句柄
            HOOKPROC              指向應用程序定義的鉤子的指針
            HPALETTE                 調色板句柄
            HPEN                          畫筆句柄
            HWND                        窗口句柄
            LOCALHAND            本地內存句柄
            LONG                          32位無符號值
            LONGLONG               64位無符號值
            LPARAM                     32位消息參數
            LPCSTR                      指向Windows常字符串(以空字符結束)的指針
            LPSTR                         指向Windows字符串(以空字符結束)的指針
            LPVOID                      指向任意類型的指針
            PROC                          指向回調函數的指針
            SHORT                        短整型數
            UCHAR                       無符號Windows字符
            UINT                           無符號整數
            ULONG                       無符號長整型數(32位)
            USHORT                     無符號知整型數(16位)
            VOID                           任意類型
            WNDPROC                 指向在應用程序中定義的窗口過程的指針
            WORD                         無符號字(16位)
            WPARAM                   32位消息參數 

            posted @ 2008-01-12 17:25 李亞 閱讀(256) | 評論 (0)編輯 收藏

            [轉]堆內存和棧內存

            堆:順序隨意
            棧:先進后出
            堆和棧的區別
            一、預備知識—程序的內存分配
            一個由c/C++編譯的程序占用的內存分為以下幾個部分
            1、棧區(stack)— 由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧
            2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
            3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 - 程序結束后有系統釋放
            4、文字常量區 —常量字符串就是放在這里的。 程序結束后由系統釋放
            5、程序代碼區—存放函數體的二進制代碼。
            二、例子程序
            這是一個前輩寫的,非常詳細
            //main.cpp
            int a = 0; 全局初始化區
            char *p1; 全局未初始化區
            main()
            {    
                 int b; 棧
                 char s[] = "abc"; 棧
                 char *p2; 棧
                 char *p3 = "123456"; 123456\0在常量區,p3在棧上。
                 static int c =0; 全局(靜態)初始化區
                 p1 = (char *)malloc(10);
                 p2 = (char *)malloc(20);
                 分配得來得10和20字節的區域就在堆區。
                 strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。
            }
            二、堆和棧的理論知識
            2.1申請方式
            stack:由系統自動分配。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間
            heap:需要程序員自己申請,并指明大小,在c中malloc函數如p1 = (char *)malloc(10);在C++中用new運算符如p2 = (char *)malloc(10);但是注意p1、p2本身是在棧中的
            2.2申請后系統的響應
            棧:只要棧的剩余空間大于所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。
            堆:首先應該知道操作系統有一個記錄空閑內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大于所申請空間的堆結點,然后將該結點從空閑結點鏈表中刪除,并將該結點的空間分配給程序,另外,對于大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的將多余的那部分重新放入空閑鏈表中。
            2.3申請大小的限制
            棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就確定的常數),如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
            堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。
            2.4申請效率的比較:
            棧由系統自動分配,速度較快。但程序員是無法控制的。
            堆是由new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便. 另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,他不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。但是速度快,也最靈活
            2.5堆和棧中的存儲內容
            棧: 在函數調用時,第一個進棧的是主函數中后的下一條指令(函數調用語句的下一條可執行語句)的地址,然后是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然后是函數中的局部變量。注意靜態變量是不入棧的。當本次函數調用結束后,局部變量先出棧,然后是參數,最后棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。
            堆:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。
            2.6存取效率的比較
            char s1[] = "aaaaaaaaaaaaaaa";
            char *s2 = "bbbbbbbbbbbbbbbbb";
            aaaaaaaaaaa是在運行時刻賦值的;而bbbbbbbbbbb是在編譯時就確定的;但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
            比如:
            #include
            void main()
            {    
                 char a = 1;     
                 char c[] = "1234567890";     
                 char *p ="1234567890";    
                 a = c[1];     
                 a = p[1];     
                 return;     
            }
            對應的匯編代碼
            10: a = c[1];
            00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
            0040106A 88 4D FC mov byte ptr [ebp-4],cl
            11: a = p[1];
            0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
            00401070 8A 42 01 mov al,byte ptr [edx+1]
            00401073 88 45 FC mov byte ptr [ebp-4],al
            第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。
            2.7小結:
            堆和棧的區別可以用如下的比喻來看出:使用棧就象我們去飯館里吃飯,只管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。
            堆和棧的區別主要分:
            操作系統方面的堆和棧,如上面說的那些,不多說了。還有就是數據結構方面的堆和棧,這些都是不同的概念。這里的堆實際上指的就是(滿足堆性質的)優先隊列的一種數據結構,第1個元素有最高的優先權;棧實際上就是滿足先進后出的性質的數學或數據結構。雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大區別的,連著叫只是由于歷史的原因。

            posted @ 2008-01-12 17:17 李亞 閱讀(167) | 評論 (0)編輯 收藏

            [轉]SQL Server連接中的四個最常見錯誤

            一."SQL Server 不存在或訪問被拒絕"

            這個是最復雜的,錯誤發生的原因比較多,需要檢查的方面也比較多.

            一般說來,有以下幾種可能性:

            1,SQL Server名稱或IP地址拼寫有誤
            2,服務器端網絡配置有誤
            3,客戶端網絡配置有誤

            要解決這個問題,我們一般要遵循以下的步驟來一步步找出導致錯誤的原因.


            ============= 首先,檢查網絡物理連接 =============
            ping <服務器IP地址/服務器名稱>

            如果 ping <服務器IP地址> 不成功,說明物理連接有問題,這時候要檢查硬件設備,如網卡,HUB,路由器等.
            還有一種可能是由于客戶端和服務器之間安裝有防火墻軟件造成的,比如 ISA Server.防火墻軟件可能會屏蔽對 ping,telnet 等的響應
            因此在檢查連接問題的時候,我們要先把防火墻軟件暫時關閉,或者打開所有被封閉的端口.

            如果ping <服務器IP地址> 成功而,ping <服務器名稱> 失敗
            則說明名字解析有問題,這時候要檢查 DNS 服務是否正常.
            有時候客戶端和服務器不在同一個局域網里面,這時候很可能無法直接使用服務器名稱來標識該服務器,這時候我們可以使用HOSTS文件來進行名字解析,
            具體的方法是:

            1.使用記事本打開HOSTS文件(一般情況下位于C:\WINNT\system32\drivers\etc).
            添加一條IP地址與服務器名稱的對應記錄,如:
            172.168.10.24 myserver

            2.或在 SQL Server 的客戶端網絡實用工具里面進行配置,后面會有詳細說明.


            ============= 其次,使用 telnet 命令檢查SQL Server服務器工作狀態 =============
            telnet <服務器IP地址> 1433

            如果命令執行成功,可以看到屏幕一閃之后光標在左上角不停閃動,這說明 SQL Server 服務器工作正常,并且正在監聽1433端口的 TCP/IP 連接
            如果命令返回"無法打開連接"的錯誤信息,則說明服務器端沒有啟動 SQL Server 服務,
            也可能服務器端沒啟用 TCP/IP 協議,或者服務器端沒有在 SQL Server 默認的端口1433上監聽.


            =============接著,我們要到服務器上檢查服務器端的網絡配置,檢查是否啟用了命名管道.是否啟用了 TCP/IP 協議等等 =============
            可以利用 SQL Server 自帶的服務器網絡使用工具來進行檢查.

            點擊:程序 -- Microsoft SQL Server -- 服務器網絡使用工具

            打開該工具后,在"常規"中可以看到服務器啟用了哪些協議.
            一般而言,我們啟用命名管道以及 TCP/IP 協議.
            點中 TCP/IP 協議,選擇"屬性",我們可以來檢查 SQK Server 服務默認端口的設置
            一般而言,我們使用 SQL Server 默認的1433端口.如果選中"隱藏服務器",則意味著客戶端無法通過枚舉服務器來看到這臺服務器,起到了保護的作用,但不影響連接.


            ============= 接下來我們要到客戶端檢查客戶端的網絡配置 =============
            我們同樣可以利用 SQL Server 自帶的客戶端網絡使用工具來進行檢查,
            所不同的是這次是在客戶端來運行這個工具.

            點擊:程序 -- Microsoft SQL Server -- 客戶端網絡使用工具

            打開該工具后,在"常規"項中,可以看到客戶端啟用了哪些協議.
            一般而言,我們同樣需要啟用命名管道以及 TCP/IP 協議.
            點擊 TCP/IP 協議,選擇"屬性",可以檢查客戶端默認連接端口的設置,該端口必須與服務器一致.

            單擊"別名"選項卡,還可以為服務器配置別名.服務器的別名是用來連接的名稱,
            連接參數中的服務器是真正的服務器名稱,兩者可以相同或不同.別名的設置與使用HOSTS文件有相似之處.


            通過以上幾個方面的檢查,基本上可以排除第一種錯誤.


            -----------------------------------------------------------------------------

            二."無法連接到服務器,用戶xxx登陸失敗"

            該錯誤產生的原因是由于SQL Server使用了"僅 Windows"的身份驗證方式,
            因此用戶無法使用SQL Server的登錄帳戶(如 sa )進行連接.解決方法如下所示:

            1.在服務器端使用企業管理器,并且選擇"使用 Windows 身份驗證"連接上 SQL Server
            操作步驟:
            在企業管理器中
            --右鍵你的服務器實例(就是那個有綠色圖標的)
            --編輯SQL Server注冊屬性
            --選擇"使用windows身份驗證"

            --選擇"使用SQL Server身份驗證"
            --登錄名輸入:sa,密碼輸入sa的密碼
            --確定

            2.設置允許SQL Server身份登錄
            操作步驟:
            在企業管理器中
            --展開"SQL Server組",鼠標右鍵點擊SQL Server服務器的名稱
            --選擇"屬性"
            --再選擇"安全性"選項卡
            --在"身份驗證"下,選擇"SQL Server和 Windows ".
            --確定,并重新啟動SQL Server服務.

            在以上解決方法中,如果在第 1 步中使用"使用 Windows 身份驗證"連接 SQL Server 失敗,
            那就通過修改注冊表來解決此問題:

            1.點擊"開始"-"運行",輸入regedit,回車進入注冊表編輯器
            2.依次展開注冊表項,瀏覽到以下注冊表鍵:
            [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer]
            3.在屏幕右方找到名稱"LoginMode",雙擊編輯雙字節值
            4.將原值從1改為2,點擊"確定"
            5.關閉注冊表編輯器
            6.重新啟動SQL Server服務.

            此時,用戶可以成功地使用sa在企業管理器中新建SQL Server注冊,
            但是仍然無法使用Windows身份驗證模式來連接SQL Server.
            這是因為在 SQL Server 中有兩個缺省的登錄帳戶:
            BUILTIN\Administrators
            <機器名>\Administrator 被刪除.
            要恢復這兩個帳戶,可以使用以下的方法:

            1.打開企業管理器,展開服務器組,然后展開服務器

            2.展開"安全性",右擊"登錄",然后單擊"新建登錄"

            3.在"名稱"框中,輸入 BUILTIN\Administrators

            4.在"服務器角色"選項卡中,選擇"System Administrators"

            5.點擊"確定"退出

            6.使用同樣方法添加 <機器名>\Administrator 登錄.

            說明:

            以下注冊表鍵:
            HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\LoginMode
            的值決定了SQL Server將采取何種身份驗證模式.
            1.表示使用"Windows 身份驗證"模式
            2.表示使用混合模式(Windows 身份驗證和 SQL Server 身份驗證).


            -----------------------------------------------------------------------------

            三.提示連接超時

            如果遇到第三個錯誤,一般而言表示客戶端已經找到了這臺服務器,并且可以進行連接,
            不過是由于連接的時間大于允許的時間而導致出錯.
            這種情況一般會發生在當用戶在Internet上運行企業管理器來注冊另外一臺同樣在Internet上的服務器,
            并且是慢速連接時,有可能會導致以上的超時錯誤.有些情況下,由于局域網的網絡問題,也會導致這樣的錯誤.

            要解決這樣的錯誤,可以修改客戶端的連接超時設置.
            默認情況下,通過企業管理器注冊另外一臺SQL Server的超時設置是 4 秒,
            而查詢分析器是 15 秒(這也是為什么在企業管理器里發生錯誤的可能性比較大的原因).

            具體步驟為:
            企業管理器中的設置:
            1.在企業管理器中,選擇菜單上的"工具",再選擇"選項"
            2.在彈出的"SQL Server企業管理器屬性"窗口中,點擊"高級"選項卡
            3.在"連接設置"下的"登錄超時(秒)"右邊的框中輸入一個比較大的數字,如 20.

            查詢分析器中的設置:
            工具 -- 選項 -- 連接 -- 將登錄超時設置為一個較大的數字


            ---------------------------------------------------------------------------------

            四.大部分機都用Tcp/ip才能成功,有次我發現用Named Pipes才可以?

            回復人: leimin(黃山光明頂)

            這是因為在WINDOWS 2000以后的操作系統中,MS為解決SQL SERVER的安全問題將TCP/IP配置
            為SQLSERVER的默認連接協議,你可以在CLIENT NETWORK UTILITY中看到TCP/IP和NAME PIPE
            的順序。

            你也可以在:
            [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib]
            "ProtocolOrder"=hex(7):74,00,63,00,70,00,00,00,00,00
            看到默認的協議。

            2.怎么在程序中更改Named Pipes , Tcp/ip ,其sql語句怎么寫?
            你可以在上面提到的注冊表的位置修改:
            CLIENT端:
            [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib]
            "ProtocolOrder"=hex(7):74,00,63,00,70,00,00,00,00,00

            SERVER端:
            [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\SuperSocketNetLib]
            "ProtocolOrder"=hex(7):74,00,63,00,70,00,00,00,00,00

            posted @ 2008-01-12 16:25 李亞 閱讀(153) | 評論 (0)編輯 收藏

            C#3.0新特性(webcast課程筆記)

            C#3.0新特性隱式類型化本地變量
            關鍵字 var
            在一個隱式類型化的本地變量和聲明中,本地變量類型的聲明過程是由
            使用的表達式初始化變量來推斷的。當一個本地變量聲明標示為var作為
            類型并且沒有var類型名稱在范圍內,那么這個聲明被視作隱式類型化的
            本地變量聲明
            簡單的說就是不定義變量的類型,而是根據變量的賦值去判斷判斷變量的數據類型。
            注意:
            . 聲明者必須包含一個構造者
            . 這個構造器必須是一個表達式
            . 在編譯時刻構造器表達式的類型不能為null類型
            . 如果本地變量包含多種構造器,那么構造器必須都具有相同的編譯時類型



            自動屬性
            允許避免手工聲明一個私有成員變量以及編寫get/set邏輯,取而代之的是編譯器會自動為你生成一個私有變量和默認的get/set操作

             

            對象初始化器和集合初始化器
            . 可以與構造函數一起使用
            . 允許部分賦值
            . 允許給internal成員賦值
            . 構造函數比對象初始化先執行的



             匿名類型
            . 允許建立新的操作符被用來作為匿名對象構造器以建立匿名類型的對象
            . 匿名類型是沒有類類型,直接繼承于object

             分部方法
            . 分部方法是一些方法,它使輕量級的事件
            處理成為可行
            . 在定義分部方法時,值得注意的是:
            1、分部方法必須聲明在分部類型(partial class)中;
            2、分部方法使用partial 修飾符;
            3、分部方法并不是總有方法體(body,即方法的實現);
            4、分部方法必須返回void;
            5、分部方法可以是靜態的(即使用static 修飾符);
            6、分部方法可以包含參數(包括在參數中使用this、ref 和
            params 修飾符,不支持out 修飾符可以使用ref 修飾符來代替它);
            7、分部方法必須是私有方法(private)。

            擴展方法
            . 擴展方法(Extension method),可以對現有類功能進行
            擴充,從而使該類型的實例具有更多的方法(功能)。
            . Extension Method僅僅是看起來像是一個類型的方法,但
            其實質上不是,它更像是靜態類型的靜態方法,事實上,
            它確實擁有靜態方法所具有的所有功能
            . Extension Method的作用域是整個namespace可見的,并
            且可以通過using namespace來導入其它命名空間中的
            Extension Method


            Lambdas和表達式樹
            C#2.0 引入了匿名函數,它允許代碼塊能夠被寫成“內聯”在代理值所
            期望的地方。當匿名函數提供功能性編程語言的巨大威力的同時,匿名
            函數的標記也顯得相當的冗長。Lambda表達式提供了更簡明的功能性
            標記來書寫匿名函數
            --Lambda表達式書寫為一組參數列表,緊接著=>標記,然后跟隨某個
            表達式或聲明塊
            --Lambda表達式的參數可以是顯式的或者隱式的類型。在一個顯式類
            型參數列表中,每個參數的類型都必須顯式聲明。
            --表達式樹允許lambda表達式能夠代表數據結構替代表示為執行代碼


            查詢表達式
            .查詢表達式提供了,語言集成化的標記,
            為了和那些關系型或者等級型查詢語言,
            例如:SQL和Xquery相類似的查詢
            .查詢表達式以from語句開始并且以select或
            者group語句結束


             

            posted @ 2008-01-08 21:16 李亞 閱讀(295) | 評論 (0)編輯 收藏

            僅列出標題
            共4頁: 1 2 3 4 
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導航

            統計

            公告

            這世界并不會在意你的自尊,這世界指望你在自我感覺良好之前先要有所成就!

            常用鏈接

            留言簿(3)

            隨筆分類(32)

            隨筆檔案(32)

            相冊

            最新隨筆

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            色婷婷久久综合中文久久一本| 国产精品久久久久AV福利动漫| 久久99国产精品尤物| 99久久精品免费看国产一区二区三区| 青青国产成人久久91网| 久久99国产精品一区二区| 日韩人妻无码精品久久久不卡| 久久综合久久美利坚合众国| 2020国产成人久久精品| 欧美成人免费观看久久| 久久精品国产亚洲AV久| 亚洲国产精品无码久久一区二区 | 亚洲欧洲久久av| 午夜精品久久久久成人| 久久妇女高潮几次MBA| 日产精品久久久一区二区| 国产成人综合久久综合| 2020最新久久久视精品爱| 成人久久久观看免费毛片| 久久午夜电影网| 日韩影院久久| 性欧美大战久久久久久久久| 狠狠色丁香久久综合婷婷| 久久人妻少妇嫩草AV蜜桃| 亚洲va久久久噜噜噜久久天堂 | 麻豆AV一区二区三区久久| 精品久久久久久久久中文字幕| 国产999精品久久久久久| 伊人久久大香线蕉综合5g| 精品久久久久久国产潘金莲| 国产精品99久久久久久董美香 | 色婷婷综合久久久久中文一区二区| 成人免费网站久久久| 久久久人妻精品无码一区| 亚洲国产精品成人久久| 久久99久久成人免费播放| 久久影院综合精品| 久久久人妻精品无码一区| 91久久婷婷国产综合精品青草| 欧美精品丝袜久久久中文字幕| 狠狠色婷婷久久一区二区三区|