[c]
#ifndef WIN32
#include "socket_posix.h"
#else /* WIN32 */
#include "socket_win32.h"
#endif /* !WIN32 */
關鍵點:
#ifndef / #else / #endif 隔離平台差異。/* !WIN32 */),長代碼塊時一眼看清配對關係。[c]
#undef errno
#define errno WSAGetLastError()
#define read(fd, buf, size) recv(fd, buf, size, 0)
#define write(fd, buf, size) send(fd, buf, size, 0)
#define close(fd) closesocket(fd)
將 POSIX 風味 API 映射到 WinSock,業務層直接調用 read/write/close,鏈接時自動解析到對應實現。
利用 ## 運算符在預處理期拼接標識符,一套宏同時生成 IPv4 與 IPv6 版本代碼:
[c]
/* 通用填充宏:AF_XXX, struct sockaddr_XXX, sin/sin6, s_addr/s6_addr */
#define FILL_SOCKADDR(AF, ST, SIN, S, host, port, count, addrlen, addrs) \
do { \
struct ST *addr; \
int i; \
addrlen = sizeof(struct ST); \
addr = (struct ST*) calloc(count, addrlen); \
for (i = 0; i < count; ++i) { \
addr[i].SIN##_family = AF; \
addr[i].SIN##_port = port; \
memcpy(&addr[i].SIN##_addr.S##_addr, ADDR(host, i), host->h_length); \
} \
addrs = (char*) addr; \
} while (0)
/* IPv4 具體化 */
#define fill_sockaddr(host, port, count, addrlen, addrs) \
FILL_SOCKADDR(AF_INET, sockaddr_in, sin, s, host, port, count, addrlen, addrs)
/* IPv6 具體化(條件編譯) */
#ifdef AF_INET6
#define fill_sockaddr6(host, port, count, addrlen, addrs) \
FILL_SOCKADDR(AF_INET6, sockaddr_in6, sin6, s6, host, port, count, addrlen, addrs)
#endif
調用 fill_sockaddr(...) 時,預處理器將 SIN##_family 展開為 sin_family,S##_addr 展開為 s_addr;同理 fill_sockaddr6 展開為 sin6_family、s6_addr。**一份邏輯,零重複,類型安全**。
| 技巧 | 示例 | 作用 |
|---|---|---|
do { ... } while(0) | 多語句宏包裝 | 保證宏在 if/else 中像單語句一樣工作 |
## Token Pasting | SIN##_family | 預處理期拼接標識符,實現代碼生成 |
# Stringizing | #define STR(x) #x | 將參數變為字符串字面量,用於日志/斷言 |
| 可變參數宏 | #define LOG(fmt, ...) printf(fmt, __VA_ARGS__) | 類 printf 宏,C99 標準 |
| 條件編譯 | #ifdef DEBUG | 調試代碼零開銷發布 |
跨平台核心在於接口統一、實現隔離;宏進階核心在於預處理期代碼生成。善用 ##、do-while(0)、可變參數等技巧,能將大量重複、易錯的樣板代碼壓縮為可維護的宏庫,是 C 語言工程化的重要標誌。