C语言 常用标准库函数代码实现

一、内存

1. memcpy函数

memcpy 函数用于 把资源内存(src所指向的内存区域) 拷贝到目标内存(dest所指向的内存区域);拷贝多少个?有一个size变量控制拷贝的字节数;

函数原型:void *memcpy( void *dest, const void *src, size_t n );
dest表示拷贝到哪个目标,src表示拷贝源,加const修饰,防止被修改,n表示拷贝多少个字节
返回void* 类型的原因,是为了使用链式表达,即strlen((char*)(memcpy(dest,src,n)),这样可以直接计算dest的长度,使程序代码更简洁

#include <stdio.h>
#include <assert.h>
#include <string.h>
void* my_memcpy(void* dest, const void* src, size_t n)
{
	assert((dest != NULL) && (src != NULL));
	char* dest_t = (char*)dest;                  //转换成字符型一个个复制拷贝,由于函数拷贝的过程是一个字节一个字节的拷贝的,
												 //所以实际操作的时候要把void*强制转化为char*,
	char* src_f = (char*)src;                    //这样在指针加的时候才会保证每次加一个字节
	while (n-- > 0)
	{
		*(dest_t++) = *(src_f++);
	}
	return dest;//void* 一定要返回一个值(指针),这个和void不太一样!函数返回指向dest的指针
}

int main()
{
	char arr1[10] = { 0 };
	char arr2[10] = { 2,3,4,5,6,7,8,9,10,11 };
	int i = 0;
	my_memcpy(arr1, arr2, 10);
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

2. memmove函数

void *my_memmove(void *dest, const void *src, size_t count)
{
    assert(dest != NULL || src != NULL);

    if (dest < src)
    {
        char *p = (char *)dest;
        char *q = (char *)src;
        while (count--)
        {
            *(p++) = *(q++);
        }
    }
    else
    {
        char *p = (char *)dest + count;
        char *q = (char *)src + count;
        while (count--)
        {
            *(--p) = *(--q);
        }
    }

    return dest;
}

memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。当目标区域与源区域没有重叠则和memcpy函数功能相同。
当出现了src的尾部和dest的头部内存有重合的时机时,memmove的处理规则是从后往前进行copy。

用上面编写的my_memcpy和my_memmove两个函数举例体会一下:

int main()
{
	char s[] = "1234567890";
	char* p1 = s;
	char* p2 = s + 2;
	my_memcpy(p2, p1, 5);
	printf("my_memcpy: s = %s\n", s);

    memcpy(s,"1234567890",10);
    my_memmove(p2, p1, 5);
    printf("my_memmove: s = %s\n", s);

	return(0);
}

在这里插入图片描述
如对 memmove 和 memcpy的区别还有不理解可以参考:https://blog.csdn.net/qq_40843865/article/details/101775762

3. memset函数

void *my_memset(void *s, int c, size_t n)
{
    unsigned char* p=s;
   	while(n--)
   	{
        *p++ = (unsigned char) c;
    }
    return s;
}

二、字符串

1. strstr函数

函数原型:char *strstr(char *str1, const char *str2);
str1: 被查找目标
str2: 要查找对象
返回值:若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。

char* my_strstr(const char *str1, const char *str2)
{
    if (str1== NULL || str2 == NULL)
        return NULL;
    const char *temp = str1;
    const char *res = str2;
    while (*str1 != '\0')
    {
        temp = str1;
        res = str2;
        while (*temp== *res){
            temp++;
            res++;
        }
        if (*res == '\0')return str1;
        str1++;
    }
    return NULL;
}

2. strcpy函数

函数原型:char *strcpy(char *dest, const char *src);
返回char* 类型的原因,是为了使用链式表达,即strlen(strcpy(dest,src)),这样可以直接计算dest的长度,使程序代码更简洁

char* my_strcpy(char* dest, const char* src)
{
	assert(dest!=NULL && src!=NULL);
	
	char* ret = dest;//dest的起始位置赋给ret

	while ((*dest++ = *src++) != '\0');//源赋值给目标,源目标指针向后移动 可以直接写成while (*dest++ = *src++);
	
	return ret;
}

3. strncpy函数

char* my_strncpy(char* dest, const char* src, size_t count)
{
	assert(dest!=NULL && src!=NULL);
	
	char* ret = dest;//dest的起始位置赋给ret

	int i = 0;
	for (i = 0; i < (int)count; i++)//源赋值给目标,源目标指针向后移动
		*dest++ = *src++;

	return ret;
}

4. strlen函数

函数原型:size_t strlen(const char *s);

size_t my_strlen(const char *s) 
{
	assert(s!=NULL);
	size_t len = 0;
	while(*s++ != '\0')
	{
		len++;
	}
	return len;
}

size_t my_strlen(const char *s) 
{
	assert(s!=NULL);
	for(size_t i=0; s[i] != '\0'; i++);
	return i;
}

5. strcmp函数

strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
函数原型:int strcmp(const char *s1,const char *s2);
ANSI标准规定,返回值为正数,负数,0 。而确切数值是依赖不同的C实现的。
1.当两个字符串不相等时,C标准没有规定返回值会是1 或 -1,只规定了正数和负数。
2.有些会把两个字符的ASCII码之差作为比较结果由函数值返回。但无论如何不能以此条依据作为程序中的流程逻辑。

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1!=NULL && str2!=NULL);//判断str1和str2是否为NULL
	while (*str1 == *str2)//如果*str1和*str2相等,进入循环
	{
		if (*str1 == '\0')//如果str1的值为'\0',str2的值也为'\0',表示两个字符串相等
			return 0;
		str1++;//指针向后移动
		str2++;
	}
	return *(const unsigned char*)str1 - *(const unsigned char*)str2;//如果两个字符串不相等,则返回它们的差值
}

或 简洁写法:

int my_strcmp(const char* s1, const char* s2)
{
	assert((s1 != NULL) && (s2 != NULL));
	while(*s1 && (*s1==*s2))
	   	s1++,s2++;
	return *(const unsigned char*)s1 - *(const unsigned char*)s2;
}

上面是返回差值的写法,下面给出返回1,-1,0的写法参考:

int my_strcmp(const char *str1, const char *str2)
{
    assert((str1 != NULL) && (str2 != NULL));
    
    while ((*str1 != '\0') && (*str2 != '\0'))
    {
        if (*str1 == *str2)
        {
            str1++;
            str2++;
        }
        else
        {
            if (*str1 > *str2)
                return 1;
            else
                return -1;
        }
    }if (*str1 == '\0' && *str2 == '\0')
        return 0;
    else if (*str1 == '\0' && *str2 != '\0')
        return -1;
    else if (*str1 != '\0' && *str2 == '\0')
        return 1;
} 

6. strncmp函数

strncmp()首先将str1第一个字符值减去str2第一个字符值。若差值为0继续比较,直到字符结束标志’\0’;若差值不为0,则将差值返回。

前面的my_strcmp稍微改动下即可:

int my_strncmp(const char* str1, const char* str2, size_t n)
{
	assert((str1 != NULL) && (str2 != NULL));//判断str1和str2是否为NULL
	while ((n-->0)&&(*str1 == *str2))//如果*str1和*str2相等,进入循环
	{
		if (*str1 == '\0')//如果str1的值为'\0',str2的值也为'\0',表示两个字符串相等
			return 0;
		str1++;//指针向后移动
		str2++;
	}
	return *(const unsigned char*)str1 - *(const unsigned char*)str2;//如果两个字符串不相等,则返回它们的差值
}

或 返回1,-1,0的写法参考

int my_strncmp(const char *s1 , const char *s2 , size_t n)
{
	assert((str1 != NULL) && (str2 != NULL));
    for( ; n > 0 ; ++s1, ++s2, --n)//for后面做多件事情,分别指向下一个元素,以及n减一
        if(*s1 != *s2)//条件运算符优先级最低.解址符优先与关系运算符
            return   ( *(unsigned char *)s1 < *(unsigned char *)s2 ? -1 : 1 );//条件运算符进行比较
        else if(*s1 == '\0')//碰到终止字符结束.
            return (0);
    return (0);//字典比较每一个字符相等或者碰到终止,则返回0
}

7. strcat函数

char* my_strcat(char* dest, const char* src)
{
	//判断dest和src是否为NULL
	assert(dest && src);
	//把dest的起始位置赋值给ret,以便返回
	char* ret = dest;
	while (*dest != '\0')//dest指针移动到字符串末尾
	{
		dest++;//dest指针向后移动 注意这里必须单独++ 不能放到判断里去
	}
	//此时dest的指针已指向最后一个位置
	while ((*dest++ = *src++) != '\0');//循环追加字符

	return ret;
}

char* my_strcat(char *dest , const char *src)
{
	assert(dest && src);
    char *s;//暂存进行拷贝作用
    for(s = dest;*s != '\0';++s)
        ;//s指向s1的结尾
    for( ; (*s = *src) != '\0'; ++s, ++src)
        ;//将s2拷贝到s1末尾,直到遇到s2的结束符
    return(dest);//可以不测试返回值
}

注:赋值表达式的返回值为等号左边的值

8. strncat函数

C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

char* my_strncat(char* dest, const char* src, size_t count)
{
	assert(dest && src);

	char* ret = dest;
	while (*dest != '\0')//dest指针移动到字符串末尾
		dest++;
	int i;
	for (i = 0; i < (int)count; i++)
		dest[i] = src[i];

	return ret;
}

9. atoi函数

int isspace(int x)  
{  
    if (x == ' ' || x == '\t' || x == '\n' || x =='\f' || x =='\b' || x == '\r')  
        return 1;  
    else   
        return 0;  
}

int isdigit(int x)  
{
    if (x <= '9' && x >= '0')           
        return 1;   
    else   
        return 0;  
}

int my_atoi(const char *nptr)  
{  
    int c;              /*当前字符*/  
    int total;          /*总计字节数*/  
    int sign;           /*正数or负数*/     
  
    /*跳过开头的空格*/  
    while (isspace((unsigned char)*nptr))  
        ++nptr;  
  
    c = (unsigned char)*nptr++;  
    sign = c;           /*保存符号*/  
    if (c == '-' || c == '+')  
        c = (unsigned char)*nptr++;    /* skip sign */  
   
    total = 0;  
    while (isdigit(c)) 
    {  
        total = 10 * total + (c - '0');     /*求和*/  
        c = (unsigned char)*nptr++;    /*获取下一个字符*/  
    }if (sign == '-')  
        return -total;  
    else  
        return total;  
}

注:atol只要把函数返回值类型改为long,变量total的类型改为long就行了。

10. itoa函数

char* myitoa(int value, char* str, int radix) {
    static char dig[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int n = 0, neg = 0;
    unsigned int v;
    char* p, * q;
    char c;
    /* 负数:十进制直接在前面加负号,其它进制用补码 */
    if (radix == 10 && value < 0) {
        v = -value;
        neg = 1;
    }
    else
    {
        v = value;
    }
    /* 其它进制的负数转为unsigned int类型,即取了补码 */
    do {
        str[n++] = dig[v % radix];
        v /= radix;
    } while (v);
    if (neg)
        str[n++] = '-';
    str[n] = '\0';
    /* 字符串反转 */
    for (p = str, q = p + (n - 1); p < q; ++p, --q)
        c = *p, * p = *q, * q = c;
    return str;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吾爱技术圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值