在C语言中,并没有像Java或Python那样内置的 lastIndexOf 方法,你可以自己实现一个函数来查找子字符串在主字符串中最后一次出现的位置。

(图片来源网络,侵删)
下面我将为你提供几种实现方式,从最简单到更健壮的版本,并解释它们的优缺点。
简单实现:从后向前遍历
这是最直观的方法,我们从主字符串的末尾开始,向前遍历,寻找子字符串的起始位置。
代码实现
#include <stdio.h>
#include <string.h> // 用于 strlen 函数
/**
* @brief 查找子字符串在主字符串中最后一次出现的位置
*
* @param mainStr 主字符串
* @param subStr 要查找的子字符串
* @return int 返回子字符串的起始索引,如果未找到则返回 -1
*/
int lastIndexOf(const char *mainStr, const char *subStr) {
// 获取主字符串和子字符串的长度
int mainLen = strlen(mainStr);
int subLen = strlen(subStr);
// 如果子字符串为空,或者比主字符串还长,直接返回 -1
if (subLen == 0 || subLen > mainLen) {
return -1;
}
// 从主字符串中可能包含子字符串的最后一个起始位置开始向前遍历
// 最后一个可能的起始位置是 mainLen - subLen
for (int i = mainLen - subLen; i >= 0; i--) {
int j;
// 检查从位置 i 开始,是否与子字符串匹配
for (j = 0; j < subLen; j++) {
if (mainStr[i + j] != subStr[j]) {
break; // 不匹配,跳出内层循环
}
}
// 如果内层循环正常结束(j == subLen),说明找到了匹配项
if (j == subLen) {
return i; // 返回起始索引
}
}
// 遍历完整个主字符串都没有找到
return -1;
}
int main() {
const char *str = "hello world, hello programming, hello C";
const char *sub1 = "hello";
const char *sub2 = "world";
const char *sub3 = "goodbye";
int index1 = lastIndexOf(str, sub1);
if (index1 != -1) {
printf("'%s' 的最后出现位置在: %d\n", sub1, index1);
} else {
printf("未找到 '%s'\n", sub1);
}
int index2 = lastIndexOf(str, sub2);
if (index2 != -1) {
printf("'%s' 的最后出现位置在: %d\n", sub2, index2);
} else {
printf("未找到 '%s'\n", sub2);
}
int index3 = lastIndexOf(str, sub3);
if (index3 != -1) {
printf("'%s' 的最后出现位置在: %d\n", sub3, index3);
} else {
printf("未找到 '%s'\n", sub3);
}
return 0;
}
代码解释
- 获取长度:使用
strlen()获取主字符串和子字符串的长度。 - 边界检查:
- 如果子字符串长度为0,根据约定,通常认为可以匹配任何位置(比如返回0或字符串长度),这里我们简单返回-1。
- 如果子字符串比主字符串还长,那肯定找不到,直接返回-1。
- 反向遍历:
for循环的起始点是mainLen - subLen,这是子字符串能在主字符串中出现的最后一个可能的起始位置,在 "hello" (len=5) 中查找 "lo" (len=2),最后一个可能的起始位置是 5 - 2 = 3。 - 匹配检查:对于每一个可能的起始位置
i,我们用一个内层for循环来检查mainStr[i...i+subLen-1]是否与subStr完全匹配。 - 返回结果:
- 如果找到匹配,立即返回当前的起始索引
i。 - 如果外层循环结束都没有找到,说明子字符串不存在于主字符串中,返回
-1。
- 如果找到匹配,立即返回当前的起始索引
使用标准库函数 strstr
C标准库中的 strstr 函数可以查找子字符串第一次出现的位置,我们可以利用它来实现 lastIndexOf,思路是:不断查找,直到最后一次找到为止。
代码实现
#include <stdio.h>
#include <string.h>
int lastIndexOf_strstr(const char *mainStr, const char *subStr) {
int last_index = -1;
const char *ptr = mainStr;
// strstr 在找不到时会返回 NULL
while ((ptr = strstr(ptr, subStr)) != NULL) {
// 记录下这次找到的位置
last_index = ptr - mainStr;
// 将指针移动到当前匹配位置的下一个字符,继续向后查找
ptr++;
}
return last_index;
}
int main() {
const char *str = "hello world, hello programming, hello C";
const char *sub1 = "hello";
const char *sub2 = "world";
int index1 = lastIndexOf_strstr(str, sub1);
printf("使用 strstr 查找 '%s': %d\n", sub1, index1);
int index2 = lastIndexOf_strstr(str, sub2);
printf("使用 strstr 查找 '%s': %d\n", sub2, index2);
return 0;
}
代码解释
- 初始化:
last_index初始化为-1,表示未找到。ptr指向主字符串的开头。 - 循环查找:
while ((ptr = strstr(ptr, subStr)) != NULL)是一个经典的C语言写法。strstr(ptr, subStr)从ptr指向的位置开始查找subStr。- 如果找到,
strstr返回匹配位置的指针;如果没找到,返回NULL。 - 赋值表达式
ptr = strstr(...)的值就是strstr的返回值。
- 记录位置:每次找到后,通过指针算术
ptr - mainStr计算出起始索引,并更新last_index。 - 移动指针:
ptr++是关键一步,它将搜索的起点移动到当前匹配的下一个字符,这样下一次strstr调用就能找到下一个(或同一个,如果子字符串为空)匹配项。 - 返回结果:当
strstr最终返回NULL时,循环结束,last_index中保存的就是最后一次找到的位置(如果找到了的话),否则仍然是-1。
两种方法的比较
| 特性 | 方法1 (从后向前遍历) | 方法2 (使用 strstr) |
|---|---|---|
| 效率 | 通常更优,一旦找到最后一个匹配项,就会立即返回,无需遍历字符串的开头部分。 | 可能较低,在最坏的情况下(查找的字符是 "a",字符串是 "aaaaaaaa"),它会遍历整个字符串的每一个位置。 |
| 代码复杂度 | 逻辑稍显复杂,需要手动实现字符串比较。 | 代码更简洁,利用了标准库函数,可读性更高。 |
| 依赖库 | 只需要 strlen,依赖非常基础。 |
依赖 strstr 函数。 |
- 如果追求性能,特别是在处理长字符串时,方法1(从后向前遍历)是更好的选择。
- 如果追求代码简洁和可读性,并且不介意可能的性能开销,方法2(使用
strstr)是一个非常优雅的解决方案。
对于大多数应用场景,两种方法都能很好地工作,你可以根据你的具体需求来选择。

(图片来源网络,侵删)

(图片来源网络,侵删)
