字符串函数+内存函数详解以及模拟实现

目录

字符串函数

strtok 截取分隔符

重点

作用举例

strerror 把错误码转换成错误信息

重点

perror

重点

字符分类函数

字符转换函数

内存函数

memcpy

重点

模拟实现

注意:小Tips

memmove

重点

模拟实现

memcmp

重点

memset

重点


字符串函数

strtok 截取分隔符

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

重点

  • sep参数是个字符串,定义了用作分隔符的字符集合

  • str参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记;

  • strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)

  • strtok函数的第一个参数不为NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

    下述例子,第一次调用strtok()函数返回第一个y的地址。打印yingyq0118

  • strtok函数的第一个参数为NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。对于同一个字符串,要多次分割,第2次及以后的第一个参数要传NULL!!

    例如同一个字符串有两个分隔符时,下一次要取到163时,要写strtok(NULL, sep);,下一次取得com时,也是写strtok(NULL, sep);至此,用作分隔符的字符已不存在,无需再次调用,再次调用则返回NULL。

  • 如果字符串中不存在更多的标记,则返回NULL 指针。

作用举例

邮箱由三个部分组成,@ .可以当作分隔符,用strtok切割成三个部分。

那么这里

//这个很弱智,只是为了详细说明函数如何使用,分开调用strtok
#include <stdio.h>
#include <string.h>
​
int main()
{
    char arr[] = "yingyq0118@163.com";
    char buf[200] = { 0 };//用于存放拷贝的内容
    strcpy(buf, arr);
​
    const char* sep1 = "@.";
    char sep2[2] = { '@','.' };
​
    char* str1 = strtok(buf, sep2);
    printf("%s\n", str1);
​
    char* str2 = strtok(NULL, sep2);
    printf("%s\n", str2);
​
    char* str3 = strtok(NULL, sep2);
    printf("%s\n", str3);
    return 0;
}
//这个才是实际使用
int main()
{
    char arr[] = "yingyq0118@163.com";
    char buf[200] = { 0 };//用于存放拷贝的内容,strtok的第一个参数
    strcpy(buf, arr);
    const char* sep = "@.";//strtok的第二个参数
​
    char* str = NULL;//保存strtok返回的地址
    for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep))
    {
        printf("%s\n", str);
    }
    return 0;
}

strerror 把错误码转换成错误信息

char * strerror ( int errnum );

重点

返回错误码,所对应的错误信息。

如果不想打印错误信息,就用strerror

#include <stdio.h>
#include <string.h>
int main()
{
    printf("%s\n", strerror(0));//No error
    printf("%s\n", strerror(1));//Operation not permitted
    printf("%s\n", strerror(2));//No such file or directory
    printf("%s\n", strerror(3));//No such process
    printf("%s\n", strerror(4));//Interrupted function call
    return 0;
}
//0,1,2,3,4,是c语言库函数报错时返回的字符码。
#include <stdio.h>
#include <string.h>
#include <errno.h>//必须包含的头文件 为了使用errno
//errno是c语言提供的全局的错误变量
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        //错误码会被记录到 错误码变量errno里面
        printf("%d\n", errno);
        printf("%s\n", strerror(errno));
        return 1;
    }
    //处理文件
    
    //关闭文件
    fclose(pf);
    pf = NULL;
    return 0;
}

perror

void perror ( const char * str );

看实际的输出结果,可以猜想perror = printf + strerror

重点

str是用户自定义的输入,函数会自己去拿errno变量中错误码的信息。

如果你想打印错误信息,就用perror

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        //错误码会被记录到 错误码变量errno里面
        perror("user");//yyq: No such file or directory
        return 1;
    }
    //处理文件
    
    //关闭文件
    fclose(pf);
    pf = NULL;
    return 0;
}
//输出:user: No such file or directory

字符分类函数

函数如果他的参数符合下列条件就返回真头文件
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
isdigit十进制数字 0~9ctype.h
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~Fctype.h
islower小写字母a~zctype.h
isupper大写字母A~Zctype.h
isalpha字母a~z或A~Zctype.h
isalnum字母或者数字,a~z,A~Z,0~9ctype.h
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符

字符转换函数

函数定义功能头文件
int toupper ( int c );小写转大写ctype.h
int tolower ( int c );大写转小写ctype.h

内存函数

memcpy

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

重点

  • 区别:memcpy什么类型的数据都能拷贝(因为接受的是void*),但是strcpy strncpy只能拷贝char类型的变量;

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置;

  • 这个函数在遇到'\0' 的时候并不会停下来;

  • 警告:如果source和destination在内存空间有任何的重叠,复制的结果都是未定义的。

模拟实现

void* my_memcpy(void* destination, const void* source, size_t num)
{
    assert(destination && source);
    void* dest = destination;//保存首元素的地址
​
    while (num--)//1个字节1个字节的拷贝
    {
        *(char*)destination = *(char*)source;//拷贝当前字符
        destination = (char*)destination + 1;//向后走1个字节
        source = (char*)source + 1;//向后走1个字节
    }
    return dest;
}
int main()
{
    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    int arr2[10] = { 0 };
    my_memcpy(arr2, arr1, 5 * sizeof(int));//拷贝前5个元素
    for (int i = 0; i < 5; i++)
    {
        printf("%d ", arr2[i]);
    }
    printf("\n");
    return 0;
}
arr1在内存中存储为

        01000000 02000000 03000000 04000000 05000000.....
低地址--------------------------------------------------------高地址
一次拿1个字节
        00000000 00000000 00000000 00000000 00000000.....
低地址--------------------------------------------------------高地址
得到
        01000000 02000000 03000000 04000000 05000000.....
低地址--------------------------------------------------------高地址

注意:小Tips

提问:为什么VS里用memcpy也可以实现重叠内存的拷贝?

C语言规定memcpy只需要实现非重叠内存的拷贝,只不过VS里面的memcpy超额完成而已。

当要拷贝的source空间和指向的destination目的空间有内存重叠时:

当destination小于source,应该从前向后(从低地址先高地址处)拷贝;

当destination大于source,应该从后向前(从高地址向低地址处)拷贝。

内存重叠要看下面的memmove。

memmove

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

重点

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的;

如果源空间和目标空间出现重叠,就得使用memmove函数处理。

当要拷贝的source空间和指向的destination目的空间有内存重叠时(出现在同一个变量使用内存拷贝的情况):

(char*)destination小于(char*)source,应该从前向后(从低地址先高地址处)拷贝,将source的开头元素拷贝到destination的开头元素位置处,对指针destination、source依次++;

(char*)destination大于(char*)source,应该从后向前(从高地址向低地址处)拷贝,将source的末尾元素拷贝到destination的末尾元素处,对num--。

模拟实现

void* my_memmove(void* destination, const void* source, size_t num)
{
    assert(destination && source);
    void* dest = destination;//保存首元素的地址
​
    if (destination < source)//数组地址随着数组下标的增加而增大
    {
        //从前向后拷贝
        while (num--)
        {
            *(char*)destination = *(char*)source;//拷贝当前字符
            destination = (char*)destination + 1;//向后走1个字节
            source = (char*)source + 1;//向后走1个字节
        }
    }
    else
    {
        //从后向前拷贝
        while (num--)
        {
            *((char*)destination + num) = *((char*)source + num);//拷贝当前字符
        }
    }
    return dest;
}

memcmp

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

重点

比较从ptr1和ptr2指针开始的num个字节

返回值描述
<0第一个字节 ptr1所指向的元素比ptr2所指向的元素小(假设比较的是unsigned char类型的数据)
0第一个字节 两个指针所指向的元素大小相等
>0第一个字节 ptr1所指向的元素比ptr2所指向的元素大(假设比较的是unsigned char类型的数据)

memset

void * memset ( void * ptr, int value, size_t num );

重点

  • ptr:要修改的空间;

  • value:要修改成的值,只能占一个字节;

  • num:要修改的字节数。

  • memset时按字节来修改内存内容。在使用时要小心,容易越界访问。

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值