C语言——12种字符串函数的使用

strlen

size_t strlen ( const char * str );
//计算字符串长度,\0不计入。

三种方法模拟实现:

//递归
size_t my_strlen1(const char* p) {
    assert(p != NULL);
    if (*p != '\0')
        return 1 + my_strlen1(p + 1);
    else
        return 0;
}

//指针-指针
size_t my_strlen2(const char* p) {
    assert(p != NULL);
    char* str = p;
    while (*p != '\0') {
        p++;
    }
    return p - str;
}

//迭代
size_t my_strlen3(const char* p) {
    assert(p != NULL);
    size_t count = 0;
    while (*p++ != '\0') {
        count++;
    }
    return count;
}

使用:

void strlen_test() {
    char arr[20] = "hello world";
    printf("%d\n", strlen1(arr));
}

strcpystrncpy

char * strncpy ( char * destination, const char * source, size_t num );
//将source的n个字符复制到destination
  • 如果source字符串长度小于num,在多出的地方复制\0;

  • 如果source字符串长度大于等于num,复制后尾部无\0

char * strcpy ( char * destination, const char * source );
//将source复制到destination,包括\0

模拟实现:

char* my_strncpy(char* des, const char* source, size_t n)
{
    assert(des != NULL && source != NULL);
    char* ret = des;
    while (n--) {
        *des = *source;
        if (*source) {
            des++;
            source++;
        }
        else {
            des++;
        }
    }
    return ret;
}

char* my_strcpy(char* des, const char* source)
{
    int n = my_strlen1(source);
    return my_strncpy(des, source, n+1); //尾部要有\0, n+1
    
    //不使用strncpy的实现
    /*assert(des && source);
    char* ret = des;
    while (*des++ = *source++){
        ;
    }
    return ret;*/
}

使用:

void strcpy_test() {
    char dest[20] = { 0 };
    char sorc1[] = "hello world";
    char sorc2[] = "friend";
    strcpy(dest, sorc1);
    printf("%s\n", dest);
    strncpy(dest + 6, sorc2, 6);
    printf("%s\n", dest);
}

//结果:
//hello world
//hello friend

strcmpstrncmp

int strcmp ( const char * str1, const char * str2 );
//比较每个字符串的第一个字符。如果它们彼此相等,则继续以下对,直到字符不同或达到终止空字符。
int strncmp ( const char * str1, const char * str2, size_t num );
//比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下对,
//直到字符不同,直到达到终止的空字符,或者直到两个字符串中的 num 字符匹配,以先发生者为准。

模拟实现:

int my_strncmp(const char* p1, const char* p2, size_t n){
    assert(p1 && p2);
    while (n--) {
        if (*p1 == *p2) {
            if (*p1 == '\0')
                return 0;
            else {
                p1++;
                p2++;
            }
        }
        else
            return *p1 - *p2;
    }
}

int my_strcmp(const char* p1, const char* p2){
    size_t n1 = my_strlen1(p1);
    size_t n2 = my_strlen1(p2);
    size_t n = n1 > n2 ? n2 : n1;
    return my_strncmp(p1, p2, n);
    
    //不调用strncmp的模拟实现
    /*assert(p1 && p2);
    while (*p1 == *p2){
        if (*p1 == '\0')
            return 0;
        else{
            p1++;
            p2++;
        }
    }
    return *p1 - *p2;*/
}

使用:

void strcmp_test() {
    char str1[] = "hello friend";
    char str2[] = "hello world";
    printf("%d\n", strcmp(str1, str2));
    printf("%d\n", strncmp(str1, str2, 5));
}

strcatstrncat

char * strcat ( char * destination, const char * source );
//将source字符串的副本追加到destination字符串。
//destination中的终止空字符被源的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符。
char * strncat ( char * destination, const char * source, size_t num );
//将源的第一个数字字符追加到目标,外加一个终止空字符。
//如果源中 C 字符串的长度小于 num,则仅复制终止空字符之前的内容。

模拟实现:

char* my_strncat(char* des, const char* source, size_t n){
    assert(des && source);
    char* ret = des;
    //找到\0
    while (*des){
        des++;
    }
    //拼接
    while (n--){
        *des = *source;
        if (*source){
            des++;
            source++;
        }
        else{
            break;
        }
    }
    //如果while结束函数未结束,最后一个字符不是\0,尾部加\0
    *des = '\0';
    return des;
}

char* my_strcat(char* des, const char* source){
    /*assert(des && source);
    char* ret = des;
    while (*des){
        des++;
    }
    while (*des++ = *source++){
        ;
    }
    return ret;*/
    int n = my_strlen1(source);
    return my_strncat(des, source, n);
}

使用:

void strcat_test() {
    char str1[30] = { 0 };
    char str2[] = "hello world";
    char str3[] = "hello friend";
    strncat(str1, str2, 6);
    strcat(str1, str3+6);
    printf(str1);
}
//结果:
//hello friend

strtok

char * strtok ( char * str, const char * delimiters );
//str是要拆分的字符串的地址,delimiters是分割符所组成字符串的地址

使用:

第一次调用时str直接传字符串地址即可,但是之后的调用str都是NULL


{
    char arr[50] = "I.am.learning.strtok";
    char div[] = ".";
    char* ret = NULL;
    for (ret = strtok(arr, div); ret != NULL;ret = strtok(NULL, div)) {
        printf("%s ", ret);
    }
}
//结果:
//I am learning strtok 

strerror

使用此函数时,不仅需要引用头文件string.h,还要有errno.h

char * strerror ( int errnum );
//解释 errnum 的值,生成一个字符串,其中包含描述错误条件的消息

使用:

void strerror_test()
{
    FILE* p = fopen("test.txt", "r");
    if (p == NULL) {
        printf("%s\n", strerror(errno));
    }
}
//结果:
//No such file or directory

值得一提的是:perror也有类似的作用,而且使用起来可能更方便,perror在头文件stdio.h中。

void strerror_test()
{
    FILE* p = fopen("test.txt", "r");
    if (p == NULL) {
       perror("fopen");
    }
}
//结果:
//fopen: No such file or directory

strstr

char * strstr ( char * str1, const char * str2 );
//返回在str1中第一次出现str2的指针

模拟实现:

char* my_strstr(const char* str1, const char* str2) {
    assert(str1 && str2);
    char* cur1 = NULL;
    char* cur2 = NULL;
    char* ret = str1;
    while (*ret) {
        cur1 = ret;
        cur2 = str2;
        while (*cur1 && *cur2 && *cur1 == *cur2) {
            cur1++;
            cur2++;
        }
        if (*cur2 == '\0') {
            return (char*)ret;
        }
        ret++;
    }
    return NULL;
}

使用:

void strstr_test() {
    char arr1[] = "qqqqwertaa";
    char arr2[] = "qwert";
    printf(strstr(arr1, arr2));
}

memcpymemmove

void * memcpy ( void * destination, const void * source, size_t num );

void * memmove ( void * destination, const void * source, size_t num );

二者都是将source的 num个字符复制到destination,但是当复制的内容存在空间重叠时,比如:

int arr[10] = {1,2,3,4,5};
memcpy(arr+1,arr,3);

这时对于memcpy,就会这样执行arr[0]->arr[1],arr[1]->arr[2],arr[2]->arr[3],这就会发生问题,

arr数组变成{1,1,1,1,5},与预期的结果{1,1,2,3,5}相差甚远。如果使用memmove就不会发生这个问题。

但是,现在的大多数编辑器对于两个函数的实现其实是相同的,在使用时也不会出现上述的问题。

模拟实现:

void* my_memcpy(void* dest, const void* sorc, size_t n){
    assert(dest && sorc);
    void* ret = dest;
    while (n--) {
        *(char*)dest = *(char*)sorc;
        dest = (char*)dest+1;
        sorc = (char*)sorc+1;
    }
    return ret;
}

void* my_memmove(void* dest, const void* sorc, size_t n) {
    assert(dest && sorc);
    void* ret = dest;
    if (dest > sorc) {
        //从后往前复制
        while (n--) {
            *((char*)dest + n) = *((char*)sorc + n);
        }
    }
    else {
        //从前向后复制,同memcpy
        while (n--) {
            *(char*)dest = *(char*)sorc;
            dest = ++(char*)dest;
            sorc = ++(char*)sorc;
        }
    }
    return ret;
}

使用:

void memcpy_test() {
    int arr1[10] = { 1,2,3,4,5 };
    int arr2[5] = { 6,7,8,9,10 };
    memcpy(arr1 + 5, arr2, 20);
    memmove(arr1 + 5, arr1, 20);
    for (int i = 0;i < 10;i++) {
        printf("%d ", arr1[i]);
    }
}
//结果:
//1 2 3 4 5 1 2 3 4 5

memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//将ptr1的前num字节与ptr2的比较

模拟实现:

int my_memcmp(const void* str1, const void* str2, size_t n) {
    assert(str1 && str2);
    while (n--) {
        if (*(char*)str1 == *(char*)str2) {
            ++(char*)str1;
            ++(char*)str2;
        }
        else
            return *(char*)str1 - *(char*)str2;
    }
    return 0;
}

使用:

void memcmp_test()
{
    int arr1[5] = { 1,2,3,4,6 };
    int arr2[5] = { 1,2,3,4,5 };
    printf("%d\n", my_memcmp(arr1, arr2, 16));
    printf("%d\n", my_memcmp(arr1, arr2, 20));
    printf("%d\n", my_memcmp(arr1, arr2+1, 20));
}

memset

void * memset ( void * ptr, int value, size_t num );
//将ptr开始的num个字节的值都修改为value

模拟实现:

void* my_memset(void* dest, int val, size_t n) {
    assert(dest);
    void* ret = dest;
    while (n--){
        *(char*)dest = (char)val;
        ++(char*)dest;
    }
    return ret;
}

使用:

void memset_test() 
{
    char arr[20] = "hello world";
    my_memset(arr, 'x', 5);
    my_memset(arr+6, 'y', 5);
    printf(arr);
}
//结果:
//xxxxx yyyyy
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值