【C语言】字符串与内存函数

字符串与内存拷贝函数

0. 目录

1. 前言

​ 在C语言中,库函数提供了能够处理字符串的一些函数,比如求取字符串长度的strlen等等,有了这些字符串函数我们可以更加快速的实现需求,下面我们将学习各种内置字符串函数的特点及其模拟实现。

2. 函数介绍

2.1 求取字符串长度函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strlen/?kw=strlen

语法格式:size_t strlen (const void* str)

作用:求取字符串的长度并返回

特点:

  • 字符串根据\0作为标志,而strlen函数统计字符串中\0之前的字符个数并返回

  • strlen函数参数指向的字符串必须包含\0,否则程序会崩溃

  • strlen函数的返回值为size_t类型,即是一个无符号数,这是一个易错点

    int main() {
        if (strlen("c") - strlen("c++") > 0) {
            printf("haha\n");
        } else {
            printf("hehe\n");
        }
        return 0;
    }
    

    这段代码实际上会打印haha,因为strlen函数返回的是无符号整数,那么-2在内存中用无符号整型表示实际上对应相当大的数

模拟实现:

  • 采用计数器方式

    size_t my_strlen(const char* str) {
        int count_len = 0;
        while (*str != '\0') {
            count_len++;
            str++;
        }
        return count_len;
    }
    
  • 采用指针-指针方式

    size_t my_strlen(const char* str) {
        char* ps = str;
        while (*ps != '\0') {
            ++ps;
        }
        return ps - str;
    }
    
  • 采用递归的方式

    size_t my_strlen(const char* str) {
        if (*str == '\0') {
            return 0;
        } else {
            return 1 + my_strlen(str + 1);
        }
    }
    

2.2 字符串拷贝函数

2.2.1 strcpy函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strcpy/?kw=strcpy

语法格式:char* strcpy(char* destination, const char* source)

作用:将source指向的字符串中内容拷贝到destionation指向的空间中

特点:

  • 源字符串必须以\0结尾,拷贝以\0作为结束标记

  • 拷贝字符连同\0一起作为拷贝内容

  • 目标空间必须足够大

  • 目标空间必须可变,即不可以直接使用常量字符串如char* dst = "hello world";

  • 函数返回目标空间起始地址

模拟实现:

char* my_strcpy(char* dst, const char* src) {
    // 当src指向\0时完成赋值拷贝,并且此时表达式结果为false停止循环
    char* ret = dst;
    while (*dst++ = *src++) {
        ;
    }
    return ret;
}
2.2.2strncpy函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strncpy/?kw=strncpy

语法格式:char* strncpy(char* destination, const char* source, size_t num)

作用:从source指向空间中拷贝num个字符到目标空间destination

模拟实现:跟strcpy函数类似

2.3 字符串拼接函数

2.3.1 strcat函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strcat/?kw=strcat

语法格式:char* strcat(char* destination, const char* source)

作用:将source所指向的字符串拼接到destionation所指向空间的末尾

特点:

  • 源字符串和目标字符串都必须以\0结尾
  • 目标空间必须可变
  • 目标空间必须足够大,可以容纳拼接后的字符串
  • 返回目标空间起始地址

模拟实现:

char* my_strcat(char* dst, const char* src) {
    char* ret = dst;
    while (*dst) {
        ++dst;
    }
    // 此时dst指向\0
    // 开始拷贝
    while (*dst++ = *src++) {
        ;
    }
    return ret;
}

注意事项:

  • 当字符串自己给自己追加时,即若源目标空间与目的空间一致,容易导致错误

    int main() {
        char arr[] = "hello";
        strcat(arr, arr); // 会出现错误
        return 0;
    }
    

    因为当进行赋值时,结束标志\0已经被赋值为别的内容,那么源空间无法找到字符串结束标记,循环会一直进行,到访问未知空间,就会发生错误。

2.3.2 strncat函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strncat/?kw=strncat

语法格式:char* strncat(char* destination, const char* source, size_t num)

作用:从source指向空间中取出num个字符追加到destination指向目标空间

模拟实现:与strcat函数类似

2.4 字符串比较函数

2.4.1 strcmp函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strcmp/?kw=strcmp

语法格式:int strcmp(const char* str1, const char* str2)

作用:返回两个字符串比较后的结果

条件返回值
str1 > str2返回大于0的数字
str1 == str2返回0
str1 < str2返回小于0的数字

模拟实现:

int my_strcmp(const char* str1, const char* str2) {
    while (*str1 && *str2 && *str1 == *str2) {
        // 说明当前比较字符相同,则继续比较
        str1++;
        str2++;
    }
    // 此时循环结束分别判断三种情况
    if (*str1 == '\0' && *str2 == '\0') {
        return 0;
    } else {
        // 此时说明字符串不相等
        return *str1 - *str2;
    }
}
2.4.2 strncmp函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strncmp/?kw=strncmp

语法格式:int strncmp(const char* destination, const char* source, size_t num)

作用:比较两个字符串前num个字符的大小

模拟实现:跟strcmp函数类似

2.5 查找字符串子串函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strstr/?kw=strstr

语法格式:char* strstr(const char* str1, const char* str2)

作用:该函数在str1指向字符串中查找是否包含str2

特点:

  • 如果str1中包含字符串str2,那么返回str1中字符串str2的起始地址,反之返回NULL

模拟实现:

char* my_strstr(const char* str1, const char* str2) {
    // 暴力枚举
    while (*str1) {
        while (*str1 && *str1 != *str2) {
            ++str1;
        }
        // 此时判断str1是否为\0
        if (*str1 == '\0') {
            // 说明没找到
            return NULL;
        } else {
            // 开始比较
            char* pstr1 = str1;
            char* pstr2 = str2;
            while (*pstr1 && *pstr2 && *pstr1 == *pstr2) {
                ++pstr1;
                ++pstr2;
            }
            // 此时判断两种情况
            if (*pstr2 == '\0') {
                // 说明找到了
                return str1;
            } else {
                // 说明该轮次没找到继续查找
                ++str1;
            }
        }
    }
}

2.6 字符串分隔函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strtok/?kw=strtok

语法格式:char* strtok(char* str, const char* sep)

作用:在字符串str中查找是否包含sep字符集合中特定字符,并将其置为\0作为标记,返回标记之前起始地址

特点:

  • sep参数是一个字符串,作为分隔符的集合
  • 当strtok函数的第一个参数不为NULL,那么函数将找到字符串str中第一个标记,置为\0并记录当前位置,返回开始查找位置到第一个标记位置该段区间的起始地址
  • 当strtok函数的第一个参数为NULL时,则以先前记录的位置开始查找后面的标记,并置为\0,返回该次区间的起始地址
  • 当strtok函数无法找到下一个标记位时返回空指针NULL

用法:

int main() {
    char arr[] = "123@163.com";
    char copy_arr[20] = "";
    char sep[] = "@.";
    strcpy(copy_arr, arr);
    char* ret;
    for (ret = strtok(copy_arr, sep); ret != NULL; ret = strtok(NULL, sep)) {
        printf("%s\n", ret);
    }
    return 0;
}

2.7 字符串错误信息函数

网站链接:https://legacy.cplusplus.com/reference/cstring/strerror/?kw=strerror

语法格式:char* strerror(int errnum)

作用:返回错误码对应的错误信息起始地址

特点:

  • 当发生字符串错误信息时,比如文件路径错误,C语言会将错误信息对应的错误码保存到全局变量errno中,通过这个变量,我们就可以使用strerror函数获取到对应的错误信息并打印

使用案例:

int main() {
    for (int i = 0; i < 10; ++i) {
        printf("%s\n", strerror(i));
    }
    return 0;
}

2.8 内存拷贝函数

2.8.1 memcpy函数

网站链接:https://legacy.cplusplus.com/reference/cstring/memcpy/?kw=memcpy

语法格式:void* memcpy(void* destination, void* source, size_t num)

作用:从source指向内容空间中拷贝num个字节到destination指向内存空间

特点:

  • 该函数与strcpy函数不同,即不关心\0是否存在
  • 参数num以字节作为单位
  • 该函数的实现只要求destination与source指向两块不同的内存空间,即不要求实现内存空间一致情况下的拷贝

模拟实现:

void* my_memcpy(void* dst, void* src, size_t num) {
    void* ret = dst;
    // 拷贝num个字节
    while (num--) {
        *((char*)dst) = *((char*)src); // 强制类型转换为char*
        dst = (char*)dst + 1;
        src = (char*)src + 1;
    }
    return ret;
}
2.8.2 memmove函数

网站链接:https://legacy.cplusplus.com/reference/cstring/memmove/?kw=memmove

语法格式:void* memmove(void* destination, void* source, size_t num)

作用:从source指向内容空间中拷贝num个字节到destination指向内存空间

特点:

  • 该函数功能与memcpy一致
  • 该函数要求与memcpy不一致,即一定可以实现重叠内存空间的拷贝

模拟实现:

void* my_memmove(void* dst, void* src, size_t num) {
    void* ret = dst;
    if (dst < src) {
        // 从左往右拷贝
        // 拷贝num个字节
        while (num--) {
            *((char*)dst) = *((char*)src); // 强制类型转换为char*
            dst = (char*)dst + 1;
            src = (char*)src + 1;
        }
    } else {
        // 从有往左拷贝num个字节
        while (num--) {
            *((char*)dst + num) = *((char*)src + num);
        }
    }
    return ret;
}
2.8.3 memcpy与memmove函数的区别

区别:memcpy函数不要求实现内存空间一致情况下的拷贝,一定可以实现重叠内存空间的拷贝

举例:

int main() {
   	char arr[] = "hello";
    memmove(arr + 2, arr, 2);
    printf("%s\n", arr);
    return 0;
}

分析:上述代码中,我们使用memmove函数的原因是arr + 2与arr本质上指向同一块内存空间,事实上该代码替换使用memcpy函数在VS2019等编译器也是可以实现该功能的,但是别的编译环境不一定支持,而memmove函数是一定可以实现该功能的。故此,如果想要拷贝同一块内存空间的数据,使用memmove函数更加可靠。

2.9 内存比较函数

网站链接:https://legacy.cplusplus.com/reference/cstring/memcmp/?kw=memcmp

语法格式:int memcmp(const void* ptr1, const void* ptr2, size_t num)

作用:比较从ptr1与ptr2开始的num个字节内容大小

返回值:

条件返回值
ptr1 > ptr2返回大于0的数字
ptr1 == ptr2返回0
ptr1 < ptr2返回小于0的数字

使用方式:

int main() {
    int arr1[] = {1, 2, 3, 4};
    int arr2[] = {1, 2, 4, 5};
    if (memcmp(arr1, arr2, 9) > 0) {
        printf("在前9个字节内容中, arr1大于arr2\n");
    } else if (memcmp(arr1, arr2, 9) == 0) {
        printf("在前9个字节内容中, arr1与arr2相等\n");
    } else {
         printf("在前9个字节内容中, arr1小于arr2\n");
    }
    return 0;
}

3. 总结

C语言提供的库函数还有很多,例如说字符串判断函数(是否为数字字符isdigit()、是否为小写字母islower())与字符串转换大小写函数(转换为小写tolower()、转换为大写toupper()),这些函数可以大大提高我们的开发效率!所以提倡大家可以参考官网教程进行学习使用。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值