C —— 字符串操作

C ------ 字符串操作

我们上一次介绍一些关于内存操作的函数,今天我们来看看字符串操作的函数,如果有小伙伴对内存的操作还不了解,可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/146564648?spm=1001.2014.3001.5502

strlen


strlen是计算一个字符串从开头到结束符(不包括结束符)的总体长度,这里strlen以'/0'作为标志,意思就是strlen会统计一个字符串的开头,到\0。然后统计这中间有多少个字符,之后作为这个字符串长度的结果。

cpp 复制代码
#include<string.h>
int main()
{
    const char* str = "abc\0defg";
    printf("%d\n",strlen(str));
}

最后的结果为3:

strlen和sizeof的区别

同样的字符串,strlen和sizeof是不一样的:

cpp 复制代码
#include<string.h>
int main()
{
    const char* str = "abc\0defg";
    char str1[] = "abc\0defg";

    printf("%d\n",strlen(str));
    printf("%d\n", sizeof(str1));
}
特性 strlen(str) sizeof(str1)
作用对象 计算字符串长度(直到第一个 '\0' 计算数组总大小(包括所有字符和隐含的 '\0'
处理 '\0' 遇到第一个 '\0' 停止 计算整个数组(包括中间的 '\0' 和末尾的 '\0'
返回值 3"abc" 的长度) 9'a','b','c','\0','d','e','f','g','\0' 共 9 字节)

模拟实现strlen

cpp 复制代码
size_t my_strlen(const char* str)
{
    if (str == nullptr)
    {
        return 0;
    }

    const char* temp = str;
    int count = 0;

    while (*temp != '\0')
    {
        temp++;
    }
    
    return temp - str; //差值即为元素个数
}

char_traits::length

C++有个跟strlen很类似的,叫做char_traits::length:

cpp 复制代码
#include<string.h>
#include<string>
int main()
{
    const char* str = "abc\0defg";

    printf("%d\n",strlen(str));
    printf("%d\n", std::char_traits<char>::length(str));
}
特性 char_traits::length strlen
语言 C++(模板通用) C(仅 char
可扩展性 支持自定义字符类型 仅支持 char
性能 可能被编译器优化(如内联) 标准库实现
头文件 <string><string_view> <cstring>

strcpy

将 src 复制到 dest(包括 '\0'),需确保 dest 空间足够。返回值就是目标地址的指针:

cpp 复制代码
#include<string.h>
#include<string>

int main()
{
    const char* str = "abc\0defg";
    char str1[] = "abc\0defg";
    char str2[100];

    char* check = strcpy(str2, str);
    printf("str2的地址:%p\n", str2);
    printf("strcpy返回值:%p\n", check);
    printf("str2的内容:%s\n", str2);
}

模拟实现strcpy

cpp 复制代码
#include<string.h>
#include<string>

char* my_strcpy(char* dest, const char* sour)
{
    if (dest == nullptr || sour == nullptr)
    {
        return nullptr;
    }

    char* p = dest;

    while ((*p++ = *sour++) != '\0');
    return dest;

}

int main()
{
    const char* str = "abc\0defg";
    char str1[] = "abc\0defg";
    char str2[100];

    char* check = my_strcpy(str2, str);
    printf("str2的地址:%p\n", str2);
    printf("strcpy返回值:%p\n", check);
    printf("str2的内容:%s\n", str2);
}

strncpy

strncpy是strcpy的安全版本:
这里注意strncpy如果目标空间不足,不会复制终止符\0到目标地址中,需要我们手动保证终止符存在

cpp 复制代码
#include<string.h>
#include<string>

int main()
{
    const char* str = "abcdefg";
    char str1[] = "abc\0defg";  // 初始化为 "abc"(遇 '\0' 终止)
    char str2[8];               // 足够容纳 "abcdefg" + '\0'

    // 安全复制:限制最大长度为 sizeof(str2)-1
    strncpy(str2, str, sizeof(str2) - 1);
    str2[sizeof(str2) - 1] = '\0';  // 手动确保终止符

    printf("str2的地址:%p\n", (void*)str2);
    printf("strncpy返回值:%p\n", (void*)str2); // strncpy 返回 dest 指针
    printf("str2的内容:%s\n", str2);           // 输出 "abcdefg"

    return 0;

strcpy和strnpy的区别

特性 strcpy strncpy
终止符处理 总是复制 '\0' 仅在 src 长度 < n 时添加 '\0'
安全性 不安全(不检查长度) 部分安全(需手动确保终止符)
典型用途 已知目标空间足够时 需要限制复制长度时

strncpy模拟实现

strncpy的模拟实现主要关注到src < nsrc > n的情况:

cpp 复制代码
char* my_strncpy(char* dest,const char* sour,int n)
{
    if (dest == nullptr || sour == nullptr)
        return nullptr;

    char* p = dest;

    while (n-- > 0 && (*p++ = *sour++) != '\0'); //循环执行,拷贝字符

    while (n > 0)
    {
        *p++ = '\0';
        n--;
    }

    return dest;
}

strcat

strcat的功能是将 source 字符串(src)的内容 追加 到 destination 字符串(dest)的末尾。

cpp 复制代码
#include<string.h>
#include<string>

int main()
{
    char str[100] = "abcdefgh";
    strcat(str, "1234567");
    printf("%s\n", str);
}

模拟实现

cpp 复制代码
char* my_strcat(char* dest, const char* sour)
{
    if (dest == nullptr || sour == nullptr)
        return nullptr;

    //开始拷贝,先找到\0的位置
    char* p = dest;
    while (*p != '\0')
        p++;

    //开始拷贝
    while (*sour != '\0')
    {
        *p++ = *sour++;
    }

    *p = '\0';

    return dest;
}

目标区域有重叠

手册里面说,strcat的使用源地址和目标地址不能有重叠
我们可以自己追加自己试试

cpp 复制代码
int main()
{
    char str[100] = "abcdefgh";
    strcat(str, str + 1);
    printf("%s\n", str);
}

我们可以看到,出现无限循环复制

这是因为,strcat会找到\0,但是此时这种情况会把\0覆盖掉

strncat

strncat在原来strcat的基础上,传递了要追加的字符串的长度,并且会自动追加\0。

这样的话,有重叠区域也没有关系了:

cpp 复制代码
#include<string.h>

int main()
{
    char str[100] = "abcdefgh";
    strncat(str, str + 1,strlen(str));
    printf("%s\n", str);
}

模拟实现

cpp 复制代码
char* my_strncat(char* dest, const char* sour, size_t size)
{
    if (dest == nullptr || sour == nullptr)
        return nullptr;

    //寻找\0
    char* p = dest;
    while (*p != '\0')
        p++;

    //拷贝
    while (size > 0 && *sour != '\0')
    {
        size--;
        *p++ = *sour++;
    }

    *p = '\0';
    return dest;
}

int main()
{
    char str[100] = "abcdefgh";
    my_strncat(str, str + 1,strlen(str));
    printf("%s\n", str);
}

strcmp

strcmp用来比较两个字符串的大小:

cpp 复制代码
#include<string.h>

int main()
{
    char str1[100] = "we can't be friends";
    char str2[100] = "we canr't be friends";

    if (strcmp(str1, str2) > 0)
    {
        printf("str1 > str2");
    }
    else if (strcmp(str1, str2) == 0)
    {
        printf("str1 == str2");
    }
    else
    {
        printf("str1 < str2");
    }
}

模拟实现

cpp 复制代码
#include<string.h>

int my_strcmp(const char* str1, const char* str2)
{
    if (str1 == nullptr && str2 == nullptr)
        return 0;

    if (str1 == nullptr || str2 == nullptr)
    {
        if (str1 == nullptr)
            return *str2;

        if (str2 == nullptr)
            return *str1;
    }

    while (*str1 != '\0' && *str2 != '\0')
    {
        if (*str1 != *str2)
        {
            return *str1 - *str2;
        }

        str1++;
        str2++;
    }

    return *str1 - *str2;
}


int main()
{
    char str1[100] = "we can't be friends";
    char str2[100] = "we canr't be friends";

    if (my_strcmp(str1, nullptr) > 0)
    {
        printf("str1 > str2");
    }
    else if (my_strcmp(str1, str2) == 0)
    {
        printf("str1 == str2");
    }
    else
    {
        printf("str1 < str2");
    }
}

strncmp

strncmp 是 C 标准库中的字符串比较函数,用于比较两个字符串的前 n 个字符
这里就不向大家演示了,大家可以自己下去试试。

strstr

strstr用于寻找字串:

strtok

strtok 是 C 标准库中的字符串分割函数,用于按照指定的分隔符将字符串拆分为多个子串:

cpp 复制代码
#include<string.h>
#include<string>

int main()
{
    char str[] = "apple,banana,orange";
    char* token = strtok(str, ",");  // 首次调用传入字符串

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ",");   // 后续调用传入NULL
    }

    return 0;
}

执行过程图解

初始字符串:

复制代码
a p p l e , b a n a n a , o r a n g e \0
^
|
str

第一次strtok后:

复制代码
a p p l e \0 b a n a n a , o r a n g e \0
^         ^
|         |
apple    下次从这里开始找

第二次strtok后:

复制代码
a p p l e \0 b a n a n a \0 o r a n g e \0
                    ^     ^
                    |     |
                    banana 下次从这里开始

  1. strtok修改原字符串 (把逗号变成\0
  2. 第一次调用传原始字符串,之后都传NULL
  3. 就像用剪刀剪绳子:
    • 第一次告诉你从哪里开始剪
    • 之后每次接着上次剪断的地方继续

可以试着把代码改成这样观察:

c 复制代码
char str[] = "apple,banana,orange";
printf("Before: %s\n", str);  // 打印原始字符串

char *token = strtok(str, ",");
while (token != NULL) {
    printf("Token: %s\n", token);
    printf("Remaining: %s\n", token + strlen(token) + 1);  // 看剩余部分
    token = strtok(NULL, ",");
}

printf("After: %s\n", str);  // 打印被修改后的字符串

这样就能看到字符串是如何被一步步修改的。

相关推荐
字节旅行者18 分钟前
C++中如何使用STL中的list定义一个双向链表,并且实现增、删、改、查操作
开发语言·数据结构·c++·链表
搞程序的心海20 分钟前
用Scala玩转Flink:从零构建实时处理系统
开发语言·flink·scala
x66ccff28 分钟前
[特殊字符] Pandas 常用操作对比:Python 运算符 vs Pandas 函数
开发语言·python·pandas
逆风优雅1 小时前
python 爬取网站图片的小demo
开发语言·python
m0_616188491 小时前
PDF预览-搜索并高亮文本
开发语言·javascript·ecmascript
IT瘾君1 小时前
Java基础:Logback日志框架
java·开发语言·logback
stevenzqzq1 小时前
kotlin中主构造函数是什么
开发语言·python·kotlin
Tttian6221 小时前
Python办公自动化(2)对word&pdf的操作
开发语言·python
美美打不死2 小时前
webpack js 逆向 --- 个人记录
开发语言·javascript·webpack
等雨季2 小时前
scala编程语言
开发语言·scala