
在 C 语言开发中,字符串处理是高频场景,而查找字符 / 子串更是核心操作。标准库<string.h>提供的 strchr、strrchr 和 strstr 函数,凭借高效、稳定的实现,成为避免 “重复造轮子” 的关键工具。本文全方位拆解这三个函数,真正做到 “知其然且知其所以然”。
在深入单个函数前,先通过一张对比表快速建立认知,明确三者的核心定位:
函数名 | 查找目标 | 查找方向 | 核心作用 | 适用场景 |
|---|---|---|---|---|
strchr | 单个字符 | 左→右(正向) | 找字符首次出现的位置 | 验证字符存在、单字符分割 |
strrchr | 单个字符 | 右→左(反向) | 找字符最后一次出现的位置 | 提取文件名、获取文件后缀 |
strstr | 子串(多字符) | 左→右(正向) | 找子串首次出现的起始位置 | 关键词匹配、子串提取 |
1. 函数简介
strchr(string character search)是最基础的字符查找函数,功能是在指定字符串中从左到右查找第一个匹配目标字符的位置,找到则返回指向该字符的指针,未找到则返回 NULL。
2. 函数原型
char *strchr(const char *str, int c);参数说明:
返回值:
3. 实现逻辑(伪代码)
strchr 的实现逻辑非常直观,核心是 “遍历字符串 + 字符比对”:
function strchr(str: const char*, c: int) -> char*:
if str == NULL: // 处理空指针(标准未定义,此处为安全增强)
return NULL
convert c to char (c_char = (char)c) // 关键转换:int→char
while *str != '\0': // 遍历到字符串结束符停止
if *str == c_char:
return (char*)str // 找到,返回当前指针
str = str + 1 // 指针后移
// 特殊情况:若目标是'\0',返回字符串末尾指针
if c_char == '\0':
return (char*)str
return NULL // 未找到4. 典型使用场景
5. 示例代码
#include <stdio.h>
#include <string.h>
int main() {
// 场景1:验证邮箱格式(是否含'@')
const char *email = "user@example.com";
char *at_pos = strchr(email, '@');
if (at_pos != NULL) {
printf("邮箱格式合法,'@'位置:%ld(索引)\n", at_pos - email);
printf("邮箱前缀:%.*s\n", (int)(at_pos - email), email); // 截取前缀
} else {
printf("邮箱格式非法(无'@')\n");
}
// 场景2:查找字符串末尾(目标为'\0')
const char *test_str = "hello";
char *end_pos = strchr(test_str, '\0');
printf("字符串长度:%ld\n", end_pos - test_str); // 输出5,等价于strlen
return 0;
}运行结果:

6. 注意事项
1. 函数简介
strrchr(string reverse character search)是 strchr 的 “反向版本”,功能是在字符串中从右到左查找最后一个匹配目标字符的位置,核心场景是获取 “最右侧” 的字符(如文件路径中的文件名)。
2. 函数原型
char *strrchr(const char *str, int c);3. 实现逻辑(伪代码)
与 strchr 的区别是 “先定位字符串末尾,再反向遍历”:
function strrchr(str: const char*, c: int) -> char*:
if str == NULL:
return NULL
convert c to char (c_char = (char)c)
const char *last_match = NULL // 记录最后一次匹配的位置
// 第一步:遍历到字符串末尾(找到'\0')
while *str != '\0':
if *str == c_char:
last_match = str // 更新匹配位置(覆盖之前的匹配)
str = str + 1
// 特殊情况:目标是'\0',返回末尾指针
if c_char == '\0':
return (char*)str
// 返回最后一次匹配的位置(未匹配则为NULL)
return (char*)last_match4. 典型使用场景
5. 示例代码(提取文件名)
#include <stdio.h>
#include <string.h>
// 从文件路径中提取文件名
char *get_filename(const char *path) {
if (path == NULL) return NULL;
// 找最后一个'/'
char *slash_pos = strrchr(path, '/');
if (slash_pos != NULL) {
return slash_pos + 1; // 跳过'/',返回文件名起始位置
}
return (char*)path; // 若无'/',路径本身就是文件名
}
int main() {
const char *paths[] = {
"/home/user/test.c", // 带完整路径
"readme.md", // 无路径(仅文件名)
"/root/docs/", // 以'/'结尾(无文件名)
NULL // 结束标志
};
for (int i = 0; paths[i] != NULL; i++) {
char *filename = get_filename(paths[i]);
printf("路径:%15s → 文件名:%s\n", paths[i], filename);
}
return 0;
}运行结果:

6. 注意事项
1. 函数简介
strstr(string substring search)是子串查找函数,功能是在指定字符串(主串)中从左到右查找第一个匹配目标子串的起始位置,是比 strchr 更灵活的查找工具(支持多字符匹配)。
2. 函数原型
char *strstr(const char *haystack, const char *needle);参数说明:
返回值:
3. 实现逻辑(伪代码:朴素算法)
strstr 的实现比前两个函数复杂,核心是 “主串遍历 + 子串比对”。以下是最易理解的朴素算法(标准库实现会优化,如 KMP 算法,但朴素算法更适合入门):
function strstr(haystack: const char*, needle: const char*) -> char*:
if haystack == NULL || needle == NULL:
return NULL
// 特殊情况:子串为空,返回主串
if *needle == '\0':
return (char*)haystack
// 主串遍历:到主串剩余长度 < 子串长度时停止
while *haystack != '\0':
const char *h = haystack // 主串当前比对位置
const char *n = needle // 子串当前比对位置
// 子串比对:逐字符匹配
while *h != '\0' && *n != '\0' && *h == *n:
h = h + 1
n = n + 1
// 若子串遍历完(说明完全匹配)
if *n == '\0':
return (char*)haystack
// 主串指针后移,继续下一轮比对
haystack = haystack + 1
// 未找到子串
return NULL4. 典型使用场景
5. 示例代码(关键词匹配与内容提取)
#include <stdio.h>
#include <string.h>
// 从URL中提取域名(假设URL格式为"http://xxx"或"https://xxx")
char *get_domain(const char *url) {
if (url == NULL) return NULL;
// 先找"http://",若找不到再找"https://"
char *http_pos = strstr(url, "http://");
if (http_pos != NULL) {
return http_pos + 7; // "http://"长度为7
}
char *https_pos = strstr(url, "https://");
if (https_pos != NULL) {
return https_pos + 8; // "https://"长度为8
}
return (char*)url; // 非HTTP/HTTPS协议,返回原URL
}
int main() {
// 场景1:关键词匹配(日志中找"error")
const char *log = "2024-05-20: info: start; 2024-05-20: error: file not found";
if (strstr(log, "error") != NULL) {
printf("日志中包含错误信息:%s\n", strstr(log, "error"));
} else {
printf("日志无错误\n");
}
// 场景2:提取URL域名
const char *urls[] = {
"http://www.baidu.com",
"https://github.com",
"ftp://ftp.example.com", // 非HTTP/HTTPS
NULL
};
for (int i = 0; urls[i] != NULL; i++) {
char *domain = get_domain(urls[i]);
printf("URL:%20s → 域名:%s\n", urls[i], domain);
}
return 0;
}运行结果:

6. 注意事项
为方便快速查阅,整理核心差异如下表:
对比维度 | strchr | strrchr | strstr |
|---|---|---|---|
查找目标 | 单个字符(int→char) | 单个字符(int→char) | 子串(const char*) |
查找方向 | 左→右(正向) | 右→左(反向) | 左→右(正向) |
返回值 | 首个匹配字符的指针,无则 NULL | 最后一个匹配字符的指针,无则 NULL | 子串起始位置的指针,无则 NULL |
特殊输入 1(c='\0') | 返回字符串末尾指针 | 返回字符串末尾指针 | 不适用(子串用 needle 参数) |
特殊输入 2(needle 空) | 不适用(无 needle 参数) | 不适用(无 needle 参数) | 返回 haystack 本身 |
时间复杂度(最坏) | O (n)(n 为主串长度) | O(n) | O (n*m)(朴素实现)/O (n+m)(优化) |
典型场景 | 验证字符存在、单字符分割 | 提取文件名、获取后缀 | 关键词匹配、子串提取 |
大小写敏感性 | 敏感(字符直接比对 ASCII) | 敏感 | 敏感 |
1. 空指针与越界问题
解决:调用函数前必须判断指针非空,如:
if (str == NULL) {
printf("错误:字符串指针为空\n");
return -1;
}2. 字符类型转换问题
3. 字符串无结束符问题
问:strchr 的参数c为什么声明为int而非char?
答:
核心原因是兼容EOF(End of File,值为 - 1):
问:如何用 strrchr 提取文件路径中的文件名(需处理边界情况)?
答:
步骤如下:
1. 判空:若路径指针为 NULL,返回 NULL;
2. 反向查找:用strrchr(path, '/')找到最后一个'/'的位置;
3. 处理边界:
代码实现:
char *get_filename(const char *path) {
if (path == NULL) return NULL;
char *slash = strrchr(path, '/');
if (slash == NULL) {
return (char*)path; // 无'/',返回原路径
}
// 若'/'是最后一个字符(如"/a/b/")
if (*(slash + 1) == '\0') {
return (char*)"";
}
return slash + 1;
}问:strstr 查找空字符串(needle="")时返回什么?为什么?
答:
返回主串haystack的指针(即主串的起始地址)。
原因依据 C 语言标准(ISO C99 及后续):
(注:实际开发中很少传入空字符串,但面试中常考此标准细节。)
问:
strchr(s, ‘\0’)的返回值是什么?它有意义吗?
答: 返回值是一个指向字符串 s末尾的 \0字符的指针。这是有意义的,并且是符合C标准定义的。因为 \0是字符串的一部分,strchr的职责就是查找字符,包括空字符。这个操作可以用来获得字符串的结束位置。
问:
strstr函数在查找空字符串(“”)时会返回什么?为什么?
答: 根据C语言标准,strstr(haystack, "")会返回指向 haystack的指针(即主串本身)。因为空字符串被定义为是任何字符串的子串,并且它出现在主串的开头位置。这是一个需要特别注意的边界情况。
问:如何自己实现一个
strrchr函数?请描述思路。
答: 思路如下:
NULL。
last_occurrence为 NULL,用于记录最后一次匹配的位置。
\0。
c匹配的字符时,就将 last_occurrence更新为当前位置的地址。
c是否是 \0。如果是,则返回当前指向 \0的指针(标准规定)。
last_occurrence。如果从未匹配过,它仍然是 NULL,符合函数规范。
strchr、strrchr 和 strstr 是 C 语言字符串处理的 “基石函数”,三者各有侧重:
掌握它们的实现逻辑、使用场景和边界处理,不仅能提升开发效率,更能在面试中展现对 C 标准库的深度理解。实际开发中,优先使用标准库函数(而非自行实现),因为标准库经过了性能优化和兼容性测试,稳定性更有保障。
博主简介 byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发,深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域。乐于技术分享与交流,欢迎关注互动! 📌 主页与联系方式
⚠️ 版权声明 本文为原创内容,未经授权禁止转载。商业合作或内容授权请联系邮箱并备注来意。