金丹三层 —— 内存函数和字符串操作函数详解

目录

一.修炼必备

二.字符串操作的常用函数

2.1 strlen()函数

2.2 strcpy()函数

2.3 strcat()函数

2.4 strcmp()函数

2.5 strstr()函数

2.6 strtok()函数

2.7 strerror()函数

三.内存操作的常用函数

3.1 memcpy()函数

3.2 memmove()函数

3.3 memcmp()函数

结语


一.修炼必备

1.入门必备:VS2019社区版,下载地址:Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 (microsoft.com)

2.趁手武器:印象笔记/有道云笔记

3.修炼秘籍:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 (nowcoder.com)

4.雷劫必备:leetcode 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

注:遇到瓶颈怎么办?百度百科_全球领先的中文百科全书 (baidu.com)

二.字符串操作的常用函数

2.1 strlen()函数

1.函数作用

        ——求解字符串'\0'之前的字符个数

2.函数原型

size_t strlen(const char* str);

3.strlen函数的细节说明

1) strlen函数返回的是'\0'之前的字符个数,不包含'\0'

2) strlen所求的字符串必须以'\0'结束,若不以'\0'结束,不知道什么遇到'\0',即字符串大小无法判断

3) strlen函数的返回值是无符号整型【易错】

4.代码解释

        代码1:

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

int main()
{
    char arr[] = "abcdef";
    printf("%d\n", strlen(arr));//6
    printf("%d\n", sizeof(arr));//7(包含'\0')
    return 0;
}

注:

1)sizeof计算的数组的大小,包含'\0'字符的;strlen函数是计算字符串长度,不包含'\0'

2)sizeof是操作符,strlen是函数

        代码2:

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

int main()
{
    char arr[] = "abcdefg\0ghij";
    printf("%d\n", strlen(arr));//7
    return 0;
}

注:strlen计算的是字符串中最先遇到'\0'之前字符的字符个数

        代码3:

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

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abcdefg";
    
    if (strlen(arr1) - strlen(arr2) > 0)//6 - 7 
    {
        printf("strlen(arr1) > strlen(arr2)\n");
        //输出strlen(arr1) > strlen(arr2)
    }
    else
    {
        printf("strlen(arr1) < strlen(arr2)\n");
    }
    return 0;
}

注:strlen函数返回的值是无符号整型的,无符号整型之间相减的结果若是小于0的,这个小于0的数也会被转成无符号数,即大于0的数

5.模拟实现strlen函数

        1)法一:计数器

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* str)
{
    assert(str);//防止str是空指针
    int count = 0;//计数器
    
    while (*str++ != '\0')
        count++;
    return count;
}

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n", len);
    return 0;
}

        2)法二:指针 - 指针

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* str)
{
    char* start = str;
    assert(str);//防止str是空指针
    
    while (*str != '\0')
        str++;
    //指针-指针
    return str - start;
}

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n", len);
    return 0;
}

注:指针 - 指针在指向同一块内存空间的情况下,相减得到的该空间的元素之差

        3)法三:递归

#include <stdio.h>
#include <assert.h>

size_t my_strlen(const char* str)
{
    assert(str);
    if (*str == '\0')
        return 0;
    //长度+1且指向下一个字符的地址
    return 1 + my_strlen(str + 1);
}

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("%d\n", len);
    return 0;
}

2.2 strcpy()函数

1.函数作用

        —— 字符串拷贝,把一个字符串拷贝给另外一个字符串

2.函数原型

char* strcpy(char* dest, const char* src);

3.strcpy函数的细节说明

1)源字符串必须以'\0'结束

2)strcpy函数在进行拷贝的时候,会把源字符串的'\0'拷贝到目标空间中

3)目标空间需要足够大,能放下整个源字符串的内容

4)目标空间必须可修改

4.代码解释

        1)代码1:

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

int main()
{
    char arr1[10] = "abcdef";
    char arr2[5] = { 'a','b','c','d','f' };
    strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

注:以上代码会崩溃,源字符串必须以'\0'结束,但是在arr2字符串,并没有以'\0'结束

        代码2:

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

int main()
{
    char arr1[10] = { 0 };
    char arr2[5] = "abcd";
    strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

        调试查看:

        代码3:

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

int main()
{
    char arr1[5] = { 0 };
    char arr2[] = "abcdefg";
    strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

注:以上代码崩溃,因为目标空间的大小没有源字符串空间的大小大;

在使用strcpy函数的时候需要保证:目标空间 > 源字符串空间

        运行结果如图:

        代码4:

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

int main()
{
    const char arr1[] = "abcdefh";
    char arr2[] = "abcdef";
    strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

注:以上代码能通过,但是不建议这样做,我们使用const修饰arr1后,这个arr1中的值不能被修改,但是strcpy确强制性的把他赋值进去了,这样不可取

5.模拟实现strcpy函数

#include <stdio.h>
#include <assert.h>

char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);//防止空指针
    char* start = dest;
    
    //循环遍历进行赋值
    while (*dest++ = *src++);
    return start;
}

int main()
{
    char arr1[10] = { 0 };
    char arr2[] = "abcdefg";
    char* ret = my_strcpy(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

6.strncpy函数

        1)函数作用

                ——拷贝源字符串的num个字符到目标空间中去

        2)函数原型

char* strncpy ( char* dest, const char* src, size_t num);

        3)注:若是拷贝的源字符串的长度小于num,则拷贝完源字符串后补0,直到num个

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

int main()
{
    char arr1[10] = { 0 };
    char arr2[5] = "abcd";
    strncpy(arr1, arr2, 8);
    printf("%s\n", arr1);
    return 0;
}

        调试后的结果:

2.3 strcat()函数

1.函数作用

        ——字符串连接函数,把源字符串连接到目标字符串的后面

2.函数原型

char* strcat(char* dest, const char* src);

3.strcat函数的细节说明

1)源字符串必须以'\0'结束

2)目标空间必须足够大,能够容纳下源字符串的内容

3)目标空间必须可修改

4.代码解释

        1)代码1:

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

int main()
{
    char arr1[20] = "abcdef";
    char arr2[6] = { 'a','b','c','d','e','f' };
    strcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

注:以上代码会崩溃,因为源字符串没有以'\0'结尾,我们调试看看。

        调试照片:

        代码2:

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

int main()
{
    char arr1[20] = { 0 };
    char arr2[6] = "abcde";
    strcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

        调试照片如下:

        代码3:

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

int main()
{
    const char arr1[20] = { 0 };
    char arr2[6] = "abcde";
    strcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

以上代码会如愿的得到结果,但是我们使用的目标空间应该可以修改,但是使用const后导致目标空间不可修改,这样不可取

5.模拟实现

#include <stdio.h>
#include <assert.h>

char* my_strcat(char* dest, const char* src)
{
    assert(dest && src);//判断空指针
    char* start = dest;
    
    //让dest先到尾部
    while (*dest != '\0')
        dest++;
    //在dest的后面连接src            
    while (*dest++ = *src++);
    return start;
}

int main()
{
    char arr1[10] = "abc";
    char arr2[5] = "efgh";
    char* ret = my_strcat(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

6.strncat函数

        1)函数作用

                ——连接num个源字符串的字符到目标空间中

        2)函数原型

char * strncat ( char * destination, const char * source, size_t num );

        3)细节说明

1> 添加num个源字符串的字符到目标空间的时候,添加完会在后面添加'\0'

2> 若是num的长度大于源字符串的长度,那么不足的地方补'\0'

        4)代码解释

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

int main()
{
    char arr1[15] = "abcde";
    char arr2[5] = "oxyz";
    strncat(arr1, arr2, 6);
    printf("%s\n", arr1);
    return 0;
}

2.4 strcmp()函数

1.函数作用

        —— 字符串比较函数,两个字符串相等的话返回0

2.函数原型

int strcmp(const char* str1, const char* str2);

3.strcmp函数的细节说明

1)strcmp函数的返回值

        a. < 0:说明字符串1小于字符串2

        b. > 0:说明字符串1大于字符串2

        c. = 0:说明字符串1等于字符串2

2)strcmp函数的比较规则:每个字符都相等则两个字符串就相等【达到'\0'结束】,一旦有一个字符不相等,就返回

4.代码解释

        代码1:

#include <stdio.h>

int main()
{
    char* p = "abcdef";
    if ("abcdef" == (*p))
    {
        printf("true\n");
    }
    else
    {
        printf("false\n");//false
    }
    return 0;
}

注:== 比较的是值是否相等,若是比较地址的话,判断两个地址的首地址是否相等

        代码2:

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

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "acdefg";
    int ret = strcmp(arr1, arr2);
    printf("%d\n", ret);//-1
    return 0;
}

注:在VS中,字符串进行比较,一旦比较出了结果:

1)字符串1 > 字符串2,VS会返回一个1;

2)字符串1 < 字符2,VS会返回一个-1;

3)字符串1 = 字符串2,VS会返回一个0【Linux的gbd环境下返回的是两者的字符差】

5.模拟实现strcmp函数

#include <stdio.h>
#include <assert.h>

int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 && str2);
    while (*str1)
    {
        //判断是否相等
        if (*str1 == *str2)
        {
            str1++;
            str2++;
        }
        else
        {
            //不相等就跳出循环
            break;
        }
    }
    return *str1 - *str2;//返回值
}

int main()
{
    char arr1[] = "abc";
    char arr2[] = "ab";
    int ret = my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

6.strncmp函数

        1)函数作用

                ——比较前num个字符,判断是否相等

        2)函数原型

int strncmp ( const char * str1, const char * str2, size_t num );

        3)该函数和strcmp函数的作用一样,除了strncmp比较的是num个字节的字符

2.5 strstr()函数

1.函数作用

        ——在字符串1中找字符串2是否存在

2.函数原型

char* strstr(const char* str1, const char* str2);

3.strstr函数的细节说明

1)strstr在查找字符串的时,若找到了,返回的是找到的起始地址,一直到字符串结束

2)若是没有找到,则返回一个空指针NULL

4.代码解释

        代码1:

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

int main()
{
    char arr1[] = "abcdeeeefghijk";
    char arr2[] = "efg";
    char* ret = strstr(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

        调试图片如下:

        代码2:

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

int main()
{
    char arr1[] = "abcdeeehijk";
    char arr2[] = "efg";
    char* ret = strstr(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

        调试图片如下:

5.模拟实现strstr函数

#include <stdio.h>
#include <assert.h>

char* my_strstr(const char* str1, const char* str2)
{
    assert(str1 && str2);
    char* s1 = NULL;
    char* s2 = NULL;
    char* ret = (char*)str1;//强制类型转化防止const的警告

    while (*ret)
    {
        s1 = (char*)ret;//到下一个字符的地址
        s2 = (char*)str2;//回溯
        //判断是否相等
        while (*s1 && *s2 && *s1 == *s2)
        {
            s1++;
            s2++;
        }
        //s2到了末尾
        if (*s2 == '\0')
            return s1;
        ret++;
    }
    return NULL;
}

int main()
{
    char arr1[] = "oppqqqrst";
    char arr2[] = "ppq";
    char* ret = my_strstr(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

2.6 strtok()函数

1.函数作用

        —— 分隔字符串,使用标记分隔字符串

2.函数原型

char* strtok(char* str, const char* sep);

3.strtok函数的细节说明

1)字符串sep作为分隔字符串的分隔符号【注:sep是一个不可变的字符串】

2)strtok函数是有记忆的,会记住分隔后的地址

3)函数解释:strtok函数的第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记;strtok函数的第二个参数指定一个字符串,该字符串由用于分隔标记的字符组成,这些字符可以是任意数量,但是它们必须是单个字符。strtok函数将从str中查找这些分隔符,并将它们用 \0 结尾,返回一个指向标记的指针。strtok函数在每次调用时都会改变被操作的字符串,因此在使用strtok函数切分字符串时,一般都是使用临时拷贝的内容,这样可以避免改变原字符串中的内容。strtok函数还有一个可选参数,该参数可以用于指定strtok函数的行为,其中有两种可能的值:STRTOK_RETURN_DELIMS 和 STRTOK_SKIP_EMPTY,如果指定了 STRTOK_RETURN_DELIMS,则strtok函数返回的指针将指向分隔符;如果指定了 STRTOK_SKIP_EMPTY,则strtok函数将跳过空格,返回指向下一个有效标记的指针。总之,strtok函数是一个用于将字符串分隔成标记的函数,它可以指定分隔符,并可以指定strtok函数的行为,从而达到分割字符串的目的。

4.代码解释

        代码1:

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

int main()
{
    char arr[] = "[email protected]";
    char* sep = "@.";//分隔符号
    
    char* ret = strtok(arr, sep);
    printf("%s\n", ret);
    
    ret = strtok(NULL, sep);
    printf("%s\n", ret);
    
    ret = strtok(NULL, sep);
    printf("%s\n", ret);
    return 0;
}

        运行结果如图:

        思考以下代码会输出什么?

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

int main()
{
    char arr[] = "972,606,[email protected]";
    char* sep = ",+";//分隔符号
    
    char* ret = strtok(arr, sep);
    printf("%s\n", ret);
    
    ret = strtok(NULL, sep);
    printf("%s\n", ret);
    
    ret = strtok(NULL, sep);
    printf("%s\n", ret);
    
    ret = strtok(NULL, sep);
    printf("%s\n", ret);
    return 0;
}

        代码2:循环使用strtok函数

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

int main()
{
    char arr[] = "[email protected]";
    char* sep = "@.";
    
    char* ret = NULL;
    for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep))
    {
        printf("%s\n", ret);
    }
    return 0;
}

2.7 strerror()函数

1.函数作用

        —— 返回错误码所对应的错误的信息

2.函数原型

char* strerror(int errnum);

3.strerror函数细节说明

1)strerror函数接受一个errno值作为参数,并返回一个指向错误消息字符串的指针。这个字符串可以用于在程序中显示错误消息,

2)strerror函数会查找errno值,并返回一个指向错误消息字符串的指针,这个字符串可以用来描述错误。

4.代码解释

        代码1:

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

int main()
{
    printf("%s\n", strerror(1));
    printf("%s\n", strerror(2));
    printf("%s\n", strerror(3));
    printf("%s\n", strerror(4));
    printf("%s\n", strerror(5));
    return 0;
}

        所对应的错误码信息如下:

        代码2:在程序中的正确使用

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

int main()
{
    FILE* pf = fopen("abc.txt", "r");
    if (pf == NULL)
    {
        printf("%s\n", strerror(errno));
    }
    //释放空间
    free(pf);
    pf = NULL;
    return 0;
}

        运行结果如下:

三.内存操作的常用函数

3.1 memcpy()函数

1.函数作用

        ——从源地址拷贝num个字节的数据到目标地址空间中

2.函数原型

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

3.memcpy函数的细节说明

1)memcpy函数从src的位置开始向后复制num个字节的数据到dest的内存位置

2)这个函数不能和strcpy函数混淆,这个函数遇到'\0'不能停止

3)如果src和dest有任何的重叠,复制的结果都是未定义的

4.代码解释

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

int main()
{
    int arr1[10] = { 0 };
    int arr2[5] = { 1,2,3,4,5 };
    
    //从arr2复制16个字节的数据到arr1中
    memcpy(arr1, arr2, 16);
    return 0;
}

        调试代码如图:

5.模拟实现memcpy函数

#include <stdio.h>
#include <assert.h>

void* my_memcpy(void* dest, const void* src, size_t num)
{
    assert(dest && src);
    void* start = dest;//起始地址
    
    //复制num个字节
    while (num--)
    {
        *(char*)dest = *(char*)src;//赋值
        dest = (char*)dest + 1;//指向下一个空间
        src = (char*)src + 1;
    }
    return start;
}

int main()
{
    int arr1[10] = { 0 };
    int arr2[5] = { 6,7,8,9,10 };
    void* ret = my_memcpy(arr1, arr2, 16);
    
    for (int i = 0; i < 4; i++)
        printf("%d ", *((int*)ret + i));
    
    return 0;
}

3.2 memmove()函数

1.函数作用

        ——函数作用和memcpy函数是一样的

2.函数原型

void* memmove(void* dest, const void* src, size_t num);

3.memmove函数的细节说明

1)函数的功能和memcpy函数的功能一致:也是内存拷贝

2)memmove函数区别于memcpy的地方:memmove函数可以处理内存重叠的空间

4.代码解释

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

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    //把arr的20个字节的空间拷贝到arr+2的空间上
    memmove(arr + 2, arr, 20);
    
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

        运行结果如图:

5.模拟实现memmove函数

#include <stdio.h>
#include <assert.h>

void* my_memmove(void* dest, const void* src, size_t num)
{
    assert(dest && src);
    void* ret = dest;
    
    //判断大小然后考虑拷贝的方式
    if (src > dest)
    {
        while (num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }
    else
    {
        while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }
    }

    return ret;
}

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    void* ret = my_memmove(arr, arr + 4, 20);
    
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
        printf("%d ", *((int*)ret + i));
    return 0;
}

3.3 memcmp()函数

1.函数作用

        ——内存比较,将内存中的数据拿出来一个一个的进行比较

2.函数原型

int memcmp(const void* ptr1, const void* ptr2, size_t num);

3.memcmp函数的细节说明

memcmp函数是比较两个内存块中num个字节的数据大小

1)若memcmp函数返回0,则说明比较的num个字节是相等的

2)若memcmp函数返回值

3)若memcmp函数返回值>0,则说明内存块1大于内存块2

4.代码解释

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

int main()
{
    int arr1[5] = { 1,2,3,4,5 };
    int arr2[5] = { 1,2,3,4,6 };
    int ret = memcmp(arr1, arr2, 16);//比较16个字节的数据
    printf("%d\n", ret);//0
    return 0;
}

5.模拟实现memcmp函数

#include <stdio.h>
#include <assert.h>

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
    assert(ptr1 && ptr2);
    while (num)
    {
        //不相等则跳出
        if ((*(char*)ptr1) != (*(char*)ptr2))
            break;
        
        ptr1 = (char*)ptr1 + 1;
        ptr2 = (char*)ptr2 + 1;
        num--;
    }
    
    //判断是不是循环到了最后一个
    if (num == 0)
    {
        return 0;
    }
    //不是最后一个
    return *(char*) ptr1 - *(char*)ptr2;
}

int main()
{
    int arr1[5] = { 1,2,3,4,6 };
    int arr2[5] = { 1,2,3,4,5 };
    int ret = my_memcmp(arr1, arr2, 20);
    printf("%d\n", ret);
    return 0;
}

结语

少年,修行不易;未来就在前方,要相信自己能达到你所期望的高度。少年,加油!!!

猜你喜欢

转载自blog.csdn.net/qq_63200557/article/details/129807002