字符串函数:strtok/strerror/ 内存函数:memcpy/memmove/memcmp/memset

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

//strtok(字符串分割函数)
    //char* strtok(char* str, const char* sep)
    //str是一个字符指针,指向的是一个包含若干个分隔符的字符串;sep指向的是由str指向的字符串中出现的所有形式的分隔符构成的一个集合,本质还是一个字符串
    //str指向的字符串被sep指向的字符串中的分隔符分割成一个个的标记。例:
        //现有字符串:"[email protected]",则该字符串被两个分隔符"@ ."分割成三个标记,第一个标记为abcd、第二个标记为1234、第三个标记为com
    //strtok函数找到str中的下一个标记,并将其用\0结尾,返回一个指向这个标记的指针。如:
        // "[email protected]"中的第一个标记:abcd,strtok函数会用\0将abcd结尾,strtok函数的返回值是指向abcd首元素的字符指针
    // (注:strtok函数会改变备操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)
    //strtok函数在寻找str中的第一个标记时,strtok的第一个参数不为NULL,除第一个标记外strtok在寻找其它标记时,strtok的第一个参数都为NULL,因为strtok函数具有记忆功能:
        //strtok函数在寻找完一个标记之后会保存它现在所在的位置,在寻找下一个标记时候直接用空指针NULL来接收它当前所在的位置,从该位置开始继续往后找即可。如:
            //strtok函数在寻找完"[email protected]"中的第一个标记abcd之后,它当前所在的位置在[email protected]中的分隔符@位置处,在寻找下一个标记时候将@的位置传给strtok,从@开始往后找,直到再次遇到分隔符停止寻找,这两个分隔符之间的所有内容就是前面说的‘下一个标记’
    //如果字符串中的所有标记都被找完,再执行strtok函数会返回空指针NULL
int main()
{
    char arr[] = "[email protected]";
    char buf[30] = { 0 };
    strcpy(buf, arr);
    const char* p = "@.";
    char* str = strtok(buf, p);
    printf("%s\n", str);    //lbj

    str = strtok(NULL, p);
    printf("%s\n", str);    //123

    str = strtok(NULL, p);
    printf("%s\n", str);    //emil

    str = strtok(NULL, p);
    printf("%s\n", str);    //NULL
        //字符串"[email protected]"中的一共只有三个标记,只需要执行三次strtok就可以找完所有标记,这是第四次执行strtok函数,所以会返回空指针NULL
    return 0;
}
//代码优化
int main()
{
    char arr[] = "[email protected]";
    char buf[30] = { 0 };
    strcpy(buf, arr);
    const char* p = "@.";
    char* str = NULL;
    for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))    //和for(i=0;i<10;i++)是一个道理
    {        //str = strtok(buf, p):寻找第一个标记,并将strtok返回的第一个标记的首字符地址赋值给str
            //str != NULL:判断条件,当strtok返回空指针NULL时终止循环
            //str = strtok(NULL, p):用于寻找第一个标记以外的其他标记
        printf("%s\n", str);
    }
    return 0;
}

//strerror
    //char* strerror(int errnum)    errnum是错误码,strerror的返回值是错误码所对应的错误信息的首字符地址
    // 其实:c语言的库函数在调用失败的时候会将一个错误码存放在一个叫做error的变量中,当我们想知道调用库函数时发生了什么错误信息,就可以将error中的错误码翻译成错误信息
    // 有一个库函数perror(),它的作用等价于strerror()+printf(),感兴趣可以了解一下
//功能演示
int main()
{
    char* p = strerror(0);
    printf("%s\n", p);    //No error  当错误码是0时,代表没错误信息
    p = strerror(1);
    printf("%s\n", p);    //Operation not permitted
    p = strerror(2);
    printf("%s\n", p);    //No such file or directory
    p = strerror(3);
    printf("%s\n", p);    //No such process
    return 0;
}

//memcpy
    //void* memcpy(void* destination, const void* source, size_t num);
        //void*可以接收任意类型的指针,运算或者解引用时必须先进行强制类型转换;destination是目标空间起始位置;source是源空间的起始位置;num的单位是字节
        //memcpy的返回值是目标空间的起始位置,即destination
        //即memcpy是将源空间的前num个字节拷贝到目标空间
    // memcpy是内存拷贝函数,内存不是字面意思,它指的是内存中存储的任意类型的数据
    // strcpy只能拷贝字符串,而memcpy可以拷贝任何类型的数据
    //(函数需要包含头文件<string.h>)
    //memcpy不适用于destination和source有内存重叠的情形,该情形专门由下一个讲到的mememove函数来处理
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[] = { 2,4,6,8,10 };
    memcpy(arr1, arr2, 20);    //20的单位是字节
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);    //2 4 6 8 10 6 7 8 9 10
    }
    return 0;
}
//模拟实现memcpy(不考虑目标空间和源空间的内存重叠)
void* my_memcpy(void* arr1, const void* arr2, size_t num)
{
    void* ret = arr1;
    while (num--)
    {
        *(char*)arr1 = *(char*)arr2;//强制类型转换只是在该语句中,该语句执行完又会恢复到原来的类型,即强制类型转换都是临时性的
        ((char*)arr1)++;
        ((char*)arr2)++;
    }
    return ret;
}
int main()
{
    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[] = { 2,4,6,8,10 };
    my_memcpy(arr1, arr2, 20);    //20的单位是字节
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr1[i]);    //2 4 6 8 10 6 7 8 9 10
    }
    return 0;
}

//memmove
    //相对于memcpy,memmove专门解决目标空间和源空间的内存重叠问题
    //(函数需要包含头文件<string.h>)
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    memmove(arr + , arr + 2, 240);
        //arr是数组名,表示数组首元素地址;arr + 4是数组第五个元素地址;arr + 2是数组第三个元素地址;20的单位是字节(即5个int类型数据)
        //arr + 4是目标空间的起始地址,arr + 2是源空间的起始地址,即从arr + 2的位置开始向后复制20个字节(5个int型)的数据到以arr + 4为起始地址的目标空间
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);    //1 2 3 4 3 4 5 6 7 10
    }
    return 0;
}
//模拟实现memmove
    //如果源空间和目标空间出现重叠:当目标空间的起始位置大于源空间的起始位置(即目标空间起始位置相对于源空间起始位置而言是较高地址)时,源空间内存储的数据从高地址向低地址的顺序依次拷贝到目标空间;其他情况均是从低地址向高地址的顺序依次拷贝
void* my_memmove(void* arr1, const void* arr2, size_t num)
{
    void* ret = arr1;
    if (arr1 <= arr2)
    {
        while (num--)
        {
            *(char*)arr1 = *(char*)arr2;
            ((char*)arr1)++;
            ((char*)arr2)++;
        }
    }
    else
    {
        while (num--)
        {
            *((char*)arr1 + num) = *((char*)arr2 + num);
        }
    }
    return ret;
}
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    my_memmove(arr + 4, arr + 2, 20);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);    //1 2 3 4 3 4 5 6 7 10
    }
    return 0;

//memcmp
    //int memcmp(const void* ptr1, const void* ptr2, size_t num)    注意:这里的num单位是字节
    //memcmp是比较从ptr1和ptr2指针开始的前num个字节
    //memcmp和strcmp的区别是可以对任意类型的数据进行比较,其他无异
int main()
{
    char arr1[] = "abcddef";
    char arr2[] = "abcdefg";
    int p1 = memcmp(arr1, arr2, 4);
    int p2 = memcmp(arr1, arr2, 5);
    printf("%d\n", p1);        //0
    printf("%d\n", p2);        //-1
    return 0;
}

//memset
    //void* memset(void* ptr, int value, size_t num)
    //memset是将ptr指向的内容的前num个字节的值设置成value    注意:num单位是字节        memset的返回值是ptr
int main()
{
    char str[] = "hello world";
    int arr[10] = { 0 };
    memset(str, 'a', 5);
    memset(arr, 1, 10);
    printf("%s\n", str);    //aaaaa world
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);    //16843009 16843009 257 0 0 0 0 0 0 0
            //memset不能实现将arr的所有元素设置成1,主要原因还是memset中的参数num的单位是字节,它是按字节来设置的
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/libj2023/article/details/131484305