常见字符串和内存函数的使用和剖析

1.strlen函数,通过字符串的地址计算其长度,遇到/0停止计算.
strlen函数的三种实现方法:

#include
#include
//1.计数器方法
int my_strlen1(char* p)
{
    int count = 0;
    assert(p != NULL);
    while (*p != '\0')
    {
        p++;
        count++;
    }
    return count;
}
//2递归方法
int my_strlen2(char* p)
{
    assert(p!= NULL);
    if (*p =='\0')
    {
        return 0;
    }
    else
    {
        return 1 + my_strlen2(p + 1);
    }
}
//指针相减法
int my_strlen3(char* p)
{
    assert(p!= NULL);
    char* ret = p;
    while (*p!='\0')
    {
        p++;
    }
    return p - ret;
    


}
int main()
{
    char arr[] = "abcdefgh";
    int ret = my_strlen1(arr);
    printf("%d\n", ret);
}

2.strcpy函数

strcpy(char*destination,const char*source)
   //拷贝时会把‘\0’一起拷贝
   //目标空间必须足够大
   //目标空间必须可变
   
//实现strcpy函数
char* my_strcpy(char* dest, const char* source)
{
    assert(dest);
    assert(source);
    char* ret = dest;
    while (*dest++= *source++)
    {
        ;
    }
    return ret;
}

3.strcat函数,用于拼接字符串

  char*strcat(char* destination, const char* source);
  //目标字符串的空间得足够大
  //detsintaion中的‘\0’会被所拼接的字符串得首字符所覆盖
  //source中的‘\0’也会被拷贝过去
  
char* my_strcat(char* dest, const char* src)
{
    assert(dest);
    assert(src);
    char* ret = dest;
    while (*dest!= '\0')
    {
        dest++;
    }
    while (*dest++ = *src++)//此时dest已经指向了目标函数的'\0'处
    {
        ;
    }
    return ret;
}

4、strcmp函数

      int strcmp(const char *str1, const char *str2)
      //两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止
      //当两个字符串不相等时,C标准没有规定返回值会是1 或 -1,只规定了正数和负数,在不同的编译器下返回的结果不同
      
//代码实现
int my_strcmp(char* str1, char* str2)
{
    assert(str1);
    assert(str2);
    while (*str1 == *str2)
    {
        if (*str1 == '\0')
        {
            return 0;//二者的字符始终相等,直到str1指向了'\0',比较结束,则此时二者相等,所以返回0;
        }
        str1++;
        str2++;
    }
    return(*str1 - *str2);//在遇到'\0'之前,二者便已经不相等了,跳出循环,直接返回二者不相等处字符的ASCII码的差值来判断大小

}

5.strncpy函数

       char *strncpy(char *destinin, char *source, int maxlen);
       //用来向目标字符数组复制制定长度的字符串
       //这个函数与strcpy先比更加安全
       //关于strcpy函数的函数实现,简单的实现就是按照制定长度把src中的字符一个一个拷贝到dest中
       
char* my_strncpy(char* dest, char* src,int len)
{
    assert(dest);
    assert(src);
    char* ret = dest;//保存目标函数的首元素地址
    while (len--)
    {
        *dest++ = *src++;//根据制定长度,按序拷贝
    }
    return ret;
}
  //但根据各种资料以及网上大牛对此函数的理解,在实现strcpy函数时还需要考虑两种情况,即指定的长度大于源字符串的长度以及两个字符串内存重叠的情况
  下面是考虑了指定长度的函数实现
  
char* my_strncpy1(char* dest, char* src, int len)
{
    assert(dest);
    assert(src);
    int offest = 0;//源字符串和指定长度的差值
    char* ret = dest;
    if (len > strlen(src))//len>strlen(src)的情况
    {
        int offest = len - strlen(src);
        int len = strlen(src);//令strlen(src)=len
    }
    while (len--)
    {
        *dest++ = *src++;
    }
    while (offest--)
    {
        *dest++ = '\0';//将多出的部分全部赋值为'\0'
    }
    return ret;
}

6.strstr函数

   strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
   //函数的代码实现
char* my_strstr(const char* p1, const char* p2)
{
    assert(p1);
    assert(p2);
    char* s1 = NULL;
    char* s2 = NULL;
    char* cur = (char*)p1;//cur指针用来保匹配的起始位置
    if (*p2 == '\0')//如果str2是空字符串,则停止匹配
    {
        return (char*)p1;
    }
    while (*cur)//判断cur是否为空指针
    {
        s1 = cur;//s1是可以移动的指针,以cur为起点往后移动进行比较匹配
        s2 = (char*)p2;//s2代替p2往后检索匹配
        while (*s1 && *s2 && (*s1 == *s2))//在s1,s不为空且匹配成功的情况下,二者继续往后检索进行字符匹配
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')//str2已经检索完,且所有字符都和str1匹配成功
        {
            return cur;//找到子串了,返回cur的地址
        }
        if (*s1=='\0')
        {
            return NULL;//如果str1的长度小于str2的长度,则匹配失败,返回空指针
        }
        cur++;//在str1和str2第一次检索匹配失败后,cur后移一位,从新的起点和str1开始进行匹配
    }
    return NULL;//找不到子串
}

7.strerror函数

   strerror()用来依参数errnum 的错误代码来查询其错误原因的描述字符串, 然后将该字符串指针返回.
    //错误码 错误信息
    //0      No error
    //1      Operation not permitted
    //strerror的作用是把错误信息翻译
    //errno是一个全局的错误码变量,当c语言的库函数在执行过程中发生错误,就会把对应的错误码赋值到errno中
    char* str = strerror(1);
    printf("%s\n", str);
    //打开文件
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        printf("%s\n", strerror(errno));//给出没有找到这个文件的错误的原因
    }
    else
    {
        printf("open file success\n");
    }
}

8.内存操作函数:memcpy

       memcpy函数:memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
       
#include
#include
#include

void*my_memcpy(void* dest, const void* src, size_t num)//自己实现一个memcpy函数,size_int num拷贝的字节数
{
    void* ret = dest;//把首元素地址存起来,方便返回
    assert(dest != NULL);
    assert(src != NULL);
    while (num--)//一个字节一个字节的拷贝
    {
        *(char*)dest = *(char*)src;//强转成char*类型,方便一个字节一个字节的拷贝
        //++(char*)dest;
        //++(char*)src;
        dest = (char*)dest + 1;
        dest = (char*)src + 1;
    }
    return ret;
}
//c语言规定:memcpy只处理不重叠的内存拷贝,memmove处理重叠内存的拷贝
int main()
{
    //memcpy函数,返回值是void*通用型指针//可以拷贝任何类型的数据,但若目的数组和源数组有所重叠,则memcpy不适用所有情况
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    //my_memcpy(arr + 2, arr, 20);
    memmove(arr + 2, arr, 20);//memmove()函数可以实现重叠拷贝,把12345拷贝到34567上,重叠拷贝
    for ( i = 0; i < 10; i++)
    {
        printf("%d", arr[i]);
    }
}
memmove函数:同memcpy函数功能类似,其函数原型为 void * memmove(void *dest, const void *src, size_t num);与memcpy的区别在于,memmove可以处理内存重叠的情况
void* my_memmove(void* dest, const void* src, size_t num)//函数实现
{
    void* ret = dest;//把首元素地址存起来,方便返回
    assert(dest != NULL);
    assert(src != NULL);
    if(dest

常见字符串和内存函数的使用和剖析_第1张图片
图1为从前往后拷贝的情况,蓝色框代表src,黄色框代表dest,此时二者有重叠的部分,要把src中的内容拷贝到dest中,则只能从前往后拷贝,即按照顺序把34567拷到23456所在的内存空间,如果逆序拷贝,即先拷贝7,则会导致7覆盖掉6,导致6无法拷贝,其他元素亦然,所以只能正向拷贝,此时归类于dest

常见字符串和内存函数的使用和剖析_第2张图片
图2中,dest位于src和src+count之间的情况,则需要,从后往前拷贝,如果正向拷贝则也会出现前面一个元素覆盖后面元素,导致后面元素无法拷贝的情况,逆向拷贝即按照76543的顺序拷贝。逆向拷贝的具体代码实现:

      while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }//参考下图

常见字符串和内存函数的使用和剖析_第3张图片

因为需要一个字节一个字节的拷贝,系统是按照小端模式存储的字符串,假设num一开始是20,
在循环刚开始便num--,此时num变为了19,则*((char*)src + num)指向src最后一个数字的
最后一个字节的起始地址,*((char*)dest + num)指向dest最后一个数字的最后一个字节的起始地址,再拷贝,就成功将scr的最后一个字节拷贝到dest的最后一个字节,num--控制循环终止条件,则最终实现了逆向拷贝

常见字符串和内存函数的使用和剖析_第4张图片
图3中,若dest位于src+count之后,此时正向拷贝,逆向拷贝都不会出现覆盖元素的情况

你可能感兴趣的:(c)