个人主页:BabyZZの秘密日记
收入专栏:C语言
在文本处理、协议解析、命令行解析等场景中,“大小写不敏感”是十分常见的需求。C 标准库
提供了两个最常用的工具函数:
int tolower(int c);
—— 大写 → 小写int toupper(int c);
—— 小写 → 大写本文将从 函数原型、实现原理、可移植陷阱、完整示例、性能扩展 五个方面带你一次吃透它们。
#include
int tolower(int c); /* 若 c 是大写字母('A'~'Z'),返回对应小写字母;否则返回原值 */
int toupper(int c); /* 若 c 是小写字母('a'~'z'),返回对应大写字母;否则返回原值 */
注意:参数与返回值类型都是
int
,但实际只使用低 8 位;传入EOF(-1)
是合法的,函数会原样返回。
int tolower(int c) {
if (isupper(c))
return c | 0x20; /* 0x20 = 32,把第 5 位(bit5)置 1 即可 */
return c;
}
int toupper(int c) {
if (islower(c))
return c & ~0x20; /* 把第 5 位清 0 */
return c;
}
为什么用第 5 位?
ASCII 表中,'A'=0x41
, 'a'=0x61
,差值正好是 0x20
,因此可以用位运算实现极快的转换。
与 locale 的关系
在默认的 "C"
或 "POSIX"
locale 下,仅对 26 个英文字母生效;若切换到其它 locale(如 UTF-8),tolower('İ')
也可能返回 'i'
(视实现而定)。因此 不要假设 ASCII 以外字符的行为。
参数必须先用 unsigned char
强转
char
可能为有符号类型,值 >127 会被当作负数,导致未定义行为(UB)。
char buf[] = "Straße"; // 'ß' = 0xDF
for (size_t i = 0; buf[i]; ++i)
buf[i] = (char)tolower((unsigned char)buf[i]);
不能链式连续调用
toupper(tolower(c))
看起来可以“统一成大写”,但如果 c
不是字母,会两次经过函数调用,浪费 CPU 缓存行。更好的做法是先判断:
c = islower(c) ? toupper(c) : c;
宏 vs 函数
还提供了宏 _tolower(c)
/ _toupper(c)
,它们 不做范围检查,只在已知 c
为大/小写时调用,速度更快。使用前要自行保证条件。
#include
#include
#include
/* 忽略大小写的 strstr */
const char *strcasestr(const char *haystack, const char *needle) {
if (!*needle) return haystack;
for (; *haystack; ++haystack) {
const char *h = haystack;
const char *n = needle;
while (*h && *n && tolower((unsigned char)*h) == tolower((unsigned char)*n)) {
++h;
++n;
}
if (*n == '\0') return haystack;
}
return NULL;
}
int main(void) {
const char *text = "Hello, C Language!";
const char *key = "c lAn";
const char *pos = strcasestr(text, key);
if (pos)
printf("Found at offset %ld: %s\n", pos - text, pos);
else
puts("Not found");
return 0;
}
编译运行:
$ gcc demo.c -o demo && ./demo
Found at offset 7: C Language!
若字符串非常长(>1 MB),可借助 SSE2/AVX2 指令一次处理 16/32 字节:
#include
void tolower_avx2(char *s, size_t n) {
const __m256i delta = _mm256_set1_epi8('a' - 'A');
const __m256i upper = _mm256_set1_epi8('Z');
const __m256i lower = _mm256_set1_epi8('A' - 1);
size_t i = 0;
for (; i + 31 < n; i += 32) {
__m256i v = _mm256_loadu_si256((__m256i*)(s + i));
__m256i gt = _mm256_cmpgt_epi8(v, lower);
__m256i le = _mm256_cmpgt_epi8(upper, v);
__m256i mask = _mm256_and_si256(gt, le);
v = _mm256_add_epi8(v, _mm256_and_si256(mask, delta));
_mm256_storeu_si256((__m256i*)(s + i), v);
}
/* 尾部不足 32 字节回退到单字节 */
for (; i < n; ++i)
s[i] = (char)tolower((unsigned char)s[i]);
}
实测在 -O3
下可带来 3~5 倍 的吞吐量提升,但代码复杂、需要 CPU 支持,请按场景权衡。
函数 | 作用 | 关键点 |
---|---|---|
tolower | 大写 → 小写 | 先强转 unsigned char ;与 locale 相关 |
toupper | 小写 → 大写 | 同上;可配合 _toupper 宏提速 |
牢记 先做范围检查再转换,就能安全、高效地应对绝大多数文本处理需求。