CString、LPCTSTR、LPTSTR、TCHAR、WCHAR、string、wchar_t、char解析

[转] https://www.cnblogs.com/magic-cube/archive/2011/05/12/2044713.html
一.类型简介
1.CString:动态的TCHAR数组。它是一个完全独立的类,封装了“+”等操作符和字符串操作方法,换句话说就是CString是对TCHAR操作的方法的集合。

2.LPCTSTR:常量的TCHAR指针,其定义为

typedef const TCHAR* LPCTSTR
其中

 L表示long指针 这是为了兼容Windows 3.1等16位操作系统遗留下来的,在win32中以及其他的32位操作系统中, long指针和near指针及far修饰符都

   是为了兼容的作用。没有实际意义。

P表示这是一个指针

C表示是一个常量

T表示在Win32环境中, 有一个_T宏

STR表示这个变量是一个字符串

2.LPTSTR:TCHAR指针,其定义为

typedef TCHAR* LPTSTR
L、P、T的含义同上。

3.TCHAR:TCHAR其实是一个宏,其定义为:

#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif
也就是说,如果我们使用unicode编码,那么TCHAR指的就是wchat_t,如果我们使用的是ASCII编码,那么TCHAR指的就是char,这样处理的目的是为了程序的可移植性。T表示在Win32环境中, 有一个_T宏 。

4.WCHAR:WCHAR其实也是一个宏,表示的就是wchar_t,为了书写方便重新定义的一个宏而已,其定义为:

typedef wchar_t WCHAR
5.string:string是c++中的字符串变量,因为操作c类型的char非常麻烦,而且很容易出现内存泄漏,所以c++就对c中的char 进行了封装,其中

包含了赋值、删除、增加等常用操作,这些操作都不用考虑内存,是的使用更加方便,所以能使用string就尽量使用string,使用string要包含其头文件:

#include

注意不是:

#include <string.h>
因为string.h是C字符串头文件。

string中常用的函数有:

a) =,assign() //赋以新值
b) swap() //交换两个字符串的内容
c) +=,append(),push_back() //在尾部添加字符
d) insert() //插入字符
e) erase() //删除字符
f) clear() //删除全部字符
g) replace() //替换字符
h) + //串联字符串
i) ==,!=,<,<=,>,>=,compare() //比较字符串
j) size(),length() //返回字符数量
k) max_size() //返回字符的可能最大个数
l) empty() //判断字符串是否为空
m) capacity() //返回重新分配之前的字符容量
n) reserve() //保留一定量内存以容纳一定数量的字符
o) [ ], at() //存取单一字符
p) >>,getline() //从stream读取某值
q) << //将谋值写入stream
r) copy() //将某值赋值为一个C_string
s) c_str() //将内容以C_string返回
t) data() //将内容以字符数组形式返回
u) substr() //返回某个子字符串
v)查找函数
w)begin() end() //提供类似STL的迭代器支持
x) rbegin() rend() //逆向迭代器
y) get_allocator() //返回配置器

6.wchar_t:wchar_t是c++中用来表示宽字节的数据类型,即unicode编码的数据类型。

7.char:char是c中的字符数据类型,属于ASCII编码。

下面是msdn上给出的定义:

type Meaning in MBCS builds Meaning in Unicode builds

WCHAR wchar_t wchar_t
LPSTR char* char*
LPCSTR const char* const char*
LPWSTR wchar_t* wchar_t*
LPCWSTR const wchar_t* const wchar_t*
TCHAR char wchar_t
LPTSTR TCHAR* TCHAR*
LPCTSTR const TCHAR* const TCHAR*
二.相互转化

既然有定义了这么多的数据类型,所以他们之间的相互转化是少不了的。

A):CString的转化

1.CString和LPCTSTR的转化:

CString和LPCTSTR不需要转化,两者是等价的,所以:

CString str(“cstring”);
LPCTSTR pcStr = str;
2.CString和LPTSTR的转化:

下述转法虽然可以,但是却不安全:

CString str(“string”);
LPTSTR pStr = (LPTSTR)(LPCTSTR)(str);
因为本来转化后的字符串变得可以修改了,造成了安全隐患。

正确的转化方法为:

CString str(“string”);
LPTSTR pStr = str.GetBuffer();
str.ReleaseBuffer();

注意:GetBuffer()和ReleaseBuffer()之间不可以调用任何的CString函数,比如GetLength()函数,因为无法预测对内存的操作,所以任何CString函数得到的

结果都是不确定的。

3.CString和WCHAR(wchar_t)的转化

方法一:使用wcscpy()函数

CString str(“string”);
WCHAR pWchar[100];
wcscpy(pWchar,str);
方法二:使用wcscpy_s()函数

这个函数是上一个函数的安全版本,调用上一个函数如果pWchar的内存不足时,容易引发意味的错误,但是wcscpy_s()则不会,应该其内存大小已经指定出来了:

CString str(“string”);
WCHAR pWchar[100];
wcscpy(pWchar,100,str);
方法三:使用_tcscpy()函数

CString str(“string”);
WCHAR pStr[100];
_tcscpy(pStr,str);
方法四:使用_tcscpy_s()函数

同wcscpy_s()一样,_tcscpy_s()函数也是_tcscpy()函数的安全版本:

CString str(“string”);
WCHAR pStr[100];
_tcscpy_s(pStr,100,str);

4.CString和char*的转化

方法一:使用wcstombs()函数

CString str(“string”);
char pChar[100];
wcstombs(pChar,str,100);
方法二:使用wcstombs_s()函数

同上面一样,wcstombs_s()是wcstombs()的安全版本:

总结:

  上面一会使用strcpy(),一会使用wcscpy(),一会又使用_tcscpy(),这三者有什么关系呢,其实strcpy()处理的就是ASCII编码的字符,像char,而wcscpy()处理的是Unicode 编码,_tcscpy()则是一个宏,如果你使用的是ASCII编码,那么_tcscpy()表示的就是strcpy(),如果你使用的是Unicode编码,那么_tcscpy()表示的就是wcscpy(),这可以通过定义_UNICODE或UNICODE宏来实现。你可能已经知道了为什么要定义这么一个宏,对!就是为了代码的移植。还有一个函数就是wcstombs(),这个函数是干什么用的呢?其实除了Unicode编码,还有一个编码,那就是多字节编码,通常用的是双字节编码,vc就支持这种编码,函数wcstombs()就是为了实现多字节和单字节转换而设计的。

获得系统版本号

获得系统版本号
OSVERSIONINFO m_osv;
memset(&m_osv,0,sizeof(m_osv)); //
m_osv.dwOSVersionInfoSize = sizeof(m_osv);//在调用函数之前先初始化结构的大小
GetVersionEx(&m_osv);//

BOOL isWinNT6X(){return m_osv.dwMajorVersion >= 6;}

第七日 母の日と父の日

A:もうすぐ母の日です。今年は日本にいないから、ネットで母へのプレゼントを注文します。
B:日本の母の日はいつですか?
A:5月の第2日曜日が母の日です。
B:そうですか。どんなプレゼントをしますか。
A:やはり定番の赤いカーネーションとメッセージカードですね。
B:へー、それが定番ですか。
A:ええ、母の日といえば、子どもが母親に感謝(かんしゃ)の気持ちを込めて、プレゼントやメッセージを贈る(おくる)日で、赤いカーネーションというのが昔からのイメージですが、最近はアジサイやクレマチス、バラといったその季節ならではのものも人気となっています。
B:そうですか。予算はだいたいいくらですか。
A:まあ、普通は3000円から5000円ぐらいですね。また、最近は体験型(たいけんがた)のギフトも人気があるらしいです。
B:体験型のギフトって?
A:たとえな温泉旅行、エステ、クルージングなどのようなものですね。
B:なるほど。いい思い出にもなりますね。ところで、父の日は?
A:5月の母の日に対し、6月の第3日曜日が「父親への尊敬(そんけい)と感謝の気持ちを伝える日」として父の日に制定されました。よく贈られるプレゼントはお酒やネクタイなどです。しかし、母の日は9割の人がプレゼントを贈っているのに対して、父の日は7割ぐらいらしいです。
B:そうですか。父の日は忘れがちかな。

每日十句(2)

1、辞書を使わずに日本語の新聞を読むことができますか?
2、うちの子供は野菜を食べたがらなくって困っているんですよ。
3、妹は最初に「水泳教室に行きたくない」と嫌がっていたが、今でも友達できて、楽しいに通っている。
4、友達の話では新しい辞書はとてもいいそうです
5、ポケットから財布が落ちそうだよ。
6、私は中田さんのような優しい人が好きです。
7、みんなが子供のように元気に歌い始めた。
8、二人は来年結婚するらしいです。
9、今日は涼しくて、秋らしい天気です。
10、あの雲の形はまるで馬みたいだね。

深入浅出MFC-笔记

WinSDK

Win32程序开发流程
windows程序分为【程序代码】和【UI(User Interface)资源】两大部分,两部分最后以RC编辑器整合为一个完整的EXE文件。所谓UI资源是指功能菜单,对话框外貌,程序图标,光标形状等等东西。这些UI资源的实际内容(二进制代码)系借助各种工具产生,并以各种扩展名存在,如.ico, .bmp, .cur等等。程序员必须在一个所谓的资源描述档(.rc)中描述它们。RC编辑器(RC.EXE)读取RC档的描述后将所有UI资源档集中制作出一个.RES档,再与程序代码结合在一起,这才是一个完整的Windows可执行档。

C++操作注册表

//*****************************************************************************
// 関数名 :GetUseNetwork()
// 概略 :前回変更したネットワークアダプタ情報の取得
//*****************************************************************************
int IPMessage::GetUseNetwork(TCHAR szUseNetwork[256])
{
//初期化
long lRet = 0;
HKEY hKey; // レジストリキーオープン用キー・ハンドル
TCHAR szKeyName[256]; // レジストリパス

//レジストリ「SOFTWARE\\×××\\OPOSPLUS\\Administrator\\」で取得します。
lstrcpy(szKeyName, PROFILE_KEY4);
lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_ALL_ACCESS, &hKey);

if(lRet == ERROR_SUCCESS)
{
    //変数
    UINT    cchValue = 256;
    long    lDataLen = cchValue * sizeof(TCHAR);
    UINT    cbData(lDataLen);
    DWORD   cb(cbData);
    TCHAR   szVer[256];
    LPBYTE  pbData = (LPBYTE)szVer;
    DWORD   dwType(REG_SZ);
    CString strNetworkname ;
    lRet = ::RegQueryValueExA(hKey, "UseNetworkCard", NULL, &dwType, pbData, &cb);
    if(lRet ==ERROR_SUCCESS)
    {
        strNetworkname = pbData;
        //前回変更したアダプタ名を保存します。
        lstrcpy(szUseNetwork, strNetworkname);
    }
    else
    {
        return 1;
    }
}
else
{
    return 1;
}   
return 0;

}

//*****************************************************************************
// 関数名 :SetUseNetwork()
// 概略 :変更したネットワークアダプタ情報の設定
//*****************************************************************************
int IPMessage::SetUseNetwork(CString strAdapterName)
{
HKEY hKey; // レジストリキーオープン用キー・ハンドル
TCHAR szKeyName[256]; // レジストリパス
long lRet = 0;

//レジストリ「SOFTWARE\\×××\\OPOSPLUS\\Administrator\\」で取得します。
lstrcpy(szKeyName, PROFILE_KEY4);
lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_ALL_ACCESS, &hKey);

if (lRet == ERROR_SUCCESS)
{
    UINT cbData   = (lstrlen(strAdapterName) + 2) * sizeof(TCHAR); 
    LPBYTE pbData = (LPBYTE)_alloca(cbData);
    memset(pbData, NULL, cbData);
    lstrcpy((LPTSTR)pbData, strAdapterName);

    lRet = RegSetValueEx(hKey, _T("UseNetworkCard"), 0, REG_SZ, pbData , cbData);
    if (lRet != ERROR_SUCCESS)
    {
        return 1;
    }
}
return 0;

}

获取windows操作系统分辨率(DPI)

// ScreenDPI.cpp : Defines the entry point for the console application.
//

#include “stdafx.h”
#include
#include <windows.h>
#include <math.h>
using namespace std;

// 1毫米=0.039370078740157英寸
#define INCH 0.03937

float GetDPI()
{
HDC hdcScreen;
hdcScreen = CreateDC(L”DISPLAY”, NULL, NULL, NULL);

int iX = GetDeviceCaps(hdcScreen, HORZRES);    // pixel
int iY = GetDeviceCaps(hdcScreen, VERTRES);    // pixel
int iPhsX = GetDeviceCaps(hdcScreen, HORZSIZE);    // mm
int iPhsY = GetDeviceCaps(hdcScreen, VERTSIZE);    // mm

if (NULL != hdcScreen)
{
    DeleteDC(hdcScreen);
}
float iTemp = iPhsX * iPhsX + iPhsY * iPhsY;
float fInch = sqrt(iTemp) * INCH ;
iTemp = iX * iX + iY * iY;
float fPixel = sqrt(iTemp);

float iDPI = fPixel / fInch;    // dpi pixel/inch
cout<<"DPI:"<<iDPI<<endl;
return iDPI;

}

int _tmain(int argc, _TCHAR* argv[])
{
GetDPI();
system(“pause”);
return 0;
}

2.2 C++类的成员变量和成员函数

类是一种数据类型,它类似于普通的数据类型,但是又有别于普通的数据类型。类这种数据类型是一个包含成员变量和成员函数的一个集合。
类的成员变量和普通变量一样,也有数据类型和名称,占用固定长度的内存空间。但是,在定义类的时候不能对成员变量赋值,因为类只是一种数据类型,本身不占用内存空间,而变量的值则需要内存来存储。

类的成员函数也和普通函数一样,都有返回值和参数列表,它与一般函数的区别是:成员函数是一个类的成员,出现在类体中,它的作用范围由类来决定;而普通函数是独立的,作用范围是全局的,或位于某个命名空间内。

上节我们在最后的完整示例中给出了 Student 类的定义,如下所示:
1. class Student{
2. public: //类包含的变量
3. char *name;
4. int age;
5. float score;
6.
7. public: //类包含的函数
8. void say(){
9. printf(“%s的年龄是 %d,成绩是 %f\n”, name, age, score);
10. }
11. };
上面的代码在类体中定义了成员函数。你也可以只在类体中声明函数,而将函数定义放在类体外面,如下图所示:
1. class Student{
2. public:
3. char *name;
4. int age;
5. float score;
6.
7. public:
8. void say(); //函数声明
9. };
10.
11. //函数定义
12. void Student::say(){
13. printf(“%s的年龄是 %d,成绩是 %f\n”, name, age, score);
14. }
在类体中直接定义函数时,不需要在函数名前面加上类名,因为函数属于哪一个类是不言而喻的。
但当成员函数定义在类外时,就必须在函数名前面加上类名予以限定。::被称为域解析符(也称作用域运算符或作用域限定符),用来连接类名和函数名,指明当前函数属于哪一个类。
如果在域解析符“::”的前面没有类名,或者函数名前面既无类名又无域解析符“::”,如:
1. //无类名
2. ::say( ){
3. //TODO
4. }
5. //无类名也无域解析符
6. say( ){
7. //TODO
8. }
则表示 say() 函数不属于任何类,这个函数不是成员函数,而是全局函数,即非成员函数的一般普通函数。
成员函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前,否则编译时会出错。
虽然成员函数在类的外部定义,但在调用时会根据在类中声明的函数原型找到函数的定义(函数代码),从而执行该函数。

Inline成员函数

在类体中和类体外定义成员函数是有区别的:在类体中定义的成员函数为内联(inline)函数,在类体外定义的不是。
内联函数一般不是我们所期望的,它会将函数调用处用函数体替代,所以我建议在类体内部对成员函数作声明,而在类体外部进行定义,这是一种良好的编程习惯。
当然,如果你的函数比较短小,希望定义为内联函数,那也没有什么不妥的。

如果你既希望将函数定义在类体外部,又希望它是内联函数,那么可以在声明函数时加 inline 关键字如下所示:
1. class Student{
2. public:
3. char *name;
4. int age;
5. float score;
6.
7. public:
8. inline void say(); //声明为内联函数
9. };
10.
11. //函数定义
12. void Student::say(){
13. printf(“%s的年龄是 %d,成绩是 %f\n”, name, age, score);
14. }
这样,say() 就会变成内联函数。
值得注意的是,如果在类体外定义 inline 函数,则必须将类定义和成员函数的定义都放在同一个头文件中(或者写在同一个源文件中),否则编译时无法进行嵌入(将函数代码的嵌入到函数调用出)。这样做虽然提高了程序的执行效率,但从软件工程质量的角度来看,这样做并不是好的办法,因此实际开发中较少在类中使用内联函数。
C++提出内联函数的主要用意是:用内联函数取代带参宏定义(函数传参比宏更加方便易用),而不是提高程序运行效率,因为与执行函数花费的时间相比,调用函数花费的时间往往微乎其微。

C语言动态数组的实现:数组长度随数组元素改变,不会溢出,不会浪费资源

我们知道,C语言中的数组大小是固定的,定义的时候必须要给一个常量值,不能是变量。

这带来了很大的不便,如果数组过小,不能容下所有数组,如果过大,浪费资源。

请实现一个简单的动态数组,能够随时改变大小,不会溢出,也不会浪费内存空间。

下面的代码实现了简单的动态数组:
#include <stdio.h>
#include <stdlib.h>
int main()
{
//从控制台获取初始数组大小
int N;
int *a;
int i;
printf(“Input array length:”);
scanf(“%d”,&N);
//分配空间
a=(int *)calloc(N,sizeof(int));
//填充数据
for(i=0;i<N;i++){
a[i]=i+1;
printf(“%-5d”,a[i]);
if((i+1)%10==0){
printf(“\n”);
}
}
//释放内存
free(a);
a=NULL;
printf(“\n”);
return 0;
}
运行结果:
Input array length:20
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20

C语言使用枚举输出一周中的每一天

下面的程序将输出一周中的每一天:

#include <stdio.h>
#include<stdlib.h>

// 定义枚举类型
enum days {monday,tuesday,wednesday,thursday,friday,saturday,sunday};
typedef enum days days; // 我们可以使用 days 来代替 enum days

days yesterday(days today){
  return (today+6)%7;// (enum days)((today+6)%7);
}

days tomorrow(days today){
  return (today+1)%7;// (enum days)((today+1)%7);
}

// 常量字符串数组
const char * const thedays[] = {"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"};

int main(void){
  days today;
  printf("today \tyesterday \ttomorrow\n"
    "============================================\n");
  for (today=monday;today<=sunday;today++) // today =(enum days)(today+1)
    printf("%s = %d \t %s = %d \t %s = %d\n", thedays[today], today,thedays[yesterday(today)], yesterday(today),thedays[tomorrow(today)], tomorrow(today));
  system("pause");
}

输出结果:

today                        yesterday               tomorrow

monday = 0            sunday = 6              tuesday = 1
tuesday = 1            monday = 0             wednesday = 2
wednesday = 2      tuesday = 1             thursday = 3
thursday = 3           wednesday = 2       friday = 4
friday = 4                 thursday = 3            saturday = 5
saturday = 5           friday = 4                  sunday = 6
sunday = 6             saturday = 5            monday = 0

 

 


该代码在VS环境中会出错,按注释中的方式修改。