11_C基础(宏定义)
宏定义
类似的宏名称通常用于表示不同的状态码、错误码或特定的常量值。以下是一些常见的宏名称示例,通常用于各种系统或应用程序中:
文件操作相关的宏
FILE_OPEN_SUCCESS
:文件打开成功FILE_READ_ERROR
:文件读取错误FILE_WRITE_ERROR
:文件写入错误FILE_PERMISSION_DENIED
:文件权限不足FILE_NOT_FOUND
:文件未找到FILE_ALREADY_EXISTS
:文件已经存在
系统状态相关的宏
SYSTEM_OK
:系统操作成功SYSTEM_ERROR
:系统错误SYSTEM_BUSY
:系统忙SYSTEM_TIMEOUT
:系统超时SYSTEM_OUT_OF_MEMORY
:系统内存不足
网络操作相关的宏
NETWORK_CONNECTED
:网络连接成功NETWORK_DISCONNECTED
:网络断开NETWORK_TIMEOUT
:网络超时NETWORK_ERROR
:网络错误NETWORK_UNREACHABLE
:网络不可达
常量值相关的宏
MAX_BUFFER_SIZE
:最大缓冲区大小DEFAULT_TIMEOUT
:默认超时时间MAX_CONNECTIONS
:最大连接数PI
:圆周率E
:自然对数的底
用户输入相关的宏
USER_INPUT_INVALID
:用户输入无效USER_NOT_AUTHORIZED
:用户未授权USER_AUTH_SUCCESS
:用户认证成功USER_AUTH_FAILURE
:用户认证失败
这些宏的命名通常遵循一定的命名规范,使用全大写字母和下划线分隔词语,以便与普通变量名区分开来。同时,这样的命名方式有助于代码的可读性和可维护性。
错误码宏名称
错误码宏在编程中广泛用于表示不同类型的错误和异常情况。以下是一些常见的错误码宏分类和示例,它们可以应用于不同的场景:
1. 通用错误码
SUCCESS
:操作成功(通常为0
)。ERROR
:通用错误(通常为-1
或0x80000001
)。INVALID_ARGUMENT
:无效参数(例如0x80000002
)。NULL_POINTER
:空指针错误(例如0x80000003
)。OUT_OF_MEMORY
:内存不足(例如0x80000004
)。NOT_IMPLEMENTED
:功能未实现(例如0x80000005
)。TIMEOUT
:操作超时(例如0x80000006
)。
2. 文件操作错误码
FILE_NOT_FOUND
:文件未找到(例如0x80000007
)。FILE_READ_ERROR
:文件读取错误(例如0x80000008
)。FILE_WRITE_ERROR
:文件写入错误(例如0x80000009
)。FILE_PERMISSION_DENIED
:文件权限不足(例如0x8000000A
)。FILE_ALREADY_EXISTS
:文件已存在(例如0x8000000B
)。FILE_TOO_LARGE
:文件过大(例如0x8000000C
)。
3. 网络操作错误码
NETWORK_DISCONNECTED
:网络断开连接(例如0x8000000D
)。NETWORK_TIMEOUT
:网络操作超时(例如0x8000000E
)。NETWORK_UNREACHABLE
:网络不可达(例如0x8000000F
)。NETWORK_PROTOCOL_ERROR
:网络协议错误(例如0x80000010
)。DNS_RESOLUTION_FAILED
:DNS解析失败(例如0x80000011
)。
4. 数据库操作错误码
DB_CONNECTION_FAILED
:数据库连接失败(例如0x80000012
)。DB_QUERY_ERROR
:数据库查询错误(例如0x80000013
)。DB_TRANSACTION_FAILED
:数据库事务失败(例如0x80000014
)。DB_RECORD_NOT_FOUND
:记录未找到(例如0x80000015
)。DB_DUPLICATE_RECORD
:重复记录(例如0x80000016
)。
5. 用户身份验证错误码
AUTH_FAILED
:身份验证失败(例如0x80000017
)。AUTH_EXPIRED
:身份验证已过期(例如0x80000018
)。AUTH_TOKEN_INVALID
:无效的身份验证令牌(例如0x80000019
)。USER_NOT_AUTHORIZED
:用户未授权(例如0x8000001A
)。USER_NOT_FOUND
:用户未找到(例如0x8000001B
)。
6. 设备操作错误码
DEVICE_NOT_FOUND
:设备未找到(例如0x8000001C
)。DEVICE_BUSY
:设备繁忙(例如0x8000001D
)。DEVICE_ERROR
:设备错误(例如0x8000001E
)。DEVICE_TIMEOUT
:设备超时(例如0x8000001F
)。DEVICE_NOT_READY
:设备未准备好(例如0x80000020
)。
7. 安全相关错误码
SECURITY_VIOLATION
:安全违规(例如0x80000021
)。ENCRYPTION_ERROR
:加密错误(例如0x80000022
)。DECRYPTION_ERROR
:解密错误(例如0x80000023
)。ACCESS_DENIED
:访问被拒绝(例如0x80000024
)。INVALID_CERTIFICATE
:无效的证书(例如0x80000025
)。
8. 系统操作错误码
SYSTEM_ERROR
:系统错误(例如0x80000026
)。SYSTEM_BUSY
:系统繁忙(例如0x80000027
)。SYSTEM_SHUTDOWN
:系统关机(例如0x80000028
)。SYSTEM_RESTART_REQUIRED
:系统需要重启(例如0x80000029
)。UNSUPPORTED_OPERATION
:不支持的操作(例如0x8000002A
)。
这些错误码宏可以帮助开发者更容易地识别和处理各种错误条件。具体的数值可以根据项目需要进行定义和调整,但通常要保证每个错误码具有唯一性,并且能够清晰地表示特定的错误状态。
status错误码宏
以下是一些常见的 STATUS
错误码的宏定义列表,通常用于系统编程中来标识不同的异常和错误情况。每个错误码都有特定的十六进制值和对应的含义。
常见的 STATUS
错误码列表:
STATUS_SUCCESS
(0x00000000
):
操作成功完成。
STATUS_UNSUCCESSFUL
(0xC0000001
):
操作未成功。
STATUS_NOT_IMPLEMENTED
(0xC0000002
):
请求的操作未实现。
STATUS_INVALID_HANDLE
(0xC0000008
):
句柄无效。
STATUS_ACCESS_DENIED
(0xC0000022
):
拒绝访问,权限不足。
STATUS_INSUFFICIENT_RESOURCES
(0xC000009A
):
资源不足,无法完成操作。
STATUS_INVALID_PARAMETER
(0xC000000D
):
参数无效。
STATUS_NO_MEMORY
(0xC0000017
):
系统内存不足,无法分配。
STATUS_CONFLICTING_ADDRESSES
(0xC0000018
):
地址冲突,无法映射。
STATUS_INVALID_PAGE_PROTECTION
(0xC0000045
):
无效的页面保护属性。
STATUS_ILLEGAL_INSTRUCTION
(0xC000001D
):
非法指令。
STATUS_FLOAT_DIVIDE_BY_ZERO
(0xC000008E
):
浮点除以零错误。
STATUS_INTEGER_OVERFLOW
(0xC0000095
):
整数溢出。
STATUS_STACK_OVERFLOW
(0xC00000FD
):
栈溢出错误。
STATUS_INVALID_DISPOSITION
(0xC0000026
):
无效的异常处理结果。
STATUS_ARRAY_BOUNDS_EXCEEDED
(0xC000008C
):
数组下标超出范围。
STATUS_FLOAT_INEXACT_RESULT
(0xC000008F
):
浮点运算结果不精确。
STATUS_INVALID_PARAMETER_1
(0xC00000EF
):
第一个参数无效。
STATUS_INVALID_PARAMETER_2
(0xC00000F0
):
第二个参数无效。
STATUS_PRIVILEGED_INSTRUCTION
(0xC0000096
):
试图执行特权指令。
STATUS_INVALID_CID
(0xC000000B
):
客户端标识符无效。
STATUS_TIMEOUT
(0x00000102
):
操作超时。
STATUS_PENDING
(0x00000103
):
操作正在等待完成。
STATUS_NOT_SUPPORTED
(0xC00000BB
):
不支持请求的操作。
STATUS_INVALID_DEVICE_REQUEST
(0xC0000010
):
无效的设备请求。
STATUS_NO_SUCH_DEVICE
(0xC000000E
):
没有这样的设备。
STATUS_END_OF_FILE
(0xC0000011
):
到达文件结尾。
STATUS_ILLEGAL_VLM_REFERENCE
(0xC00002C0
):
非法的虚拟内存引用。
STATUS_CONTROL_C_EXIT
(0xC000013A
):
Control-C 退出。
STATUS_LOGON_FAILURE
(0xC000006D
):
登录失败。
其他常见的错误码
STATUS_FLOAT_OVERFLOW
(0xC0000091
): 浮点溢出。STATUS_FLOAT_UNDERFLOW
(0xC0000093
): 浮点下溢。STATUS_FLOAT_INVALID_OPERATION
(0xC0000090
): 无效的浮点操作。STATUS_NO_SUCH_FILE
(0xC000000F
): 没有这样的文件。STATUS_INVALID_SYSTEM_SERVICE
(0xC000001C
): 无效的系统服务调用。STATUS_NOT_LOCKED
(0xC000007E
): 资源未锁定。STATUS_RESOURCE_DATA_NOT_FOUND
(0xC000008B
): 资源数据未找到。STATUS_PRIVILEGED_INSTRUCTION
(0xC0000096
): 特权指令错误。STATUS_UNWIND_CONSOLIDATE
(0x80000029
): 异常展开需要合并。
特定错误码
STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY
(0xC01E0301
): 无效的 VIDPN 拓扑。STATUS_INVALID_IMAGE_FORMAT
(0xC000007B
): 无效的镜像格式。STATUS_IN_PAGE_ERROR
(0xC0000006
): 页面内读取失败。
这只是 STATUS
错误码的部分示例,实际上存在大量的错误码用于各种具体的系统状态和异常情况。错误码通常定义在操作系统的头文件中(如 Windows 的 ntstatus.h
),并根据具体需求在编程中使用。
兼容性宏
兼容性宏(Compatibility Macros)是编程中用来确保代码在不同平台、编译器或版本之间能正常工作的宏定义。这些宏通常用于处理操作系统、编译器、库的差异,使得代码可以在不同环境下编译并运行,而不需要针对每个平台或版本进行单独的实现。
兼容性宏的常见用途
操作系统兼容性
不同操作系统(如 Windows、Linux、macOS)提供了不同的系统调用、库函数和文件路径格式。兼容性宏可以帮助程序员在不同的操作系统之间切换,而不需要修改代码逻辑。
示例:
#ifdef _WIN32 #define PATH_SEPARATOR '\\' #else #define PATH_SEPARATOR '/' #endif
编译器兼容性
不同编译器可能对某些语法、内建函数或扩展特性有不同的支持。通过使用兼容性宏,可以在不同的编译器之间切换,而不影响代码的可移植性。
示例:
#ifdef _MSC_VER #define INLINE __inline #else #define INLINE inline #endif
库和API兼容性
当使用某些外部库或API时,这些库的不同版本可能会引入或弃用某些函数、宏或类型。兼容性宏可以用来处理这些版本差异,确保代码能在不同版本的库下编译和运行。
示例:
#if LIBRARY_VERSION >= 200 #define NEW_FUNCTION() library_new_function() #else #define NEW_FUNCTION() library_old_function() #endif
字节顺序(Endianness)处理
不同的处理器架构可能使用不同的字节顺序(大端序和小端序)。兼容性宏可以帮助程序员处理这些差异,以确保数据在不同架构下的一致性。
示例:
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define TO_BIG_ENDIAN_32(x) (x) #else #define TO_BIG_ENDIAN_32(x) __builtin_bswap32(x) #endif
功能检测(Feature Detection)
有时,程序需要在编译时检测某些功能或特性是否可用,并据此决定是否使用某些代码路径。兼容性宏可以用来实现这样的功能检测。
示例:
#ifdef __has_feature #if __has_feature(thread_sanitizer) #define TSAN_ENABLED 1 #else #define TSAN_ENABLED 0 #endif #else #define TSAN_ENABLED 0 #endif
兼容性宏的设计原则
明确性:兼容性宏应该尽量明确地表达其意图。例如,如果宏是用来处理操作系统差异的,宏名中应包含操作系统的标识。
示例:
#ifdef _WIN32
表示 Windows 特定的代码块。
可读性:宏的命名应当具有可读性,方便其他程序员理解其作用。例如,
INLINE
宏表示内联函数的兼容性处理。避免污染全局命名空间:使用兼容性宏时,应注意避免使用太过通用的名称,防止冲突或污染全局命名空间。
维护一致性:在项目中,应保持对同一类兼容性问题的处理方式一致。例如,所有处理路径分隔符的地方都应使用相同的宏。
兼容性宏的示例
以下是一些常见的兼容性宏示例:
跨平台动态库加载宏:
根据操作系统的不同,动态库的加载方式也不同:
#ifdef _WIN32 #define LOAD_LIBRARY(name) LoadLibrary(name) #define GET_PROC_ADDRESS(lib, name) GetProcAddress(lib, name) #define CLOSE_LIBRARY(lib) FreeLibrary(lib) #else #include <dlfcn.h> #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY) #define GET_PROC_ADDRESS(lib, name) dlsym(lib, name) #define CLOSE_LIBRARY(lib) dlclose(lib) #endif
跨编译器内联宏:
不同编译器使用不同的关键词来定义内联函数:
#ifdef _MSC_VER #define INLINE __inline #elif defined(__GNUC__) #define INLINE inline __attribute__((always_inline)) #else #define INLINE inline #endif
C和C++兼容宏:
当需要在 C 和 C++ 代码之间切换时,可以使用
extern "C"
来保证 C++ 编译器使用 C 的符号命名规则:#ifdef __cplusplus #define EXTERN_C extern "C" #else #define EXTERN_C #endif
通过使用这些兼容性宏,开发者可以编写更为健壮、可移植的代码,减少在不同平台或编译器下调试和维护的工作量。
语句块宏
语句块宏(Statement Block Macros)是一种特殊的宏,它将多条语句组合在一起,使得这些语句在调用该宏时像单条语句一样执行。语句块宏在C和C++等语言中常见,通常用于简化代码、提高代码的可读性,并在某些情况下避免代码重复。
语句块宏的基本用法
在C语言中,宏通常用#define
指令定义。语句块宏将多个语句放在一起,用大括号 {}
包围,并通过 do { ... } while(0)
结构包裹,以确保宏在使用时的行为像单个语句一样。这种结构能够避免某些语法问题,例如条件语句中的语句块可能引发的错误。
语句块宏的结构
典型的语句块宏定义如下:
#define MACRO_NAME(arg1, arg2) \
do { \
statement1; \
statement2; \
statement3; \
} while (0)
语句块宏的好处
防止语法错误:
在条件语句或循环中使用宏时,若宏中包含多条语句,而未使用
do...while(0)
结构包裹,可能会引发语法错误。使用do...while(0)
可以确保即使宏展开后也不会影响条件或循环语句的结构。示例:
#define SAFE_FREE(ptr) \ do { \ if (ptr) { \ free(ptr); \ ptr = NULL; \ } \ } while (0)
提高代码可读性:
通过使用语句块宏,复杂的逻辑可以封装在宏中,代码使用时显得简洁。例如,错误检查、内存管理等常用的逻辑可以封装成宏,使用时代码更清晰。
示例:
#define CHECK_AND_RETURN(ptr) \ do { \ if (!(ptr)) { \ return -1; \ } \ } while (0)
减少代码重复:
语句块宏可以用来封装多次使用的代码片段,减少代码重复。例如,日志记录、错误处理等可以通过语句块宏来统一处理。
示例:
#define LOG_ERROR_AND_EXIT(msg) \ do { \ printf("Error: %s\n", msg); \ exit(EXIT_FAILURE); \ } while (0)
语句块宏的使用示例
以下是一些常见的语句块宏的使用示例:
错误处理宏:
当某个操作失败时,打印错误信息并返回一个错误码:
#define CHECK_ERROR(cond, msg) \ do { \ if (!(cond)) { \ fprintf(stderr, "%s\n", msg); \ return -1; \ } \ } while (0)
使用方式:
int open_file(const char *filename) { FILE *fp = fopen(filename, "r"); CHECK_ERROR(fp != NULL, "Failed to open file"); // 其他操作... return 0; }
资源释放宏:
在需要释放多个资源时,使用语句块宏可以统一处理,并避免忘记释放资源:
#define CLEANUP() \ do { \ free(resource1); \ free(resource2); \ fclose(fp); \ } while (0)
使用方式:
int process_data() { // 申请资源... if (error_occurred) { CLEANUP(); return -1; } // 其他操作... CLEANUP(); return 0; }
条件执行宏:
条件执行的宏,可以确保多个语句在条件满足时执行:
#define EXEC_IF_TRUE(cond, action) \ do { \ if (cond) { \ action; \ } \ } while (0)
使用方式:
EXEC_IF_TRUE(x > 0, printf("x is positive\n"));
注意事项
作用域:
语句块宏中的变量通常只在宏内部作用域内可见,这意味着宏中的局部变量不会影响宏外部的变量。但是,如果宏中修改了传入的参数(例如指针),会影响到调用方,因此要小心处理传入参数的修改。
副作用:
使用宏时需要注意副作用问题。宏的参数在展开时会重复使用,因此如果传入的参数有副作用(例如自增、自减操作),可能会导致意外的行为。
示例:
#define SQUARE(x) ((x) * (x)) int y = SQUARE(++i); // 如果i为1,那么i会被加两次,结果为3*3=9,而不是2*2=4
调试困难:
由于宏在预处理阶段被展开,调试时宏展开后的代码可能与原始代码不同,导致调试困难。解决方法包括使用函数替代宏,或者在编译器提供的预处理输出中查看宏展开的结果。
总结来说,语句块宏是一个强大的工具,可以在简化代码和处理跨平台兼容性方面发挥作用。然而,在使用时需要注意可能引入的复杂性和副作用,并遵循良好的编程实践以确保代码的可维护性。
tchar.h
tchar.h
是一个用于处理可移植的字符串操作的头文件,特别是在需要同时支持 ANSI(单字节字符集)和 Unicode(多字节字符集)的 Windows 应用程序中。它定义了一组类型、宏和函数,这些内容使得程序员可以编写与字符类型无关的代码,从而实现代码的字符集独立性。
tchar.h
的主要内容
tchar.h
通过定义 TCHAR
类型和一系列相关的宏和函数来实现字符集的抽象。
1. 字符类型和字符串类型
TCHAR
:TCHAR
是一个字符类型的宏,根据编译时的字符集设置,它会被定义为char
或wchar_t
。如果定义了
UNICODE
或_UNICODE
,则TCHAR
定义为wchar_t
,用于 Unicode 字符串处理;否则,它定义为char
,用于 ANSI 字符串处理。
LPTSTR
/LPCTSTR
:LPTSTR
是指向TCHAR
字符串的指针类型,即char*
或wchar_t*
。LPCTSTR
是指向常量TCHAR
字符串的指针类型,即const char*
或const wchar_t*
。
2. 字符串宏
tchar.h
提供了一组宏,用于将字符串文字或字符类型转换为 TCHAR
对应的格式。这些宏根据编译环境(ANSI 或 Unicode)来调整其含义。
_T
/TEXT
:这些宏用于将字符串文字转换为适当的字符类型。
例如:
_T("Hello")
或TEXT("Hello")
在 ANSI 模式下是"Hello"
,在 Unicode 模式下是L"Hello"
。
3. 字符串操作函数
tchar.h
还通过定义一组与字符串操作相关的宏来实现对不同字符集的支持。这些宏会根据是否定义了 UNICODE
来映射到相应的标准 C 字符串操作函数或宽字符函数。
_tprintf
:对应于
printf
或wprintf
,用于输出格式化字符串。
_tcslen
:对应于
strlen
或wcslen
,用于计算字符串长度。
_tcscpy
:对应于
strcpy
或wcscpy
,用于复制字符串。
_tcscat
:对应于
strcat
或wcscat
,用于连接字符串。
_ttoi
:对应于
atoi
或wtoi
,用于将字符串转换为整数。
_tfopen
:对应于
fopen
或_wfopen
,用于打开文件。
示例代码
假设我们编写一个需要同时支持 ANSI 和 Unicode 的应用程序,下面是使用 tchar.h
的一个简单示例:
#include <tchar.h>
#include <stdio.h>
int _tmain(int argc, TCHAR *argv[]) {
_tprintf(_T("Hello, World!\n"));
TCHAR str[100];
_tcscpy(str, _T("This is a test."));
_tprintf(_T("The string is: %s\n"), str);
int num = _ttoi(_T("12345"));
_tprintf(_T("The number is: %d\n"), num);
return 0;
}
工作原理
如果程序使用的是 ANSI 编译,
TCHAR
将被替换为char
,所有的_t
前缀函数(如_tprintf
)会变成printf
等 ANSI 函数。如果程序使用的是 Unicode 编译,
TCHAR
将被替换为wchar_t
,而_t
前缀函数会变成wprintf
等宽字符函数。
应用场景
tchar.h
在编写需要同时支持 ANSI 和 Unicode 字符集的 Windows 应用程序时非常有用。随着 Unicode 在现代应用程序中的普及,许多程序默认使用 Unicode 编译。不过,tchar.h
在处理遗留代码或需要同时支持多种字符集时仍然是一个有用的工具。
结论
tchar.h
是一个用于处理字符集的抽象层,使得编写可移植代码变得更加容易。通过使用 TCHAR
、_T
和 _t
前缀的函数,开发者可以在同一个代码库中支持 ANSI 和 Unicode,从而提高代码的可移植性和维护性。
_T和__T的区别
_T
和 __T
在 Windows 编程环境中是与字符集处理相关的宏,但它们的使用场景和定义略有不同。
_T
宏
定义:
_T
是一个在tchar.h
中定义的宏,用于将字符串文字转换为与当前字符集(ANSI 或 Unicode)匹配的格式。作用: 它根据是否定义了
UNICODE
或_UNICODE
,将字符串文字转换为char
或wchar_t
类型。使用: 在编写兼容 ANSI 和 Unicode 的代码时,使用
_T
可以确保字符串文字在不同字符集模式下正确处理。定义示例:
#ifdef _UNICODE #define _T(x) L##x #else #define _T(x) x #endif
在
UNICODE
模式下,_T("Hello")
会转换为L"Hello"
(宽字符字符串)。在 ANSI 模式下,_T("Hello")
仍然是"Hello"
(普通的 C 字符串)。
__T
宏
定义:
__T
是与_T
类似的宏,用于将字符串文字转换为特定字符集格式。通常,这个宏与_T
是等价的,实际上很多编译器的实现中它们是同一宏的不同别名。作用:
__T
的作用与_T
完全相同:根据字符集环境,将字符串文字转换为char
或wchar_t
。使用: 在大多数情况下,开发者更常用
_T
而非__T
。__T
在一些代码库或旧的代码中可能会看到,但其使用方式和作用与_T
没有本质区别。定义示例:
#ifdef _UNICODE #define __T(x) L##x #else #define __T(x) x #endif
主要区别
命名约定:
_T
是标准的、更常用的宏名,而__T
则是带有双下划线的版本,通常在 C 和 C++ 编程中,双下划线开头的名称常用于内部或编译器特定的实现,但在这个特定的宏中,二者在功能上是等价的。使用习惯:
_T
是 Microsoft 官方文档和示例中推荐使用的宏,而__T
相对较少使用。在一般的 Windows 应用开发中,开发者通常会使用_T
而非__T
。
结论
同功能不同命名:
_T
和__T
的功能相同,都是用于处理与字符集相关的字符串文字转换。唯一的区别在于命名约定和使用习惯上。推荐使用: 通常情况下,推荐使用
_T
,因为它是更常用和标准化的宏,并且在代码可读性和一致性方面更好。
如果你在代码中看到 __T
,可以将其理解为 _T
的另一种形式,它们的作用和效果是完全相同的。