惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News | PayPal Newsroom
P
Proofpoint News Feed
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
SecWiki News
SecWiki News
Know Your Adversary
Know Your Adversary
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
NISL@THU
NISL@THU
WordPress大学
WordPress大学
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Threat Research - Cisco Blogs
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
Security Archives - TechRepublic
Security Archives - TechRepublic
有赞技术团队
有赞技术团队
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
V
V2EX
G
GRAHAM CLULEY
TaoSecurity Blog
TaoSecurity Blog
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
F
Fortinet All Blogs
博客园 - 叶小钗
博客园 - 三生石上(FineUI控件)
云风的 BLOG
云风的 BLOG
Recorded Future
Recorded Future
Latest news
Latest news
The Hacker News
The Hacker News
aimingoo的专栏
aimingoo的专栏
T
Troy Hunt's Blog
S
Schneier on Security
I
Intezer
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Apple Machine Learning Research
Apple Machine Learning Research
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
爱范儿
爱范儿
The Register - Security
The Register - Security
S
SegmentFault 最新的问题
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
宝玉的分享
宝玉的分享
Recent Commits to openclaw:main
Recent Commits to openclaw:main
美团技术团队
B
Blog RSS Feed

博客园 - 老羽

推荐AspNetPager分页控件 SqlServer2005中分页代码测试 WM中实现Splash动画效果 自定义合并多文件协议设计 JScript中Date.getTime转.Net中的DateTime .net中处理C/C++中的位域 POOM(Pocket Outlook Object Model)开发介绍及应用(转) 多线程执行多任务的DEMO GPRS管理与创建APN拨号连接 WM中电话相关的技巧 - 老羽 - 博客园 一道面试题题解 【转】Windows Mobile控制面板程序 [转]Windows Mobile上的服务程序 Smartphone2003开发总结一:Form总结 C++常用技巧一(整理收集) [转]CString,string,char*的综合比较 [转]C++ int,char,string,CString类型转换(整理总结) C++字符串函数详解[转] Smartphone2003中实现焦点在ListView控件与其它控件中切换
[转] C++ Windows字符和字符指针类型
老羽 · 2009-06-08 · via 博客园 - 老羽

转自:http://www.cnblogs.com/beyond-code/archive/2009/03/24/1420780.html

下面我罗列一些我们在Windows平台下编程经常使用到的和字符或字符串有关的数据类型。

char  和  wchar_t 

这两个类型大家绝对不会陌生吧,一个是单字节的字符类型,一个是宽字节的字符类型(也就是Unicode字符)。

char   c = 'b';

wcha_t  wc = L'b';

上面我就分别定义了2个变量c和wc ,相信第一个定义大家都看的懂,就是定一个字符变量c,其中保存了'b'这个字符。 那么第二个呢?  我相信还是很多人都看的懂,要是你看不懂也没关系,现在就告诉你,也是定义一个字符变量wc, 只不过这个字符变量是Unicode字符变量,用2个字节来保存一个字符,而上面的c这个字符变量只有一个字节来保存,那么在'b'前面的L又是什么意思呢,它就表示这里的'b'这个字符是一个Unicode字符,所以第二个定义的意思就是将L'b'这个Unicode字符保存到wc这个Unicode字符变量中。

如果我要定义一个字符数组怎么定义呢? 用分别用单字节的char和宽字节的wchar_t来定义就应该是:

char  c[10];

wchar_t wc[10];

如果是要带初始化的字符数组的声明,我们来看看怎么写

char c[] = "beyondcode";

wchar_t wc[] = L"beyondcode";

看到了吗,宽字节的操作其实和单字节的字符操作一样吧,只是在前面加上L表示是宽字节的字符或者字符串。

上面都是属于C/C++中的知识,并没有涉及太多Windows中的数据类型,那么各位朋友们在Windows编程中看到的满到处都是的 TCHAR,LPSTR, LPCSTR, LPWSTR, LPCWSTR, LPTSTR, LPCTSTR 这些数据类型又是怎么回事呢? 别急,我们一步一步的来,最后我会联系到那上面去的。

上面的你都知道或者是理解了的话,那我们继续,除了可以声明一个字符数组,我还可以定义一个字符指针变量来指向一个字符数组,当然这个字符数组可以是Unicode的宽字节字符数组,也可以是单字节字符数组,如下:

char  c[] = "hello beyondcode"; //定义一个字符数组

wchar_t  wc[] = L"hello beyondcode"; //定义一个宽字节字符数组

char   *p = c; //定义一个字符指针,指向刚才的字符数组

wchar_t *wp = wc; //定义一个宽字节字符指针,指向刚才的宽字节字符数组

这样之后,我就可以通过指针来改变刚才我们定义的2个数组,例如:

p[0] = 'H';

wp[0] = L'H';

把上面2个数组的第一个字符通过指针改变成大写。这里是可以通过指针来修改的,因为我没有定义指针为常量指针,也就是没有加const 修饰符。如果我像下面这样定义的话,那么就不能通过这些指针来改变他们所指向的数据了,而是只有读取他们。

const  char  *p = c;

const  wchar_t  *wp = wc;

上面将的都是C/C++的基础知识,有点啰嗦,为了照顾新手朋友们嘛,下面我们就来看看Windows是怎么定义它的数据类型的

首先,定义了CHAR, WCHAR的这2个字符数据类型,就是我们上面讨论的两个字符数据类型改了一下名字而已。现在你还不昏吧··

typedef char CHAR;

typedef wchar_t WCHAR;

然后,用刚才定义的 CHAR, WCHAR这2个字符数据类型去定义了一系列其他字符指针类型。

typedef CHAR *LPSTR;

typedef WCHAR *LPWSTR;

这样一定义之后,LPSTR的就是 CHAR*, 而CHAR 又是char, 所以LPSTR的本质就是 char*,也就是我们上面熟悉的不能再熟悉的字符指针,  那LPWSTR不用我推导,相信你也推导出来了吧。不过我还是推导一下,LPWSTR是 WCHAR * , WCHAR是wchar_t,这样LPWSTR就是 wchar_t* ,也就是我们上面讨论的宽字节字符指针。上面这些定义都是在WinNT.h这个头文件中定义的,读者朋友们有兴趣在这个头文件里面去挖掘挖掘吧,上面2个定义我只是提取了重要的部分,其实在里面他还定义了其他很多别名.

看了LPSTR, LPWSTR是怎么一回事之后,我们再接再厉,看看LPCSTR,LPCWSTR这2个数据类型又是怎么一回事呢, 老规矩,先看windows的定义。

typedef CONST CHAR *LPCSTR;

typedef CONST WCHAR *LPCWSTR;

和上面的比较,名字中就多了一个大写的C,这个C的含义就代表是const修饰符,也就是我们上面所说的常量指针,指向的内容不能通过这个指针被改变,但可以读取。定义中的大写的CONST也是一个宏,我在第一篇文章中就讲过了,代换出来也就是const, 所以请读者自己推导一下这两个数据类型的本质是什么。

所以,在windows平台下的编程过程中,凡是可以使用char* 的地方,你都可以使用LPSTR来代替,凡是可以使用wchar_t*的地方,你都可以使用LPWSTR来代替,至于怎么用,还是那句老话,看你个人心情,只不过Windows的API函数中关于字符串的都是使用LP这种数据类型。但是你还是可以给他传递char* 或者 wchar_t* ,只要他们的本质是一样的,那怎么不可以呢~~

下面,我们来看一看一些示例。

char  c = 'c';  和 CHAR c = 'c';    是一样的

wchar_t wc = L'w'; 和 WCHAR wc = L'w';    是一样的

char* p  和 LPSTR p 是一样的

wchar_t* wp  和 LPWSTR wp    是一样的

再来看看动态内存分配怎么写的呢

char* p = new char[10]; //动态分配了十个字符

也可以写成

CHAR* p = new CHAR[10];

LPSTR p = new CHAR[10];

LPSTR p = new char[10];

宽字节的再来一次

wchar_t* wp = new wchar_t[10];

也可以写成下面这些形式

WCHAR*  wp = new WCHAR[10];

LPWSTR  wp = new WCHAR[10];

LPWSTR  wp = new wchar_t[10];

上面定义的这些字符指针 p , wp都没有用const修饰符,所以可以通过他们来修改他们所指向的内容。这里留给读者一个问题,怎么定义有const修饰符的字符指针呢,都可以用什么形式来写呢,写得越多越好哟。。

通过上面这些,我想你大概已经了解了LPSTR, LPCSTR, LPWSTR, LPCWSTR这四个数据类型了,他们无非就是:

LPSTR  -------    char*

LPCSTR -------  const char*

LPWSTR -------  wchar_t*

LPCWSTR --------   const wchar_t* 

下面我提一个问题,如果你在你的程序中使用的字符串都是通过LPWSTR,LPCWSTR这种宽字节(Unicode)字符指针来进行操作的,那么在Unicode环境下编译,完全没有问题,如果这时你需要编译一套ASCII版本的程序,那你会怎么办呢?   你说将用LPWSTR 和LPCWSTR的地方全部换成LPSTR和LPCSTR,再将字符串前面的L去掉就可以了,对,这是一种方法,但是!!所有人在这里都应该知道我要说但是,这也太麻烦了吧。难道没有通用点的方法吗?   有!!  所有人在这里也都知道我会说有,呵呵。  那就是使用微软的通用数据类型,说通用数据类型有点太专业了,其实也就那样,请听我慢慢分析来。我在上一篇文章中说过,凡是涉及字符串操作的API函数有2套,一个A系列的,一套W系列的,还有一套宏,能根据不同的工程环境定义成不同的API函数名。那么在字符类型上微软也使用几乎同样的技术,定义了一套宏能根据不同的工程环境定义成不同的字符数据类型。我上面就提到过的TCHAR,LPTSTR, LPCTSTR就是这样的类型。

首先说说TCHAR,它是被这样定义的:

#ifdef UNICODE

typedef  WCHAR TCHAR;

#else

typedef  char TCHAR

看到了吗? 它也是根据UNICODE这个宏被定义没有,如果被定义了,那么TCHAR代表的数据类型就是WCHAR, 也就是wchar_t, 如果没被定义,那么TCHAR 就代表的是char

同样LPTSTR,LPCTSTR也是这样的,考虑到篇幅,我就只列出LPTSTR来给大家看看了

#ifdef   UNICODE

typedef LPWSTR LPTSTR;

#else

typedef  LPSTR LPTSTR;

这个是我简化了的定义,真实面目有些复杂,不过意思也是如此,有兴趣可以自己看看,在WinNT.h这个头文件中。下面再次解释一下上面这个LPTSTR的定义, 还是老样子,根据UNICODE这个宏被定义与否来决定怎么定义LPTSTR ,如果是定义了UNICODE这个宏,表示当前工程环境是Unicode环境,那么LPTSTR就被定义为了LPWSTR, LPWSTR就是我们前面所讲的wchar_t* ,所以此时LPTSTR代表的数据类型就是wchar_t* ,  如果这时的工程没有定义UNICODE这个宏,那么就定义LPTSTR为LPSTR,而LPSTR就是我们前面所说的char* ,所以这是的LPTSTR就代表char*。懂了吗?各位,我都觉得自己有些啰嗦了··不好意思···

然后还有一个宏需要讲一下,由于我们使用通用数据类型,那么我事先就不知道我的源代码需要在Unicode下编译还是在ASCII环境下编译,所以如下这种情况

TCHAR tc = 'a';  或者是 TCHAR tc = L'a';  是否合适呢? 前面我已经说过了字符或字符串常量前面加L代表这是宽字节的字符或字符串,将一个宽字节字符赋值给一个TCHAR数据类型的变量tc,什么情况下是正确的呢?  各位思考一下呢?  

如果当前工程是Unicode环境,那么TCHAR数据类型就是wchar_t的宽字节类型,所以tc就是宽字节字符变量,那么上面第二个赋值语句就是正确的,而第一个就是错误的。

如果反过来,当前的工程是ASCII环境,那么TCHAR代表的是char这种数据类型,那么第一个赋值语句就是正确的,而第二个就是错误的了。

分析了这么多,我就是要讲一个宏 _T(), 只要将字符或者字符串常量放在_T()这个宏里面,那么这个宏就能根据当前的环境决定是否在字符或字符串前面加L,如下面:

TCHAR tc = _T('A');

如果这么写,在不需要改写源代码的情况下,就可以编译出Unicode和ASCII两套程序,而只需要改变工程的环境而已。