c++ 宏-T

1. 定义

_T("")是一个宏,定义于tchar.h下。
[cpp]  view plain  copy
 print ?
  1. #define _T(x)       __T(x)  
  2. #define __T(x)      L ## x  

2. 作用

他的作用是让你的程序支持Unicode编码,因为Windows使用两种字符集ANSI和UNICODE,前者就是通常使用的单字节方式,但这种方式处理象中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符。
Windows NT的所有与字符有关的函数都提供两种方式的版本,而Windows 9x只支持ANSI方式。
1)编译一个程序为ANSI方式,_T实际不起任何作用。
2)编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。

3. _T和L的区别

L不管你是以什么方式编译,一律以UNICODE方式保存。比如:
[cpp]  view plain  copy
 print ?
  1. wchar_t Str[] = L"Hello World!";  
这个就是双子节存储字符了。_T是一个适配的宏
1)当#ifdef _UNICODE的时候,_T就是L
2)没有#ifdef _UNICODE的时候,_T就是ANSI的。
而且MS推荐你使用相匹配的字符串函数。比如处理LPTSTR或者LPCTSTR 的时候,不要用strlen,而是要用_tcslen,否则在UNICODE的编译条件下,strlen不能处理 wchar_t*的字符串。T是非常有意思的一个符号(TCHAR、LPCTSTR、LPTSTR、_T()、_TEXT()...),它表示使用一种中间类型,既不明确表示使用 MBCS,也不明确表示使用UNICODE。那到底使用哪种字符集呢?编译的时候才决定。

4. 用类型说明宏

LPSTR:32bit指针指向一个字符串,每个字符占1字节
LPCSTR:32-bit指针指向一个常字符串,每个字符占1字节
LPCTSTR:32-bit指针指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义
LPTSTR:32-bit指针每字符可能占1字节或2字节,取决于Unicode是否定义

比如:
[cpp]  view plain  copy
 print ?
  1. LPTSTR lpStr = new TCHAR[32];  
  2. TCHAR* szBuf = _T("Hello");  
以上两句使得无论是在UNICODE编译条件下都是正确编译的。
<think>我们正在讨论C++定义的使用指南和替换规则。根据提供的引用内容,我们可以总结出定义的一些关键点:1.定义是文本替换,没有类型检查,可能导致类型不安全。2.定义的作用域是全局的,从定义点开始直到文件结束,或者被#undef取消。3.定义可以用于定义常量、带参数的以及条件编译等。4.定义的使用有一些约定,比如常量通常用大写字母,以便引起注意并区分于变量。5.定义可能带来一些问题,如调试困难(因为预处理器替换后,源代码中的名会被替换掉)、重定义冲突等。6.现代C++中,推荐使用const、constexpr、enum、inline函数等来替代,以提高类型安全和可维护性。下面我们将详细说明的使用和替换规则。 ###定义的基本语法定义使用`#define`预处理指令,有两种基本形式:1.定义符号常量(无参):```c#definePI3.14159```在代码中任何出现`PI`的地方都会被替换为`3.14159`。2.定义带参数的(类似函数):```c#define SQUARE(x) ((x)*(x))```当使用`SQUARE(5)`时,会被替换为`((5)*(5))`。###替换的规则替换是纯文本替换,预处理器不会进行语法检查,只是简单地将名替换为定义的文本。替换规则如下:1.在代码中遇到名(对于带参,后面紧跟括号和参数),预处理器就会用定义中的替换文本进行替换。2.对于带参,实参会替换替换文本中的形参。注意:参数名必须与定义中的形参名相同。3.替换文本中非形参部分的相同符号不会被替换。4.替换时,实参是原样替换(作为字符串)到替换文本中的。因此,为了避免运算符优先级问题,通常需要在替换文本中给形参加上括号,整个表达式也要加括号。例如:`SQUARE(x)`定义为`((x)*(x))`,这样在`SQUARE(a+b)`时会被替换为`((a+b)*(a+b))`,避免因为运算符优先级而出现错误。5.定义可以包含多个语句,但要注意替换后可能改变代码结构。例如:```c#define SWAP(a, b) {int temp= a; a= b; b= temp; }```但是这样的在if语句中可能有问题,因为if语句后只能跟一条语句,而这里用大括号包起来是一个复合语句。另外,如果使用不当,可能造成变量重复定义等问题。6.定义中可以使用`#`运算符(字符串化)和`##`运算符(连接)。- `#`运算符:将参数转换成字符串常量。例如:```c#defineSTRING(x)#x```则`STRING(hello)`会被替换为`"hello"`。- `##`运算符:将两个标记连接在一起。例如:```c#defineCONCAT(a,b)a##b```则`CONCAT(var,123)`会被替换为`var123`。###的作用域定义从定义点开始生效,直到文件结束,或者遇到`#undef`取消定义。定义不受作用域限制(例如函数内部、命名空间内部等),但可以通过`#undef`来提前结束其作用域。 ###的注意事项1.定义不是语句,末尾不加分号(除非替换文本中需要分号)。2.定义可以写在函数外面或里面(但通常写在文件开头)。3.定义可以嵌套,即一个的定义中可以包含另一个,但要注意不能递归(即不能直接或间接地包含自身)。4.定义中的注释不会被替换到代码中,因为预处理器会先移除注释。 ###的缺点与替代方案1.缺点:-没有类型检查,容易出错。-调试困难,因为调试器看到的是替换后的代码。-可能产生副作用,例如`SQUARE(i++)`会导致`i`被增加两次。-作用域不受控制,容易造成命名冲突。2.替代方案:-常量:使用`const`或`constexpr`常量。-函数:使用`inline`函数代替带参,可以避免副作用,并且有类型检查。-类型别名:使用`using`或`typedef`代替定义类型。-条件编译:在C++中,条件编译仍然需要(如`#ifdef`),但其他情况尽量用语言特性代替。 ###示例代码```c++#include <iostream>using namespacestd;//定义常量#definePI3.14159//带参数的#defineMAX(a, b) ((a)> (b)? (a): (b))//多语句,使用do-while(0)结构以避免语法问题#define SWAP(a, b) do{ \autotemp =a;\a =b;\b =temp;\ }while(0)//字符串化#define PRINT_VAR(x) cout<< #x "= "<< x<< endlintmain(){double r=5.0;doublearea =PI *r *r;PRINT_VAR(area);//输出:area =78.5397int x=10,y =20;PRINT_VAR(x);PRINT_VAR(y);SWAP(x,y);PRINT_VAR(x);PRINT_VAR(y);cout<< "Maxbetween10 and20: "<< MAX(10,20)<< endl;//注意:MAX的副作用问题int a=5,b =10;int max_val= MAX(++a, b); //被替换为((++a)>(b)?(++a):(b)),这里a自增了两次PRINT_VAR(max_val); PRINT_VAR(a);return0; }```注意:上面的`MAX(++a, b)`示例展示了可能带来的副作用。在调用时,`++a`在比较时自增一次,如果大于b则返回`++a`(再次自增),否则返回b。因此,a被增加了1或2次,这通常不是我们想要的。而使用内联函数则可以避免。###总结是强大的预处理指令,但应谨慎使用。在C++中,应优先考虑使用语言本身的特性(如const、inline、模板等)来替代,以提高代码的安全性和可维护性。然而,在条件编译、日志输出、头文件保护等场景中,仍然不可替代。§§相关问题§§1. C++中如何避免使用定义常量?2.定义和内联函数有什么区别?3.定义中的`#`和`##`运算符具体如何使用?4.为什么说定义可能导致调试困难?5.头文件保护中的定义是如何工作的?</think>### C++ 定义使用指南与替换规则 #### **一、定义基础** 是预处理器指令(以`#`开头),在编译前进行文本替换。基本语法: ```cpp #define 标识符 替换文本 ``` **示例**: ```cpp #define PI 3.14159 // 常量 #define SQUARE(x) ((x)*(x)) // 带参 ``` 编译时,代码中的`PI`替换为`3.14159`,`SQUARE(5)`替换为`((5)*(5))`[^2][^3]。 --- #### **二、核心替换规则** 1. **直接文本替换** - 不做语法检查,仅机械替换: ```cpp #define SQR(x) x*x int a = SQR(1+2); // 替换为 1+2*1+2 = 5(非预期结果) ``` **正确做法**:为参数和整体加括号: ```cpp #define SQR(x) ((x)*(x)) // 替换为 ((1+2)*(1+2)) = 9 ``` 2. **作用域与重定义** - 从定义点生效到文件结束(或`#undef`取消)。 - 重定义相同会触发警告,除非内容完全相同: ```cpp #define MAX 100 #define MAX 100 // 允许(内容相同) #define MAX 200 // 警告(内容不同) ``` 3. **多行定义** 使用`\`实现多行: ```cpp #define SWAP(a,b) do { \ auto temp = a; \ a = b; \ b = temp; \ } while(0) ``` **注意**:`do{...}while(0)`避免在条件语句中出错[^4]。 --- #### **三、进阶用法** 1. **字符串化运算符 `#`** 将参数转为字符串: ```cpp #define STRINGIFY(x) #x const char* str = STRINGIFY(Hello); // 替换为 "Hello" ``` 2. **拼接运算符 `##`** 连接标识符: ```cpp #define VAR_NAME(n) var##n int VAR_NAME(1) = 10; // 替换为 int var1 = 10; ``` 3. **可变参数** ```cpp #define LOG(fmt, ...) printf(fmt, __VA_ARGS__) LOG("Value: %d", x); // 替换为 printf("Value: %d", x); ``` --- #### **四、的缺点与替代方案** 1. **主要缺点**: - **类型不安全**:无类型检查(如`SQUARE("text")`导致错误)[^1]。 - **调试困难**:编译器看到的是替换后的代码[^1]。 - **作用域污染**:全局生效,易命名冲突[^1][^3]。 2. **现代C++替代方案**: | 场景 | 示例 | 替代方案 | |---------------|----------------------|----------------------------| | 常量 | `#define PI 3.14` | `constexpr double PI = 3.14;` | | 函数式 | `#define MAX(a,b) ((a)>(b)?(a):(b))` | `template<typename T> T max(T a, T b) { return a>b?a:b; }` | | 条件编译 | `#ifdef DEBUG` | 仍需(无替代) | --- #### **五、最佳实践** 1. **命名规范**: - 常量全大写(如`BUFFER_SIZE`),函数式首字母大写(如`SafeDelete`)[^2]。 2. **避免副作用**: ```cpp #define INCREMENT(x) x++ // 危险! int a = 1; int b = INCREMENT(a) * 2; // 替换为 a++ * 2(行为未定义) ``` 3. **头文件保护**: ```cpp #ifndef MY_HEADER_H #define MY_HEADER_H // 头文件内容 #endif ``` > **关键原则**:优先使用 `constexpr`、`inline`、模板等语言特性,仅在必要场景(如条件编译、日志输出)使用[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值