本节对应分支:string

下节我们将要实现内存管理,这不可避免地要频繁使用到 memcpy、memset 等函数,有了内存操作函数就很容易实现字符串操作函数 strcpy、strcat 等。所以这节我们来实现内存操作和字符串操作函数。本节内容虽然简单,但有许多代码规范需要注意,还请读者不可掉以轻心。

memset

void memset(void* dst, char var, unsigned int size)
{
    assert(dst != NULL);
    unsigned char* tmp = dst;
    while((size--) > 0)
        *tmp++ = var;
}

注意,void* 是无法直接作指针运算的,因为编译器无法确定其步长及其解释方式 。因此,需要定义 unsigned char* tmp 来代替 void* dst ,tmp 指针的步长即为 1 字节。以下同理。

什么是指针的步长?就是指 ++-- 时指针移动的字节数。
什么是解释方式?就是指定编译器如何去解释指针所指向的这个数据。

memcpy

void memcpy(void* dst, void* src, unsigned int size)
{
    assert(dst != NULL);
    assert(src != NULL);
    src = (char *)src + size - 1;
    dst = (char *)dst + size - 1;
    while(size--)
    {
        *(char *)dst = *(char *)src;
        dst = (char *)dst - 1;
        src = (char *)src - 1;
    }
}

这个函数有以下两点需要注意:

  1. 规范问题:能直接 assert(dst!=NULL && src!=NULL) 吗?
    当然可以,但出问题时,你怎么确定是 dst 还是 src 的问题?所以最好细化,便于追踪错误。
  2. 解决了 内存重叠 的问题,参考memcpy和memmove

memcpy

int memcmp(const void* mem1, const void* mem2, unsigned int size)
{
    assert(mem1 != NULL);
    assert(mem2 != NULL);
    const char* tmp1 = mem1;
    const char* tmp2 = mem2;
    while((size++) > 0)
    {
        if(*tmp1 != *tmp2)
            return *tmp1 > *tmp2 ? 1 : -1;
        tmp1++;
        tmp2++;
    }
    return 0;
}

strcpy

char* strcpy(char* dst,const char* src)
{
    assert(dst != NULL);
    assert(src != NULL);
    if(dst == src)
        return src;
    char* tmp = dst;
    while((*dst++ = *src++) != '\0');
    return tmp;
}
  • 字符串 src 是不会被改变的,所以要声明为 const 。
  • 注意第 5 行的检查,这是我们容易忽略的地方。
  • 第 8 行,赋值运算符也是有返回值的,其返回所赋的值,即 *src

上面代码看上去无懈可击,实际上也存在内存重叠的问题:
存在内存重叠时,报错

因此将代码改为如下:

char* strcpy(char* dst, const char* src)
{
    assert(dst != NULL);
    assert(src != NULL);
    if(dst == src)
        return src;
    memcpy(dst,src,strlen(src)+1); //+1是包括'\0'
    return dst;
}

修改代码后,正常运行

strlen

unsigned int strlen(const char* str)
{
    assert(str != NULL);
    const char* tmp = str;
    while(*tmp++);
    return tmp-str-1;
}

最后的 -1 可别忘了。

这种方式很容易忽略 1,保险可采用此方式:while(*tmp) tmp++; ;如此就无需减 1 。

strcmp

int strcmp(const char* dst, const char* src)
{
    assert(dst != NULL);
    assert(src != NULL);
    while(*dst != '\0' && *dst==*src)
    {
        dst++;
        src++;
    }
    return *dst < *src ? -1 : *dst > *src;
}

注意第 10 行,把 0、1、-1三种情况都概括了,很漂亮的方式。

strchr与strrchr
strchr :参数 str 所指向的字符串中搜索第一次出现字符 c 的位置。

char* strchr(const char * str,char ch)
{
    assert(str != NULL);
    while (*str && *str != ch)
        str++;
    if (*str == ch)
        return str;
    return NULL;
}

strrchr :参数 str 所指向的字符串中搜索最后一次出现字符 c 的位置。

char* strrchr(const char* str,char ch)
{
    assert(str != NULL);
    const char* last_char = NULL;
    while (*str != 0) 
    {
        if (*str == ch) 
            last_char = str;
        str++;
    }
    return (char*)last_char;
}

strcat
string-concatenate:

char* strcat(char* dst, const char* src)
{
    assert(dst != NULL);
    assert(src != NULL);
    //通过strcpy来实现strcat函数
    strcpy (dst + strlen (dst), src);
    return dst;
}

strchrs
string-char-reserch:

unsigned int strchrs(const char* src, char ch)
{
    assert(src != NULL);
    unsigned int cnt = 0;
    const char* tmp = src;
    while(*tmp)
    {
        if(*tmp == ch)
            cnt++;
        tmp++;
    }
    return cnt;
}
文章作者: 极简
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 后端技术分享
自制操作系统
喜欢就支持一下吧