• 【C语言】字符串函数和内存函数的使用及其模拟实现



     一、strlen(求字符串长度)

    1、模拟实现strlen

    1.1计数器方法

    1.2递归方法

    1.3指针减指针的方法

    二、strcpy(字符串拷贝)

    1、使用strcpy的注意事项

    1.1 sour需要包含结束字符:

    1.2 dest空间不足导致异常:

    1.3 dest与sour的内存重叠

    2、模拟实现strcpy

    三、strcat(字符串连接)

    1、使用strcat的注意事项

    2、模拟实现strcat

    四、strcmp(字符串比较)

    模拟实现strcmp函数

    五、strncpy(限定个数的字符串拷贝)

    1、使用strncpy的注意事项

    2、模拟实现strncpy

    六、strncat(限定个数的字符串连接)

    1、使用strncat的注意事项

    2、模拟实现strncat

    七、strncmp(限定个数的字符串比较函数)

    八、strstr(查找子串)

    1、使用strstr的注意事项

    2、模拟实现strstr

    九、strtok(字符串切割)

    1、使用strtok的注意事项

    2、strtok函数的使用

    十、strerror(获取指向错误消息字符串的指针)

    1、strerror函数的使用

    十一、memcpy(内存拷贝)

    1、使用memcpy的注意事项

    2、模拟实现memcpy

    十二、memmove(内存拷贝,可以拷贝重叠内存)

    模拟实现memmove

    十三、memcmp(内存比较)

    使用memcmp的注意事项

    十四、memset(内存设置)

    memset函数的使用


     一、strlen(求字符串长度)

    strlen的作用是获取字符串的长度。该函数返回类型为size_t,参数不需要修改,所以形参加了const修饰。

    1、模拟实现strlen

    1.1计数器方法

    1. size_t my_strlen(const char* p)
    2. {
    3. assert(p);
    4. size_t count = 0;///计数器
    5. while (*p != '\0')
    6. {
    7. count++;
    8. p++;
    9. }
    10. return count;
    11. }

    先在函数内部对传入的指针断言,防止传入空指针。

    用count变量进行计数,每次*p不等于'\0',count++。

    1.2递归方法

    1. size_t my_strlen(const char* p)
    2. {
    3. assert(p);
    4. if (*p != '\0')
    5. {
    6. return 1+my_strlen(p + 1);
    7. }
    8. return 0;
    9. }

    递归思想是大事化小,小事化了。有一个判断条件,每次进入条件调用自身时,参数范围越缩越小,达到递归的目的。

    1.3指针减指针的方法

    1. size_t my_strlen(char* p)
    2. {
    3. assert(p);
    4. char* q = p;
    5. while (*q)
    6. {
    7. q++;
    8. }
    9. return q - p;
    10. }

    不过用这种方法,形参如果用const修饰会报警告,因为执行char* q = p时,相当于一次权限放大。

    二、strcpy(字符串拷贝)

    将sour指向的 C 字符串复制到dest所指向的数组中,包括终止空字符(并在该点停止)。

    1、使用strcpy的注意事项

    1.1 sour需要包含结束字符:

    如果故意让arr2没有'\0',那么使用strcpy函数时会引发异常。

    1.2 dest空间不足导致异常:

    可以发现,当dest空间不足以拷贝sour的数据时,strcpy函数仍然会将所有数据越界拷贝至dest中,并在dest栈帧销毁时引发异常。所以这也是这类函数不安全的原因。

    1.3 dest与sour的内存重叠

    拷贝出现内存重叠时,会引发异常。

    2、模拟实现strcpy

    1. char* my_strcpy(char* dest, const char* sour)
    2. {
    3. assert(dest && sour);
    4. char* p = dest;
    5. while (*dest++ = *sour++)
    6. {
    7. ;
    8. }
    9. return p;
    10. }

    实现的功能是把sour的值复制到dest里,注意'\0'也要拷贝过去。

    三、strcat(字符串连接)

    1、使用strcat的注意事项

    和strcpy一样:

    1、sou指向的字符串必须以'\0'结尾,如果没有'\0',vs会报异常。

    2、目标空间必须足够大

    3、dest与sour指向的内存禁止重叠(例如自己追加自己)

    2、模拟实现strcat

    1. char* my_strcat(char* dest, const char* sour)
    2. {
    3. assert(dest && sour);
    4. char* p = dest;
    5. while (*dest)
    6. {
    7. dest++;
    8. }
    9. while (*dest++ = *sour++)
    10. {
    11. ;
    12. }
    13. return p;
    14. }

    四、strcmp(字符串比较)

    第一个字符串大于第二个字符串,返回值>0

    第一个字符串等于第二个字符串,返回值==0

    第一个字符串小于第二个字符串,返回值小于0

    模拟实现strcmp函数

    1. int my_strcmp(const char* str1, const char* str2)
    2. {
    3. assert(str1 && str2);
    4. while (*str1 == *str2)//循环先把相等的找完
    5. {
    6. if (*str1 == '\0')//需要在++前判断*str是不是为\0
    7. return 0;
    8. str1++;
    9. str2++;
    10. }
    11. return *str1 - *str2;
    12. }

    五、strncpy(限定个数的字符串拷贝)

    1、使用strncpy的注意事项

    注意dest指向的空间要大于num

    如果sour指向的字符串的长度比num大,从sour中拷贝num个字符到dest。

    如果sour指向的字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加'\0',直到num个。

    2、模拟实现strncpy

    1. char* my_strncpy(char* dest, char* sour, size_t num)
    2. {
    3. assert(dest && sour);
    4. char* p = dest;
    5. while (num&&*sour)//num==0或*sour=='\0'则跳出循环
    6. {
    7. *dest++ = *sour++;
    8. num--;
    9. }
    10. while (num)//把剩余的num替换为'\0'
    11. {
    12. *dest++ = '\0';
    13. num--;
    14. }
    15. return p;
    16. }

    六、strncat(限定个数的字符串连接)

    1、使用strncat的注意事项

    注意dest指向的空间要足够

    字符串追加完毕后,在末尾添加'\0'

    如果sour中字符串的长度小于 num,则仅复制终止空字符之前的内容(不会像strncpy一样追加'\0');

    2、模拟实现strncat

    1. char* my_strncat(char* dest, const char* sour, size_t num)
    2. {
    3. assert(dest && sour);
    4. char* p = dest;
    5. while (*dest)//先找到dest的'\0'
    6. {
    7. dest++;
    8. }
    9. while (num&&*sour)//判断num==0和*sour=='\0'
    10. {
    11. *dest++ = *sour++;
    12. num--;
    13. }
    14. *dest = '\0';
    15. return p;
    16. }

    七、strncmp(限定个数的字符串比较函数)

    和strcmp类似,跳过。

    八、strstr(查找子串)

    1、使用strstr的注意事项

    若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。

    2、模拟实现strstr

    1. char* my_strstr(const char* str1, const char* str2)
    2. {
    3. assert(str1 && str2);
    4. const char* p = str1;//p的作用是记录初始位置
    5. const char* s1 = str1;
    6. const char* s2 = str2;
    7. while (*p != '\0')
    8. {
    9. while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)//*s1==*s2,进行逐个比较
    10. {
    11. s1++;
    12. s2++;
    13. }
    14. if (*s2 == '\0')//如果发现*s2=='\0',那么str2被找到了,返回指针p
    15. {
    16. return (char*)p;
    17. }
    18. p++;//反之p的地址加1,继续寻找下一个位置
    19. s1 = p;//s1和s2都要回来
    20. s2 = str2;
    21. }
    22. return NULL;//当*p=='\0',说明str1找完了(找不到),返回空指针
    23. }

    九、strtok(字符串切割)

    1、使用strtok的注意事项

    第一个参数是被切割的字符串,第二个参数是分隔符的字符集合。

    在第一次调用时,该函数需要一个 C 字符串作为str的参数,其第一个字符用作扫描标记的起始位置。在随后的调用中,该函数需要一个空指针,并使用最后一个标记结束后的位置作为新的扫描起始位置。

    为了确定记号的开始和结束,该函数首先从起始位置扫描不包含在分隔符中的第一个字符(它成为记号的开始)。然后从标记的开头开始扫描分隔符中包含的第一个字符,该字符成为标记的结尾。如果找到终止空字符,扫描也会停止。

    strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:

    strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容

    并且可修改。)
    找到最后一个标记的点由函数内部保存,以便在下次调用时使用(不需要特定的库实现来避免数据竞争)

    2、strtok函数的使用

    第一次使用strtok传入被切割的字符串地址,后续传入空指针即可,strtok函数会记住被切割的位置。

    当字符串中没有更多的标记时,返回空指针。

    注意:找到分隔符的位置将被替换为'\0',使用strtok会破坏原有字符串。使用时建议创建一个临时变量。

    十、strerror(获取指向错误消息字符串的指针)

    参数errnum是错误码

    1、strerror函数的使用

    1. #include
    2. #include
    3. #include
    4. int main ()
    5. {
    6. FILE * pFile;
    7. pFile = fopen ("unexist.ent","r");
    8. if (pFile == NULL)
    9. printf ("Error opening file unexist.ent: %s\n",strerror(errno));
    10. return 0;//errno是C语言设置的一个全局的错误码存放的变量
    11. }

    十一、memcpy(内存拷贝)

    num是拷贝的字节数。

    1、使用memcpy的注意事项

    将 num 字节值从sour指向的位置直接复制到的dest指向的内存块。
    源指针和目标指针所指向的对象的基础类型与此函数无关;结果是数据的二进制副本。
    函数不检查sour中是否有任何终止空字符 - 它始终精确地复制数字字节。
    为避免溢出,dest参数和sour所指向的数组的大小应至少为 num 个字节,并且不应重叠(对于重叠的内存块,memmove是一种更安全的函数)。虽然vs中memcpy和memmove都能拷贝重叠内存,虽然都可以使用,但是要知道文档中规定memcpy不应处理重叠内存块。

    2、模拟实现memcpy

    1. void* my_memcpy(void* dest, const void* sour, size_t num)//注意返回类型是void*,链式访问时需要强转
    2. {
    3. assert(dest && sour);
    4. void* p = dest;
    5. while (num)
    6. {
    7. num--;
    8. *(char*)dest= *(char*) sour;
    9. dest = (char*)dest + 1;
    10. sour = (char*)sour + 1;
    11. }
    12. return p;
    13. }

    十二、memmove(内存拷贝,可以拷贝重叠内存)

    memmove和memcpy的特性一样,唯一区别是memmove可以拷贝内存重叠的区域。

    模拟实现memmove

    1. void* my_memmove(void* dest, const void* sour, size_t num)
    2. {
    3. assert(dest && sour);
    4. void* p = dest;
    5. //dest<=sour,数据从前向后拷贝
    6. if (dest <= sour)
    7. {
    8. while (num)
    9. {
    10. num--;
    11. *(char*)dest = *(char*)sour;
    12. dest = (char*)dest + 1;
    13. sour = (char*)sour + 1;
    14. }
    15. }
    16. //dest>sour,数据从后向前拷贝
    17. else
    18. {
    19. while (num)
    20. {
    21. num--;
    22. *((char*)dest + num) = *((char*)sour + num);
    23. }
    24. }
    25. return p;
    26. }

    十三、memcmp(内存比较)

    使用memcmp的注意事项

    memcmp与strcmp不同的是,该函数在找到'\0'不会停止比较。如图所示,使用时num的参数不要大于数组长度。

    十四、memset(内存设置)

    memset是按字节修改内存

    memset函数的使用

  • 相关阅读:
    DocCMS keyword SQL注入漏洞复现 [附POC]
    android mk常用代码
    12c向19c迁移:OGG基本配置
    Spring事务的实现方式
    linux下golang环境配置问题记录
    P3-Python学习当中的两大法宝函数
    Unity AudioClip和PCM音频数据的转化
    学习数据结构和算法的第8天
    报错Could not resolve placeholder ‘driver‘ in value “${driver}“
    股指期货开户的条件和流程
  • 原文地址:https://blog.csdn.net/gfdxx/article/details/125714133