API入门系列之三 -那迷惑人的Windows字符和字符指针类型

char和 wchar_t 1、wchar_t Unicode称为宽字符型字串,COM里使用的都是Unicode字符串。wchar_t是Unicode字符的数据类型,它实际定义在<string.h>里。不能使用类似strcpy这样的ANSI C字符串函数来处理wchar_t字符串,必须使用wcs前缀的函数,例如wcscpy。 如果你想使用Unicode字符串,那么请使用这一套: wchar_t szString[100]; wcscpyszString,L"test"); 2、char ANSI字符类型。 如 阅读详情

原创文章,转载请注明作者及出处。

首发

http://blog.youkuaiyun.com/beyondcode

http://www.cnblogs.com/beyond-code/

http://hi.baidu.com/beyondcode

大家好,通过前面两篇打头文章,我也看了留言,感谢那些给我提意见的人和指出错误之处的人。再次谢谢你们的支持。 另外,Windows SDK编程交流群已经建立了,欢迎各位志同道合者加入进行交流( 群号:81543028 )

本打算通过前面两篇文章的讲解,后来的系列就可以通过使用一些简单的,常用的API写一些示例程序的讲解进行,但是发现还有一个不得不先讲一讲的要点,Windows下和字符串操作有关的数据类型。我看留言中也有几位朋友提到了,那我就在这篇中讲它吧。不会很枯燥的,各位慢慢看下去就是了。

下面我罗列一些我们在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两套程序

而只需要改变工程的环境而已。

这篇文章的内容大概就这么多了,关于后续文章的内容安排,我会适当采纳各位朋友的留言来进行安排。

在这里我介绍的是Windows平台下的和字符串操作有关的数据类型,至于MFC中的CString类,c++标准库中的string,我就不做讲解了。

还有,前两篇的文章中我说微软定义这些数据类型为大写是为了编码的方便,不需要切换输入法,很多朋友都留言给我指出了,其实我是开了一个小玩笑,毕竟面对的是初学者,我觉得在目前这种环境下,没有必要给他们讲什么代码移植性啊,兼容性这些概念,那些在达到一定程度之后就会慢慢理解的,所以我自己想了一个小小的优点来说明那个问题,不过还是感谢你们的补充说明,谢谢··

C#调用C++离线SDK实战:P/Invoke封装行驶证OCR识别避坑指南 跨语言调用是工程开发中的常见需求,C#通过P/Invoke机制调用C++动态库,涉及调用约定、字符串编码与内存管理等底层原理。理解这些基础概念,能帮助开发者高效实现原生代码集成,在保证性能的同时规避内存泄漏与崩溃风险。离线OCR识别技术则凭借本地化处理能力,在车管所、检测站等隐私敏感且追求低延迟的场景中具有独特价值。本文以百度行驶证C++离线SDK为例,从DllImport声明、缓冲区处理、DLL依赖排查到完整代码结构,系统梳理了C#接入C++离线SDK的工程实践路径,并针对DllNotFoundExcep 阅读详情

相关推荐

ThingsKit物联网平台产品管理之物模型

物模型是ThingsKit物联网平台为产品定义的数据模型,用于描述产品的功能。本文介绍物模型相关概念和使用限制。

云腾五洲官方博客 1174

本地模型驱动的自构建dev harness:416次运行仅176美元的低成本AI编程闭环

在AI辅助开发领域,自动化验证循环是连接代码生成与质量保障的纽带。通过将任务描述、代码修改与测试反馈整合为闭环,开发者可以在低成本条件下实现高效的智能迭代。本地模型作为推理后端,不仅降低了单次运行的成本,还保障了代码隐私。一个典型的dev harness能够将失败信息结构化回传,让模型在多次试错中逐步逼近正确方案。这种模式特别适用于单元测试补全、配置修复和机械重构等可自动验证的开发任务。本文结合一个真实案例的416次运行与176美元总成本,拆解如何搭建自我构建工具链,并讨论其中的参数调优、成本控制与排障策略

weixin_34111790的博客 392

无人机通信安全渗透测试:从信号拦截到GPS欺骗的完整攻防框架

在物联网和智能设备普及的背景下,无线通信安全成为关键基础设施防护的核心环节。其原理在于,通过软件定义无线电等设备对空中的射频信号进行捕获、分析与模拟,从而评估通信协议在加密、认证和防重放等方面的脆弱性。这项技术的价值在于,它能主动发现并修复系统设计缺陷,避免因信号被拦截或欺骗导致设备被劫持、数据泄露等重大安全事件。其应用场景广泛,涵盖消费电子、工业控制及关键基础设施等领域。本文聚焦于无人机这一典型移动物联网终端,深入探讨如何构建一套针对其通信链路的渗透测试方法论,其中涉及信号拦截、协议逆向等关键技术,旨在为

weixin_34128237的博客 391

(转)API入门系列之三 -那迷惑人的Windows字符和字符指针类型

API入门系列之三 -那迷惑人的Windows字符和字符指针类型 2009年03月24日 18:29:00 阅读数:5112 出处(https://blog.youkuaiyun.com/beyondcode/article/details/4021085)。 首发 http://blog.youkuaiyun.com/beyondcode http://www.cnblogs.com/beyond-code...

moshumu1的博客 219

DAP-Link DIY复刻指南

DAP-Link是ARM开源的一个项目,主要用于代码下载以及调试,支持SWD和JTAG调试接口,对于开发嵌入式的同学相信都或多或少有所耳闻。除了DAP-Link之外还有很多同类型的调试器,如ST-LinkJ-Link,但是这些都是不开源的,无法复刻或进行二次开发。但DAP-Link不同,完全开源,不用担心侵权问题,且随着开源社区大家的共同努力,DAP-Link经过了不断的迭代,先如今已取代ARM之前推出的CMSIS-DAP开源项目。

爱出名的狗腿子 1万+

JS逆向-快手__NS_hxfalcon参数分析

现在快手H5接口都需要__NS_hxfalcon这个参数,这篇文章就是逆向这个参数

ckcookies的博客 4076

API入门系列之三 -那迷惑人的Windows字符和字符指针类型 -转载

首发 http://blog.youkuaiyun.com/beyondcode http://www.cnblogs.com/beyond-code/ http://hi.baidu.com/beyondcode 大家好,通过前面两篇打头文章,我也看了留言,感谢那些给我提意见的人和指出错误之处的人。再次谢谢你们的支持。另外,WindowsSDK编程交流群已经建立了,欢迎各位志同道...

weixin_30515513的博客 92

ATAC-seq数据分析流程学习记录及总结

记录ATAC-seq数据分析流程,主要是参考健明老师的教程以及网上各种学习资源进行总结,网址在下面。

ACGTexplorer的博客 3755

百度研发工程师模拟笔试全解析:题型分布与备考策略

在互联网大厂研发岗的校招笔试中,数据结构、算法、C++语言特性和计算机网络始终是考察的核心板块。理解虚函数表的内存布局、TCP四次挥手中的TIME_WAIT状态、哈希冲突的处理方式,以及动态规划的边界初始化,是应对高难度选择题与编程题的基础。大厂笔试不仅检验代码能力,更强调对底层原理的掌握,通常采用“一易一中一难”的编程题结构来区分候选人的工程思维与问题分析能力。本文以百度研发工程师在线模拟笔试为范本,系统梳理题型分布、高频考点、编程题复盘思路与在线笔试时间分配策略,帮助求职者高效定位薄弱环节,将应试准备转

weixin_34357928的博客 300

【图论】系列(七)图论的中心性度量方法及重要性评价指标

图论中的中心性度量方法是用来衡量图中各个节点(或顶点)相对重要性的指标。

qq_30066733的博客 1077

【AI Agent】Hermes Agent解析

Hermes Agent:一个具备持久化学习能力的开源AI Agent框架 核心特点: 突破传统Agent的"无状态困境",通过记忆系统(SQLite+FTS5全文检索)实现跨会话经验积累 支持200+模型自由切换,11种模型家族和15+平台接入 五层架构设计(入口层、编排层、推理层、工具执行层、存储层)实现平台无关性 47+内置工具和动态技能沉淀机制,支持从任务执行中自动提炼可复用Skill 独特的四层记忆体系(会话/每日/长期/向量索引)和辩证式用户建模 与传统Agent对比: 具备自我进化能力,通过A

sysu_lluozh 948

句柄,指针,API

 所谓句柄实际上是一个数据,是一个Long (整长型)的数据。 句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言中的文件句柄。 从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样

lee576的专栏 1776

RK35X8调试YT8531单网口千兆以太网PHY芯片,修复ping不通或者IP地址丢失的问题

RK35X8上调试YT8531以太网PHY

荻夜的博客 3976

指针定义 初始化

指针和数组容易产生不可预料的错误,其中一部分时概念上的问题:指针用于低级操作,容易产生与繁琐细节相关的错误,其他错误则源于使用指针的语法规则,特别是声明指针的语法。许多有用的程序都可不使用数组或指针实现,现代C++程序采用vector类型和迭代器取代一般的数组,使用string类型取代C风格字符串。 每个指针都有一个与之关联的数据类型,该数据类型决定了指针所指向的对象的类型。eg:一个i

chenzhen1213的专栏 1883

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

27.4 API中的函数指针

有时候在API中还有函数作为参数的时候,例如EnumWindows,它的作用枚举所有的窗口。其C原型为:BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam);MSDN对于参数lpEnumFunc的解释是:Long pointer to an application-defined callback function.亦即指向函数的长指针。其实看lpEnumFunc这个参数也可以获得一些信息:lp说明是长指针,Func说明是函数。参数lpEnum

学习微软 .net 技术开发 761

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

指针初始化 赋值 void*指针

除了使用数值0火灾编译时值为0的const量外,还可以用C++从C语言继承下来的预处理器变量NULL,该变量在cstdlib头文件中定义,其值为0。如果在代码中使用了这个预处理器变量,则编译时会自动被数值0替换。因此,把指针初始化为NULL等效于初始化为0值。 注:预处理器变量不是在std命名空间中定义的,因此其名字为NULL,而非 std::NULL 除两种例外情况外,指针只能初始化或赋

chenzhen1213的专栏 5669

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Windows API入门:3:字符和字符指针

介绍了windows字符和字符指针类型:LPSTR/LPCSTR/LPWSTR/LPCWSTR和_T()宏

Soda Wang的博客 301
上一篇: API入门系列之二 -Unicode还是ASCII
下一篇: API入门系列之四 -相当简单的SDK程序
BeYoNDCoDE
博客等级 码龄18年 159粉丝 13原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值