CSTRING Operation Guide November 05, 2004 Original Source: CODEPROJECT: CSTRING Management By reading this article You can learn how to effectively use CString. CString is a very useful data type. They greatly simplify many of the operations in the MFC, making MFCs convenient when making a string operation. In any case, there are many special skills using CString, especially for programmers coming out of pure C background. This article discusses these techniques. Using CString allows you to operate more straightforward. This article is not a complete manual of CString, but there is most common basic issues. This article includes the following: Connection format strings of CString objects (including INT transformation to cstring) CSTRING type transformation into int type CSTRING type and CHAR * Types of transformation Char * Transformation into cstract cstring into char * : Using LPCTSTR forced converting CString into char * 2: GetBuffer method using the CString object CString translates into a 3: and control interface CSTRING type converted to BSTR type; BSTR type transform into cstring type; Variant type transformation into cstring Type; load string table resource; CString and temporary objects; CString efficiency; summarize the following I discussed. 1, the connection of the CString object can reflect the connection of the string, using the cstring type, you can easily connect two strings, just as follows: CString Gray ("gray" below: cstring gray CSTRING CAT ("cat"); cstract graycat = gray cat; more than the following method: char gray [] = "gray"; char cat [] = "cat"; char * graycat = malloc Strlen (Gray) Strlen (CAT) 1); strcpy (graycat, gray); strcat (graycat, cat); 2, formatted strings to format a string with a sprintf () function or wsprintf () function It is better to use the format () method of the CString object: CString S; S.Format (_T ("Total IS% D"), TOTAL); The advantage of this method is that you don't have to worry about the formatting data Whether the buffer is large enough, these work is completed by the CSTRING class. Format is the most common technique that transforms other data that is not a string type to cString type, for example, converts an integer into a cString type, which can be used as follows: cstring s; s.format (_t ("% d") , TOTAL); I am always using the character string _t () macro, this is to make my code at least Unicode consciousness, of course, the topic about Unicode is not discussed in this article.
_T () macro is in the 8-bit character environment: #define _t (x) x // non-Unicode version (Non-Unicode Version) and in the Unicode environment: #define_t (x) L ## x // unicode version So in a Unicode environment, its effect is equivalent to: S.Format (L "% D", Total); if you think your program may run in Unicode's environment So starting in the application of Unicode encoding. For example, don't use the sizeof () operator to get the length of the string, because there will be twice the error in the Unicode environment. We can use some ways to hide some details of Unicode. For example, when I need to get the character length, I will use a macro called Dim, this macro is defined in my Dim.h file, I will write in me. All programs include this file: #define Dim (x) (SizeOf ((x)) / sizeof ((x)) / sizeof ((x)) This macro not only can be used to solve the problem of Unicode's string length, or Used on the table defined at compile, it can get the number of items in the table, as follows: Class whatver {...}; whatver data [] = {{...}, ... {...},}; For (int i = 0; i When running in a Unicode environment, you don't have to worry that the 2 operation will reduce the efficiency of the program, remember, this is just a left-shifted operation, edging 饕 饕 芾 芾 馕 婊 婊#? Br> Using the _t macro does not mean that you have created a Unicode program, you just created a program with Unicode consciousness. If you compile your program in the default 8-bit mode, you get a normal 8-bit application (8-bit refers to the 8-bit character encoding, is not 8 bits. Computer system); when you compile your programs in a Unicode environment, you get a Unicode program. Remember, CString In the Unicode environment, it can be 16-bit characters. 3, CSTRING Type Transformation into an INT type to convert the data of the CString type into an integer type The simplest method is to use standard strings to integer conversion routines. Although you usually doubt using the _atoi () function is a good choice, it is rarely a correct choice. If you are ready to use Unicode characters, you should use _ttoi (), which is compiled into _atoi () in the ANSI coding system, and compiles _wtoi () in the Unicode encoding system. You can also consider using _tcstoul () or _tcstol (), they can convert the string into any long intensity of arbitrary evolution (such as binary, octal, decimal or hexadecimal), and different points lies after transformation of the former The data is unsigned, and the latter is the opposite. Look at the following example: cstring hex = _t ("fab"); cstring decimal = _t ("4011"); assert (_tcstoul (hex, 0, 16) == _ttoi (decimal)); 4, cstring and char * Types of mutual transformation This is the most common problem when beginners use CString. With the help of C , many questions you don't need to consider it, take it directly, but if you can't understand its running mechanism, there will be a lot of problems to make you confused, especially some seem to have no The code of the problem is not working properly. For example, you will be strange why you can't write the code below: CString graycat = "gray" "cat"; or this: cstring graycat ("gray" "cat"); in fact, the compiler will complain the above These attempts. why? The " " operator is defined as an overload operator for a wide variety of combinations of CString and LPCTSTR data types. Instead of two LPCTSTR data types, it is the underlying data type. You cannot overload C operators for basic data (such as int, char or char *) type. You can do this below: cstring graycat = cstring ("gray") CString ("cat"); or this: cstring graycat = cstring ("gray") "cat"; research will find: " "Always use at least one CString object and an LPCSTR. Note that the code written in Unicode is always a good thing, such as: cstring graycat = cstring (_t ("gray")) _t ("cat"); this will make your code directly transplant. Char * Convert to CString Now you have a char * type data, or a string. How to create a CSTRING object? Here are some examples: char * p = "this is a test"; or like this more unicode aware: tchar * p = _t ("this is a test") or lptstr p = _t ("this is a test" ); You can use any of the following ways: 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); use these methods to easily convert constant strings or pointer conversion CSTRING. It should be noted that the assignment of characters is always copied to the CString object, so you can do this below: tchar * p = _t ("gray"); cstring s (p); p = _t ("Cat "); s = p; result string is definitely" graycat ". There are several other constructor, but we don't consider it, if you are interested, you can view the relevant document yourself. In fact, the constructor of the CSTRING class is complicated than I show, such as: cstring s = "this is a test"; this is a hugger code, but in fact it can be compiled in the Unicode environment. It converts the 8-bit string into a 16-bit string at runtime. Anyway, if the char * pointer is 8-bit data transmitted on the network, this conversion is useful. CString is converted to one of Char *: Mandatory types are converted to LPCTSTR; this is a slightly hardened conversion. There are still many confused, and there are many correct ways, but the use of mistakes Methods may be as much as the correct use. We must first learn that CString is a very special C object, which contains three values: a pointer to a data buffer, one is a valid character count in this buffer and a buffer length. The size of the effective character may be any number of 0. 0 to this buffer maximum length value (because the string ends with a NULL character). The character count and the buffer length are smartly. Unless you do some special operations, you can't know the length of the buffer assigned to the CString object. In this way, even if you get the 0 buffer address, you can't change the content, you can't truncate the string, and there is absolutely no way to extend its content, otherwise it will see overflow. The LPCTSTR operator (or more specifically that tchar * operator) is overloaded in the CSTRING class, the definition of the operator is the address of the buffer, so if you need a string pointer pointing to CString, Can do this: CString S ("graycat"); lpctstr p = s; it can run correctly. This is achieved by the Cultural Cultural Circulation Rules. When the mandatory type is required, C rules allow this option. For example, you can define (floating point) to convert a plural (a pair of floating point numbers), only returning the first floating point number (which is actually). You can like this: Complex C (1.2F, 4.8F); float realpart = c; if the FLOAT operator defines the correct correct, the value of the real part should be 1.2. This forced transformation is suitable for all this, for example, any function with the LPCTSTR type parameter enforces this conversion. So you may have such a function (maybe in a DLL you bought): Bool Dosomethingcool (LPCTSTSTS); you call it like this: CString File ("c: // myfiles // coolstuff") BOOL Result = DOSMETHINGCOOL (File); it can run correctly. Because the DOSMETHINGCOOL function has explained a parameter that requires an LPCTSTR type, LPCTSTR is applied to this parameter, and in the MFC is the string address returned. What if you want to format a string? CString graycat ("graycat"); cstring s; s.format ("MEW! I Love% S", graycat; note due to the value in the variable parameter list (in function description "..." The) does not imply a forced type conversion operator. What will you get? A surprising result, the actual result string we get is: "MEW! I Love Graycat". Because the MFC designers are very careful when designing the CSTRING data type, the cstring type expression begins to point to the string after evaluation, so you can't see the mandatory type conversion in Format or Sprintf, you can still get the right behavior. . Describe the additional data of CString is actually after the CString name address. One thing you can't do, that is to modify the string. For example, you may try to use "," instead "." (Don't do this, if you care about the internationalization, you should use the National Language Support feature of decimal conversion,), below is a simple example: cstring v (" 1.00 "); // Money amount, two decimal lpctstr p = v; p [lstrlen (p) - 3] =,; then the compiler will report an error because you assign a constant string. If you try the following, the compiler will also be wrong: STRCAT (P, "EACH"); because Strcat's first parameter should be the LPTSTR type data, but you give an LPCTSTR. Don't try to diamond this error message, this will only make you in trouble! The reason is that the buffer has a count, which is an unbearable (it is located in a hidden area under the cstring address), if you change this string, the buffer is not reflected in the modification. In addition, if the string length is exactly the length of the physical limit of the string (this problem will also be said), then the string will override any data other than the buffer, which is the memory you have no right to write. Not right?), You will destroy the change is not your memory. This is the real death prescription of the application. CSTRING translates into char * 2: Use a getBuffer method for the CString object; if you need to modify the content in the cstring, it has a special method to use, which is getBuffer, its role is to return a writable buffer pointer. If you just want to modify characters or truncated strings, you can do this: CString S ("File.ext"); lptstr p = s.getBuffer (); lptstr dot = strchr (p,.); // OK, Should Have Used S.Find ... if (p! = Null) * p = _t (/ 0); s.releasebuffer (); this is the first use of getBuffer, is also the simplest No need to deliver parameters, it uses default 0, meaning: "Give me this string pointer, I guarantee that it is not lengthened." When you call ReleaseBuffer, the actual length of the string is recalculated and then stored in the CString object. It must be emphasized that this range between GetBuffer and ReleaseBuffer must not use any method of this buffered CString object you want to operate. Because ReleaseBuffer is called, the integrity of the CSTRING object is not guaranteed. Take the following code: cstring s (...); lptstr p = s.getBuffer (); // ... This pointer P has a lot of things int n = s.GetLength (); // is very bad D !!! !! It is possible to give a wrong answer !!! s.trimright (); // is very bad !!!!! Can not guarantee normal work !!!! s.releaseBuffer (); // should now be OK INT m = S.GetLength (); // This result can be guaranteed correctly. S.trimright (); // will work normally. Suppose you want to increase the length of the string, you first have to know how long this string may, comparison is when the string array: char buffer [1024]; indicates 1024 characters space enough to make you do anything to do Have something. A representation equal to the meaning of CString: lPTSTR P = S.GetBuffer (1024); After calling this function, you not only get the pointer of the string buffer, but also space for at least 1024 characters in length. (Note, I am talking about "characters" instead of "bytes" because cstring is implied by Unicode). At the same time, it should also be noted that if you have a constant string pointer, the value of this string itself is stored in read-only memory, if you try to store it, even if you have called GetBuffer, get a pointer to read-only memory, The deposited operation will fail and report an access error. I didn't prove this on CString, but I saw that the big C programmer often made this mistake. C programmer has a common problem that assigns a fixed length buffer that performs sprintf operation, then assigns it to a cstring: char buffer [256]; sprintf (buffer, "% ...", args, ...); // ... part omit a lot of details CString s = Buffer; although better forms can be said: cstring s; s.format (_T ("% ..."), args, .. If your string length exceeds 256 characters, the stack will not be destroyed. Another common mistake is: Since the fixed size memory does not work, then dynamically allocate bytes, this approach is larger: 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); cstract s = buffer; ... delete [] buffer; it can be Simply write: CString S; S.Format (_T ("% s is equals to% s, valid data), PARM1, PARM2); Need to note that the sprintf example is not unicode ready, although you can use Tsprintf and use _ T () to enclose the formatted string, but basic ideas is still walking, this is easy to make mistakes. CSTRING TO Char * 3: And the interface of the control; we often need to pass a CString value to a control, such as CTreeCtrl. MFC provides us with a lot of convenience to overload this operation, but in most cases, you use the "original" form update, so you need to store a string pointer to the TVItemstruct structure of the TVInsertItemstruct structure. As follows: TVINSERTITEMSTRUCT TVI; cstring s; // ... is assigned to S. TVi.Item.psztext = s; // compiler Yells at you here // ... Fill in the other domain of TVi HtreeItem Ti = C_Mytree.InsertItem (& TVi); Why will the compiler error? It is a perfect usage! But in fact, if you look at the definition of the TVItem structure, you will understand that in the TVitem structure, the statement is as follows: lptstr psztext; int cchtextmax; therefore, the assignment does not assign a variable that is paid to an LPCTSTR type, and the compiler cannot know how to know how The right side of the assignment statement is enforced into LPCTSTR. Ok, you said, then I will change to this: tvi.Item.psztext = (lpctstr) s; // The compiler will still report. The compiler is still an error because you attempt to assign a variable of a LPCTSTR type to a LPTSTR type variable, which is disabled in C or C . You can't use this method to abuse constant pointers and very quantitative pointer concepts, otherwise, you will disturb the optimization mechanism of the compiler, so that you don't know how to optimize your procedure. For example, if you do this: const I = ...; // ... do lots of stuff ... = a; // usage 1 // ... LOTS more stuff ... = a; // Usage 2, then the compiler will consider whether I is const, so the value of USAGE1 and USAGE2 is the same, and it can even calculate the address of the AAGE1 in advance, and then keep it in the next USAGE2 instead of re-re-re- Calculate. If you are written as follows: const I = ...; int * p = & i; // ... do lots of stuff ... = a; // usage 1 // ... LOTS More Stuff * P) ; // message over compilers assumption // ... and other stuff ... = a; // usage 2 The compiler will think I is constant, so that the position of A is constant, which is indirectly destroyed. Previous assumptions. Therefore, your program will reflect different behaviors in debug compilation mode (not optimized) and release compilation mode (fully optimized), this situation is not good, so when you try to assign a pointer to i to a modification When the reference is quoted, it will be diagnosed with the compiler to this is a forgery. That's why (LPCTSTR) mandatory reasons do not work. Why don't you declare this member into a lpctstr type? Because this structure is used to read and write controls. When you write data to the control, the text pointer is actually treated as LPCTSTR, and when you read the data from the control, you must have a writable string. This structure cannot distinguish it is used to read or use it. So you will often see the following usage in my code: TVi.Item.psztext = (lpctstr) s; it converts the CString mandatory type into lpctstr, that is, the address of the change string first, Then forced the type to translate into LPTSTR to assign it to it. Note that this is only valid in the method such as SET or INSERT! If you try to get data, you can't do this. If you plan to get the data stored in the control, the method is slightly different, for example, using the GetItem method for a CTREECTRL, I want to get the text of the project. I know that the length of these texts will not exceed my_limit, so I can write: TVItem TVi; // ... assorted initialization of other fields of tvitvi.psztext = s.getBuffer (my_limit); tvi.cchtextmax = my_limit; c_mytree. GetItem (& TVi); S.ReleaseBuffer (); It can be seen that the above code is applicable to all types of SET methods, but do not need to do it, because all class set methods (including Insert methods) do not change characters String content. But when you need to write a CString object, you must ensure that the buffer is writable, this is what getBuffer does. Again again: Once a getBuffer call is made, do not do anything about this CString object before calling ReleaseBuffer. 5, CString type Transformation into a BSTR type When we use the ActiveX control programming, you often need to use a value as a BSTR type. BSTR is a string, a wide string (Unicode) on the Intel platform, and can contain embedded NULL characters. You can call the unlocsystring method of the CString object to convert the cstring into bstr: cstring s; s = ...; // whatverbstr b = s.allocsystring (); now pointer B pointing is a newly allocated BSTR object, the object is A copy of CString contains the termination null character. Now you can pass it to any interface that requires BSTR. Typically, BSTR is released by the components that receive it. If you need to release BSTR, you can do this: :: sysfreestring (b); for how to express the string passing to the ActiveX control, I have argued that I have argued in Microsoft, and finally Visual Basic's people accounted for the wind, BSTR ("Basic String"'s first letters) is the result of this debate. 6, BSTR type transformation into a CString type Since BSTR is a decimal unicode string, you can create 8-bit CString with a standard conversion method. In fact, this is a CString built-in function. There is a special constructor in CString to convert the ANSI to Unicode or convert Unicode into ansi. You can also get the BSTR type string from the variant type variable, and the Variant type is the type returned by various COM and Automation (Automation). For example, in an ANSI program: BSTR B; b = ...; // wherevercstring s (b == NULL? L "": b) This usage can work very well for a single BSTR string. This is because cstring has a special constructor as a parameter in lpcwstr (BSTR is this type) and transforms it into an ANSI type. Special inspection is necessary, because the BSTR may be null, and the CString constructor is not very thoughtful for NULL values, (thank Brian Ross to point out this!). This usage can only handle a single string containing NUL termination characters; if you want to convert multiple NULL strings, you have to do some work. NULL characters embed in CString usually do not agree, should be avoided as much as possible. According to C / C rules, if you have a LPWSTR, then it doesn't choose, you can only match the LPCWSTR parameter. In Unicode mode, its constructor is: cString :: CString (LPCTSTS); as indicated above, in ANSI mode, it has a special constructor: cstring :: cstring (lpcwstr); it will call one The internal function converts the Unicode string into an ANSI string. (In Unicode mode, there is a dedicated constructor, which has a parameter is a LPCSTR type - a 8-bit ANSI string pointer, which is widened to Unicode string!) Emphasis: Be sure to check Is the value of BSTR is NULL. There is another problem, as mentioned above: BSTRS can contain multiple embedded NULL characters, but the CString constructor can only handle a single NULL character in a string. That is, if the string contains embedded NUL bytes, CString will calculate the bundle string length. You must handle it yourself. If you look at the constructor in strcore.cpp, you will find that they all call lstrlen, which is the length of the string. Note that from Unicode to ANSI conversion uses special parameters: WideChartomultibyte, if you don't want to use this default conversion method, you must write your own conversion code. If you compile the code in Unicode mode, you can simply write: CString Convert (BSTR B) {if (b == null) Return CString (_t (")); cstring s (b); // in Unicode ModeReturn S;} If it is an ANSI mode, a more complex process is required to convert. Note this code uses the same parameter value as: widechartomultibyte. So you can only use this technology when you want to change these parameters. For example, specify different default characters, different logos sets, and the like. CString Convert (BSTR B) {CSTRING S; if (b == null) Return S; // Empty for null bstr # ifdef unicodes = B; #ELSLPSTR P = S.GetBuffer (Systringlen (b) 1); :: WideCharToMultiByte (CP_ACP, // ANSI Code Page0, // no flagsb, // source widechar string-1, // assume NUL-terminatedp, // target bufferSysStringLen (b) 1, // target buffer lengthNULL, // use system Default Charnull; // DONT CARE IF DEFAULT Useds.releaseBuffer (); # endifreturn s;} I don't worry what happens if the BSTR contains Unicode characters that are not mapped to the 8-bit character set, because I specified :: WideChartomultibyte The last two parameters are NULL. This is where you may need to change. 7. Varian type is transformed into a CSTRING type in fact, I have never done it, because I have not written in the process of COM / OLE / ActiveX. But I saw a post on the Microsoft.Public.vc.mfc newsgroup to talk about this conversion, I think it is not very good to put his article in my article, so more here Do some explanations and demonstrations. If there is a place with his article, it may be my negligence. Variant types are often used to deliver parameters to COM objects or receive values returned from COM objects. You can also write it back to the Variant type method, the function returns what type of input parameters depend on the possible (and often) method (for example, in the automation operation, depending on which method is called with you, which method can be returned (by one Parameters) A result containing Byte, Word, Float, Double, Date, BSTR. (For details on the definition of the Variant structure on the MSDN). In the following example, assuming type is a BSTR variant, That is to say, the value in the string is referenced by BSRTVAL. It has the advantage that in the ANSI application, there is a constructor to convert the value of the LPCWCHAR reference to a cString (see the BSTR-to-CString section). In Unicode mode It will become a standard CString constructor, see the warning of the default :: widechartomultibyte conversion, and you feel acceptable (in most cases, you will be satisfied) .variant vadata; vadata = m_com.Yourmethodhere (); askERT (VADATA.VT == Vt_BSTR); CString strdata (Vadata.bstrval); you can also establish a more common conversion routine according to the VT domain. To this end you may consider: 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; ... The remaining type conversion by the reader's own DEFAULT: Assert (false); // unknown variant type (this assert is optional) Return CString (" "") } / * vt * /} 8, load the word String table resource If you want to create an app that is easy to log in, you cannot directly include local language strings in your source code (below, the language I use is English, because my local language English), such as this kind of writing is a bad: cstring s = "there is an error"; you should put all the strings of all your specific languages (except for the information that does not appear in the release version) . This means that it is better to write down: s.format ("% D -% s"), code, text); In your program, text strings are not language sensitive. In any case, you have to be very careful, don't use the string of the following: // FMT IS "Error In% S File% S" // readorwrite is "Reading" or "Writing" S.Format (FMT, ReadorWrite, FileName); This is my personal experience. In my first international app, I made this mistake. Although I understand German, I know that in German grammar is placed in the end of the sentence, our German issuers are still suffering from them. Have to extract those incredible German error prompts and restrictions to make them work normally. A preferred way (also the way I use now) is to use two strings, one for reading, one for writing, load the appropriate version when using, so that they are non-sensitive to string parameters. That is to say, the entire format is loaded, not the load string "Reading", "Writing": // FMT IS "ERROR IN Reading File% S" // "Error In Writing File% S" S.Format (FMT, FileName); Be sure that if you need to replace several places, you must ensure that the structure of the replacement sentence does not have problems, such as in English, can be the subject - object, subject - predicate, verb-object structure, etc.. Here, we don't discuss formatmessage, in fact it has advantages over Sprintf / Format, but it is not easy to use CString. The way to solve this problem is that we will give the parameters in the parameter table in accordance with the parameters, so that they will not take their position wrong when you output it. Next we discuss us for these independent strings. We can put the value of the string into a section called StringTable in the resource file. The process is as follows: First, use the Visual Studio's resource editor to create a string, then give each character to a ID, usually we take the name of the name to start with IDS_. So if you have a message, you can create a string resource and name it IDS_READING_FILE, and the other is named IDS_Writing_File. They appear in your .rc file below: StringTableIDS_Reading_File "Reading File% S" IDS_Writing_File "Writing File% S" END Note: These resources are saved in Unicode's format, whether you compile it in what environment. They also exist in the form of Unicode in the Win9x system, although Win9x cannot really process Unicode. Then you can use these resources like this: // Before using the resource string table, the program writes this: cstring fmt; if (...) fmt = "reading file% s"; elsefmt = "Writing file% s"; ... // Much Latercstring S; S.Format (FMT, FileName); // After using the resource string table, the program writes this: cstring fmt; if (...) fmt.loadstring (ids_reading_file); elsefmt.LoadString DS_Writing_File; ... // Much Latercstring S; S.Format (FMT, FileName); Now, your code can be ported to any language. The LoadString method requires the ID of a string resource as a parameter, then it takes out its corresponding string from the StringTable to assign the value to the CSTRING object. The constructor of the CString object also has a smarter feature to simplify the use of StringTable. This usage is not pointed out in the CString :: CString document, but is used in the sample program of the constructor. (Why did this characteristics have become part of a formal document, but in an example, I can't remember!) - [Translator Note: From this sentence, the author may be a CString designer. In fact, there is still a similar thing in front. Said that he did not have a validity check to the address pointing to using getBuffer (0). This feature is: If you convert a string of resources to LPCTSTR, you will hibernate to call LoadString. Therefore, the following two configuration strings have the same effect, and its assert will not be triggered in Debug mode: CString S; S.LoadString (IDS_WHATEVER); CSTRING T ((LPCTSTR) IDS_WHATEVER; assert (s = = T); // will not be triggered, and S and T are the same. Now you may think: How can this work? How can we convert the StringTable ID into a pointer? Very simple: all string IDs are within 1 ~ 65535, that is, all of its highs are 0, and the pointer we used in the program is not less than 65535, because the program is 64K Memory will never exist. If you try to access the memory between 0000000000000000000FFF, a memory base error will be triggered. Therefore, the value of 1 ~ 65535 cannot be a memory address, so we can use these values as the ID of the string resource. I tend to use the MakeintResource macro to make this conversion. I think this can make the code easier to read. This is a standard macro that is only suitable for use in MFC. You have to remember that most methods can accept a UINT type parameter, or accept one LPCTSTR type parameter, which is done depending on the overload function of C . The drawbacks brought by the C overload function are to cause all mandatory types of transformations that need to be displayed. Similarly, you can only deliver a resource name for many structures. CString S; S.LoadString (IDS_WHATEVER); CString T (MakeintResource (IDS_WHATEVER)); assert (s == t); tell you: I am just advocating here, but I do this. In my code, you almost impossible to find a string, of course, except those that are just accidentally or unrelated to the language in debugging. 9, cstring and temporary objects This is a small problem in the Microsoft.Public.vc.mfc newsgroup, I simply mention, this problem is a programmer who needs to write a string in the registry, he Write: I tried to use regsetValueex () to set the value of a registry key, but its result is always confused. When I declare a variable when I use a variable, it works fine, but when I use CString, I always get some garbage: "YYYY ... YYYYYY" For confirmation, I have a problem, I tried Use GetBuffer and then force to convert to CHAR *, LPCSTR. GetBuffer returned to the correct, but when I assigned it to char *, it turned garbage. The following is my block: char * szname = getName (). GetBuffer (20); RegSetValueex (HKEY, "Name", 0, REG_SZ, STRLEN (SZNAME 1)); this Name character The length of the string is less than 20, so I don't think it is a problem with the parameters of getBuffer. Really confused, please help me. Dear Frustrate, you make a quite subtle mistake, smart and smart, correct code should be like this: cstring name = getName (); RegSetValueex (HKEY, _T ("name"), 0, reg_sz, Const byte *) (LPCTSTSTR) Name, (Name.getLength () 1) * Sizeof (tchar)); why do I write the code? Do you have a problem? Mainly because the CString object returned when you call GetName is a temporary object. See: "C Reference Manual" §12.2 In some environments, the compiler is necessary to create a temporary object, which introduces the temporary object depending on the implementation. If this temporary object to which the compiler is introduced, the compiler ensures that the constructor of this class is called. Similarly, if this class declares that there is a destructor, it is also necessary to ensure that the destructor of this temporary object is called. The compiler must ensure that this temporary object is destroyed. The exact location destroyed by the destruction depends on implementation ..... This destructor must be called before exiting the range of the temporary object. Most compilers are designed: In the next execution step of the code created by the temporary object, I hidden the destructive function of this temporary object, which is realized, usually at the next semicolon. Therefore, this CString object is destructed after getBuffer calls (by way of manner, you have no reason to pass a parameter for the getBuffer function, and do not use ReleaseBuffer). So GetBuffer returned to the pointer to the address of the string in this temporary object, but after this temporary object is destructed, this memory is released. Then the MFC's debug memory distributor will be re-filled with this memory all 0xDD, which is displayed just "Y" symbol. At this time you write data to the registry, the content of the string is of course being ruined. We should not immediately transform this temporary object into a char * type. You should save it into a CString object, which means copying the temporary object, so after the temporary CSTRING object is destructed, this cstring The values in the object are saved. There is no problem to write data to the registry in the registry. In addition, my code is Unicode consciousness. The function of the operation registry requires a byte size. The actual result obtained by using lstrlen (Name 1) is half a half compared to the ANSI character than the ANSI character, and it cannot start from the second character of this string. Calculate, maybe your intention is lstrlen (name) 1 (OK, I admit that I also made the same mistake!). No matter how, in Unicode mode, all characters are 2 bytes, and we need to handle this problem. Microsoft's document is surprisingly keeping silent: REG_SZ is calculated in byte calculation with byte calculations? We assume that it is calculated in bytes, and you need to make some modifications to your code to calculate the byte size contained in this string. 10, CString's efficiency CString is a problem that it does hide some low efficiency. From another aspect, it can also be achieved more efficient, you may say the following code: cstring s = somecstring1; s = somecstring2; s = SomeCstring3; s = ","; s = SomeCstring4 Compared to the following code, the efficiency is much lower: Char S [1024]; LSTRCPY (S, SomeString1); LSTRCAT (S, SomeString2); LSTRCAT (S, SomeString 3); lstrcat (s, ") LSTRCAT (S, SomeString4); in short, you may want, first assign a memory for someCString1, then copy someCString1 to the inside, then find it to be a connection, reassign a new enough big memory, Large to the current string plus someCString2, copy the content to this memory, then connect someCString2 to the back, then release the first block, and re-point the pointer to the new memory. Then repeat this process for each string. How is more efficient to connect these four strings. In fact, in many cases, there is no need to copy the source string (the string on the left side of the = operator). In VC 6.0, in Release mode, the cache in all CString is allocated in predefined quantum. The so-called quantum is determined to be 64, 128, 256 or 512 bytes. This means unless the string is very long, the operation of the connection string is actually the optimized version (because it knows where the local string should end, so you don't need to look for the end of the string; just need memory The data is copied to the specified place to add the length of the recalculation string. So it's the same efficiency and pure C code, but it is easier to write, it is easier to maintain and easier to understand. If you still can't make it possible to find what happen, please see the source code of CString, strcore.cpp, in the MFC / SRC subdirectory of your VC98 installation directory. Take a look at the Concatinplace method, which is called in all = operators. Ah! Is CSTRING really "efficient"? For example, if I create CString Cat ("Mew!"); Then I am not getting a highly efficient, streamlined 5-byte size buffer (4 characters plus an end character), the system will assign me 64 One byte, and 59 bytes were wasted. If you think about it, please have to accept re-education. It may be a good thing for someone who may tell you at a place. Yes, this statement is indeed correct, but he ignores a very important aspect of the facts. If you have written embedded programs running under 16K EPROMS, you have reason to use it less, in this environment, it makes your program more robust. But write Windows programs on a 500MHz, 256MB machine, if you still do it, it will only run even worse than the "inefficient" code you think. for example. The size of the string is considered to be the primary factor affecting efficiency, making the string to improve efficiency as little as possible, and vice versa, which is the consistent idea. But this idea is not right, the consequence of the exact memory allocation is to reflect after the program is running for several hours. At that time, the plurality of programs will be filled with the memory, they are too small to do it. Anything, but they add your program's memory, increase the number of memory pages exchange, increase the number of page exchanges to the upper limit of the system to endure, the system will allocate more pages for your program until you The program takes up all available memory. It can be seen that although memory debris is a secondary factor in determining efficiency, it is precisely that these factors actually control the behavior of the system. In the end, it harms the system reliability, which is unacceptable. Remember, in Debug mode, memory is often precisely allocated, which is for better troubleshooting. Suppose your app usually requires a few months. For example, I often open VC , Word, PowerPoint, FrontPage, Outlook Express, Forté Agent, Internet Explorer, and other programs, and usually do not close them. I have worked for a few days in the evening, and I have to use PowerPoint for a few days (contrary. If you are unfortunate, you will experience the reliability of reliability; this program will collapse 4 ~ 6 times every day Every time because all spaces are used and fill all my exchange pages). Therefore, precise memory allocation is not desirable, it will endanger the system reliability and cause the application to crash. The multiples of quantum are allocated to the string, and the memory distributor can recover the used memory blocks, usually these recycled memory blocks can be reused by other CString objects so that they can guarantee minimal fragmentation. The function of the dispenser is enhanced, and the memory used by the application can be as small as possible, such a program can run a few weeks or months without problems. Exterior: Many years ago, when we wrote a interactive system in CMU, some research on memory distributors showed that it often produced many memory fragments.