关于c:常见字符串和内存函数的使用和剖析

8次阅读

共计 4810 个字符,预计需要花费 13 分钟才能阅读完成。

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

#include<stdio.h>
#include<assert.h>
//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<stdio.h>
#include<memory.h>
#include<assert.h>

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<src)// 此时从前往后拷贝
        while (num--)// 一个字节一个字节的拷贝
        {*(char*)dest = *(char*)src;// 强转成 char* 类型,不便一个字节一个字节的拷贝
            dest = (char*)dest + 1;
            dest = (char*)src + 1;
        }
    else
    {
        // 从后往前拷贝
        while (num--)
        {*((char*)dest + num) = *((char*)src + num);
        }
    }
    return ret;
}


图 1 为从前往后拷贝的状况,蓝色框代表 src,黄色框代表 dest, 此时二者有重叠的局部,要把 src 中的内容拷贝到 dest 中,则只能从前往后拷贝,即依照程序把 34567 拷到 23456 所在的内存空间,如果逆序拷贝,即先拷贝 7,则会导致 7 笼罩掉 6, 导致 6 无奈拷贝,其余元素亦然,所以只能正向拷贝,此时归类于 dest<src 的状况


图 2 中,dest 位于 src 和 src+count 之间的状况,则须要,从后往前拷贝,如果正向拷贝则也会呈现后面一个元素笼罩前面元素,导致前面元素无奈拷贝的状况,逆向拷贝即依照 76543 的程序拷贝。逆向拷贝的具体代码实现:

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

 因为须要一个字节一个字节的拷贝,零碎是依照小端模式存储的字符串,假如 num 一开始是 20,在循环刚开始便 num--, 此时 num 变为了 19,则 *((char*)src + num) 指向 src 最初一个数字的
最初一个字节的起始地址,*((char*)dest + num) 指向 dest 最初一个数字的最初一个字节的起始地址,再拷贝,就胜利将 scr 的最初一个字节拷贝到 dest 的最初一个字节,num-- 管制循环终止条件,则最终实现了逆向拷贝


图 3 中,若 dest 位于 src+count 之后,此时正向拷贝,逆向拷贝都不会呈现笼罩元素的状况

正文完
 0