C: 字符串函数

字符串函数就像工具箱里的扳手和螺丝刀——每种工具解决一种问题:复制、拼接、比较、查找。掌握它们,处理文本才能得心应手。

1. string.h概览

<string.h>是C标准库中字符串操作的核心头文件,提供复制、拼接、比较、查找等函数。使用前必须包含:

C
#include <string.h>

2. strcpy与strncpy

(1) strcpy

strcpy将源字符串复制到目标数组,包括\0

C
char dest[20];
strcpy(dest, "Hello World");
printf("%s\n", dest);

原型:char *strcpy(char *dest, const char *src);

⚠️ strcpy不检查目标缓冲区大小。如果源字符串比目标数组大,直接越界写入。使用前必须确保目标数组足够大。

(2) strncpy

strncpy限定了最大复制字节数,更安全:

C
char dest[6];
strncpy(dest, "Hello World", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
printf("%s\n", dest);

原型:char *strncpy(char *dest, const char *src, size_t n);

strncpy最多复制n个字节。如果源字符串长度不足n,剩余位置填\0;如果源字符串长度达到或超过n,不会自动添加\0。因此需要手动添加。

💡 始终为strncpy预留\0的位置,并在复制后手动补\0


3. strcat与strncat

(1) strcat

strcat将源字符串拼接到目标字符串末尾:

C
char buf[50] = "Hello";
strcat(buf, " World");
printf("%s\n", buf);

原型:char *strcat(char *dest, const char *src);

目标字符串原有的\0被覆盖,拼接完成后在末尾添加新的\0

⚠️ 目标数组必须有足够空间容纳拼接后的结果。strcat不检查缓冲区大小。

(2) strncat

strncat限定了最大追加字符数,且会自动添加\0

C
char buf[10] = "Hi";
strncat(buf, " World!", sizeof(buf) - strlen(buf) - 1);
printf("%s\n", buf);

原型:char *strncat(char *dest, const char *src, size_t n);

最多追加n个字符,然后自动补\0。比strncpy更安全,因为保证\0结尾。


4. strcmp与strncmp

(1) strcmp

strcmp逐字符比较两个字符串的字典序:

C
int result = strcmp("abc", "abd");

返回值含义:

C
if (strcmp(s1, s2) == 0) {
    printf("相等\n");
}
⚠️ 比较字符串用strcmp,不能用====比较的是两个指针的地址,不是字符串内容。

(2) strncmp

strncmp只比较前n个字符:

C
if (strncmp(str, "GET ", 4) == 0) {
    printf("GET请求\n");
}

适合比较前缀或限定比较范围。

▶ 示例

字符串排序:对一组字符串按字典序排列:

C
#include <stdio.h>
#include <string.h>

void sort_strings(char arr[][32], int n) {
    int i, j;
    char temp[32];

    for (i = 0; i < n - 1; i++) {
        for (j = 0; j < n - 1 - i; j++) {
            if (strcmp(arr[j], arr[j + 1]) > 0) {
                strcpy(temp, arr[j]);
                strcpy(arr[j], arr[j + 1]);
                strcpy(arr[j + 1], temp);
            }
        }
    }
}

int main(void) {
    char names[5][32] = {
        "david",
        "alice",
        "charlie",
        "bob",
        "eve"
    };
    int i;

    sort_strings(names, 5);

    for (i = 0; i < 5; i++) {
        printf("%s\n", names[i]);
    }
    return 0;
}
▶ 试一试
TEXT 📖 仅展示
alice
bob
charlie
david
eve

5. strlen

strlen返回字符串的有效长度(不含\0):

C
char s[] = "Hello";
printf("%zu\n", strlen(s));
printf("%zu\n", sizeof(s));

输出5和6。strlen数到\0为止,sizeof包含\0

strlen遍历字符串找\0,时间复杂度O(n)。如果多次使用同一字符串的长度,建议缓存结果:

C
size_t len = strlen(s);
for (size_t i = 0; i < len; i++) {
}

而非每次循环条件都调用strlen


6. strchr与strrchr

strchr在字符串中查找字符第一次出现的位置:

C
const char *p = strchr("Hello World", 'o');
if (p != NULL) {
    printf("找到: %s\n", p);
}

输出"o World"。返回指向该字符的指针,未找到返回NULL。

strrchr查找字符最后一次出现的位置:

C
const char *p = strrchr("Hello World", 'o');
if (p != NULL) {
    printf("最后出现: %s\n", p);
}

输出"orld"。


7. strstr

strstr在字符串中查找子串第一次出现的位置:

C
const char *p = strstr("Hello World", "World");
if (p != NULL) {
    printf("子串位置: %s\n", p);
}

输出"World"。未找到返回NULL。

查找所有出现位置:

C
const char *text = "abababab";
const char *p = text;
while ((p = strstr(p, "ab")) != NULL) {
    printf("位置 %ld\n", (long)(p - text));
    p++;
}

每次找到后指针前进一步,继续搜索后续位置。


8. 自定义字符串函数实现

理解标准函数的实现原理,有助于深入掌握字符串操作的本质。

(1) 自定义strlen

C
size_t my_strlen(const char *s) {
    size_t len = 0;
    while (s[len] != '\0') {
        len++;
    }
    return len;
}

(2) 自定义strcpy

C
char *my_strcpy(char *dest, const char *src) {
    char *d = dest;
    while ((*d++ = *src++) != '\0') {
    }
    return dest;
}

这个写法紧凑:*d++ = *src++先复制字符,再各自前进一步,直到复制了\0为止。

(3) 自定义strcmp

C
int my_strcmp(const char *s1, const char *s2) {
    while (*s1 == *s2) {
        if (*s1 == '\0') return 0;
        s1++;
        s2++;
    }
    return (unsigned char)*s1 - (unsigned char)*s2;
}

逐字符比较,相等就继续,遇到不等或\0就停止。返回差值表示大小关系。用unsigned char转换确保负字符值不会影响比较结果。

▶ 示例

自定义strcat和测试:

C
#include <stdio.h>

char *my_strcat(char *dest, const char *src) {
    char *d = dest;
    while (*d != '\0') {
        d++;
    }
    while ((*d++ = *src++) != '\0') {
    }
    return dest;
}

int main(void) {
    char buf[50] = "Hello";
    my_strcat(buf, ", ");
    my_strcat(buf, "World!");
    printf("%s\n", buf);
    return 0;
}
▶ 试一试
TEXT 📖 仅展示
Hello, World!

先找到dest末尾的\0,然后从该位置开始复制src的内容。

❓ 常见问题

Q strncpy为什么不能自动补\0
A strncpy设计初衷是填充固定长度缓冲区(如文件名字段),不足n时用\0填充,达到n时不加。作为安全strcpy使用时需手动补零。
Q strcmp返回的具体数值有意义吗?
A 只应判断正负零,不依赖具体数值。不同实现返回不同差值,可移植代码只用==0<0>0判断。
Q strcat多次拼接效率低怎么办?
A 每次strcat都要从dest开头扫描到末尾再拼接,多次拼接时总效率O(n^2)。可以手动记录当前末尾位置,直接从末尾开始拼接。
Q 自己实现字符串函数有什么意义?
A 理解底层原理、面试常考、嵌入式开发中可能没有标准库。但实际项目优先用标准库函数,经过充分测试和优化。

📖 小节

📝 作业

  1. 编写函数void str_tolower(char *s),将字符串中所有大写字母转为小写。
  2. 编写函数int str_count_char(const char *s, char ch),统计字符ch在字符串s中出现的次数。
  3. 不使用任何string.h函数,编写my_strstr实现子串查找功能。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏