[C语言]string.h常用字符串库函数详解+模拟实现

[C语言]string.h常用字符串库函数详解+模拟实现_第1张图片

[C语言]string.h常用字符串库函数详解+模拟实现_第2张图片


目录

字符串函数

strlen

strcpy

strcat

strcmp

strstr

内存函数

memcpy

memmove


人生百态,苦事之多。烦恼穿心,何来解脱?打开博客,吸取干货。

以码消愁,以串解忧。泱泱年轮,唯有生活。一起撸串,快乐几何。

字符串函数

strlen

在C语言中,我们通过字符数组的方式来存储字符串,也可以直接使用常量字符串,如何去判断字符串的长度便是一个问题。比如char ch[100] = "abcdef";  难道长度就是100吗?显然不对,我只有abcdef这六个字母,长度应该为6才对,所谓上天有好生之德,留下了一个库函数strlen可以求得字符串的长度。

#include 
#include 
int main()
{
    char ch[100] = "abcdef";
    int len = strlen(ch);
    printf("%d\n", len);//结果为6
    len = strlen("abcdef")
    printf("%d\n", len);//结果依然为6
    return 0;
}

strlen使用起来竟如此方便,只需把数组名或常量字符串往里一掷,它便会自己返回字符串长度。

如何模拟实现计算字符串长度?我们需要知道,C语言是如何打印字符串的。

char ch[] = "abcdef";
printf("%s\n", "abcdef");
printf("%s\n", ch);

放一个字符串以%s打印显然没问题,但是放一个数组名用%s打印也没问题,而数组名是什么?数组名是数组首元素地址(除开sizeof和&的特殊情况)那是否常量字符串也是首元素地址

printf("%p\n", "abcdef");

由此可以得知,其实C语言打印字符串是根据它的首元素地址一直往下找,找到一个打印一个,那么何时停止?根据我们扎实的C语言基础知识,我们都知道字符串以\0结尾,所以找到\0的时候便不用再打印了

知道了原理,接下来就是模拟实现了。既然字符串是根据地址找的,我们就传入首地址,既然是以\0结尾的,我们就把\0之前的字符全部统计一遍,这样就能得到字符串长度了。

int my_strlen(const char* str)//因为只需要统计个数 不需要改变字符 所以用const
{
    assert(str); //防止传NULL指针 引用头文件assert.h进行断言
    int len = 0;
    while(*str != '\0')
    {
        str++;
        len++;
    }
    return len;
}

strcpy

strcpy函数用于把一个字符串拷贝到另一个字符串中。但要在使用的时候注意一些事项:

1. 目标空间必须足够大   2. 源空间必须存在\0   3.目标空间必须可以更改

char ch[100];//目标空间可更改 且足够大
char cpy[] = "abcdef";//源空间存在\0
strcpy(ch, cpy);//将cpy的内容拷贝到ch中
printf("%s\n", ch);

既然我们知道字符串以\0结尾,那我们只需要知道他们两个字符串的首元素地址,然后到被拷贝字符串的\0为止全部拷贝进去即可。

模拟实现:

char* my_strcpy(char* dest, const char* src)
{
    assert(dest && src);//断言
    char* ret = dest;//用于返回给用户拷贝完后的地址 方便用户操作
    while(*dest++ = *src++);//当src不为\0时拷贝到dest中 并后移一位
    return ret;
}

strcat

strcat(str1, str2);会把str2的内容追加到str1后面。​但也有一些注意事项:

1.目标空间必须足够大   2.目标空间必须存在\0   3.源空间必须存在\0  4.目标空间必须可更改

char ch1[100] = "I love";//目标空间足够大且存在\0且可更改
char ch2[] = " You";//源空间存在\0
strcat(ch1, ch2);
printf("%s\n", ch1);

模拟实现:(找到目标空间\0之后和strcpy实现方式类似)

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while(*dest)
	{
		dest++;
	}
	while(*dest++ = *src++);
	return ret;
}

strcmp

当我们需要比较两个字符串是否一致时,许多小白都会直接用 == 进行比较,但我们之前提到过,字符串本质上是首元素地址, ==进行比较时比较的也是首元素的地址,所以答案会与我们的期待的不符,实际上C语言有一个库函数strcmp可以比较两个字符串,相等返回0,不相等时返回值根据第一个不等的字符词典序进行返回(大于返回>0 小于返回<0) 如: a 和 b   显然 词典序中a

char ch1[] = "abc";
char ch2[] = "abc";
char che3[] = "abd";
int flag1 = strcmp(ch1, ch2);//0
int flag2 = strcmp(ch1, ch3);//<0

模拟实现:从头开始比较,相等继续比较,不等根据词典序返回,比到\0返回0

int my_strcmp(const char* str1, const char* str2)
{
	while(*str1 == *str2 && *str1 && *str2)
	{
		if(*str1 == '\0')
			return 0;
		str1++,str2++;
	}
	return *str1 - *str2;
}

strstr

strstr的作用是在一个字符串中查找子串并返回地址。

char ch[] = "I love you";
char* low = strstr(ch, "love");//在ch中查找 love  找到返回love的首地址 找不到返回NULL
printf("%s\n", low);//love you

模拟实现:既然是找子串,那么主串没找完且长度大于子串的情况下都得继续找,所以为了方便我们就多创建一个指针指向当前主串要找的起始位置,然后循环比较。

char* my_strstr(const char* str1, const char* str2)
{
	char* s1 = str1;
	char* s2 = str2;
	char* cp = str1;
	while(*cp)
	{
		s1 = cp;
		s2 = str2;
		while(*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if(*s2 == '\0')
			return cp;
		cp++;
	}
	return NULL;
}

内存函数

既然干货都发出来了,何不再来了解了解string.h中的内存函数?

memcpy

内存函数的优点就在于可以自己指定字节数,且兼容所有的指针类型。

int a = 4;
inb b;
memcpy(&b, &a, 4);//把a地址开始到后面4个字节复制给b(a为整型 4个字节 即把a复制给b)

实现:我们需要知道要兼容所有的地址类型,我们需要void*指针 由于又是按字节复制 所以我们将void*使用时强制类型转换为char*即可。

void* my_memcpy(void* dest, const void* src, int size)
{
    assert(dest && src);
    void* ret = dest;
    while(size--)
    {
        *(char*)dest = *(char*)src;
        dest = (char*)dest + 1;//不用++的原因是 void*指针直接++只有在某些编译器上才可以
        src = (char*)src + 1;
    }
    return ret;
}

memmove

memmove相比memcpy啥的可厉害太多了,memcpy如果自己复制给自己就有可能因为重叠覆盖而出错,而memmove就修复了这一点。

memmove是如何弥补这一点的?比如 12345  你要复制45 到34的位置 如果我们先复制5 那么就会把4覆盖 复制4的时候就会出现问题, 但我们先复制4便可以解决。所以memmove可以根据你复制的目标位置和源位置作比较得到的结果,来判断先复制左端还是右端,避免重叠空间被覆盖。

用法与memcpy一致 所以不再演示。

模拟实现:

void* memmove(void* dest, const void* src, int size)
{
    assert(dest && src);
    void* ret = dest;
    if(dest <= src || (char*)dest >= (char*)src + size)//即没有重叠空间或目标空间在左方时
    {
        while(size--)
        {
            *(char*)dest = *(char*)src;
            dest = *(char*)dest + 1;
            src = *(char*) + 1;
        }
    }
    else//当重叠时 dest在右方时
    {
        dest = *(char*)dest + size;
        src = *(char*)src + size;
        while(size--)
        {
            *(char*)dest = *(char*)src;
            dest = *(char*)dest - 1;
            src = *(char*) - 1;
        }
    }
}

你可能感兴趣的:(学习,c语言,c++,算法)