字符函数和字符串函数

 1.strlen

 我们先拿到首元素a的地址  一直往后找 

找到\0 则停止

 打印随机值

一直往后找  找\0

计数器实现  计算字符串的长度

方法一 用指针实现 

方法二

用指针减指针的方法

#include<stdio.h>
#include<assert.h>
int my_strlen(const char* str)
{
	assert(str != NULL);
	//让指针p指向str的首字符的地址
	const char* p = str;
	while (*p != '\0')
	{
		p++;
	}
	//指针减去指针得到的是两指针之间的长度距离
	return p - str;
}
int main()
{
	char str[] = "abcdef";
	int ret=my_strlen(str);
	printf("%d", ret);
	return 0;
}

 方法三第三种是用函数递归的方法模拟实现strlen函数

#include<stdio.h>
int my_strlen(const char* str)
{
	if (*str == '\0')
	{
		return 0;
	} 
	else
	{
		//递归
		return 1 + my_strlen(str + 1);
	}
}
int main()
{
	char str[] = "abcdefg";
	int ret=my_strlen(str);
	printf("%d", ret);
	return 0;
}

一道坑的题

hehe

搜索库得知strlen的返回值是size_t

size_t

unsigned int 被typedef  重新命名为size_t

那么返回值是一个无符号数 

 那么3-6==-3

-3  在运算之后是以补码形式存在的

补码形式1000000000000000000000000000000000000000000011

转化成原码  11111111111111111111111111111111111111111111111111100

又因为这是一个无符号数 那么最高位也代表数值位

因此它转化成了一个很大的数

其实我们使用自己创建的函数

打印haha

因为我们自己实现的函数返回类型是int 

2.strcpy

把源头拷贝到目的地   即是把后面的拷贝到前面

通过查找库可知 strcpy返回的是目的地的起始位置

满分代码实现strcpy

首次我们得记住断言

其次我们来分析一下while循环的判断条件

*  与++ 操作符是同一级别的  通过查阅

应该从右向左进行运算

那么dest++  但是这是一个先使用后++的式子

那么同样先得到字符b再++指向下一个

当src指向\0时   条件是假

那么while循环终止

源头字符串必须包含\0  如果没有那么src找不到\0 将无法停止

源字符串为常量字符串  不可以被修改入arr1

只有当我们放到数组中 才可以进行修改如arr2

再举个例子说明一下

 当把常量字符串赋给p1和p2时

我们不是把整个字符串赋给它们的

而是把首字符的地址赋给了指针    因此不可这样比较

3.strcat

 把源头追加到目的地

目标空间不够大

并且目标空间要有\0

我们源头就是从目标\0处开始追加的

 并且源头必须有\0

我们追加时 源头中的\0也会追加过去

自己实现strcat  

 返回目的地

4.strcmp

注意参数与返回值 

strcmp的比较规则

p1和p2存放两个字符串的首字符地址 

分析之前切记一点  

strcmp比较的不是上来就直接比较字符串的长度来断言谁大谁小

我们先从第一个字符开始比较

由此可知 a<s

那么我们可知p2是大于p1的   因此返回一个负数

接下来就不用再继续比较了

如果当前面字符都相等时  我们再接着比较后面的字符的ASCII码值

直到两个字符串都比较到\0时

我们才停止比较

如果前面的字符都相等   

当p1比较到\0时

p2还没到\0时

那么证明p2>p1  返回一个负值

 两个字符串比较到\0  发现依旧相等

那么ret接收的返回值是0

代码实现strcmp

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1,const char* str2)
{
	assert(str1 && str2);
	//只要相等才循环
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		//向后走
		str1++;
		str2++;
	}
	//大于
	if (*str1 > *str2)
	{
		return 1;
	}
	//小于
	else
	{
		return -1;
	}
}
int main()
{
	char str1[] = "abcdefg";
	char str2[] = "aleo";
	int ret=my_strcmp(str1, str2);
	printf("%d",ret);
	return 0;
}

当你要求返回它俩相减的值时

总结

它们并不是说追加几个

它们都是以\0作为结束标志的

直到遇到\0时  我们才会停止

所以以上库函数是不够安全的

如何解释呢?

举个例子

当我们要把arr2拷贝到arr1时

肉眼可见  arr1是不足以提供空间大小的

但是这个比较拉跨  它以\0为执着追求的对象

因此还是硬生生的把arr2拷贝到arr1中去了   这就不安全了

 因此我们引入了相对安全的字符串函数

 5.strncpy

你让拷贝几个字节就拷贝几个

 注意当源头字符串不够多时

像这种情况

我们把arr2拷贝过去只有3个字节数

那么我们还要拷贝三个字节数  怎么办呢?

我们只能在紧接着拷贝三个\0到目的地去 

 模拟实现strncpy

6.strncat

接下来分析一下strncat的特点

从被追加字符串的\0处开始追加指定的个数

并且追加完毕之后再补上一个\0

这是为了确保追加完之后的字符串依然是一个字符串

 在追加的过程中我们可以指定追加的字节个数

从hello字符串的\0处开始追加

当指定长度追加完之后

我们肯定希望追加之后仍为一个字符串

因此我们还要再补上一个\0

 strncat不同于strncpy

当你要求追加的个数大于源头字符串长度时

我们不再把未完成追加的地方补\0

而是仅仅根据第一个特点

从上一个字符串\0处开始追加 直到追加完时

再在结束处补\0即可

如图

7.strncmp

返回0

当改为4时返回一个负数

注意

返回值的规则是与strcmp是相同的 

8.strstr

查找子字符串

即查找一个字符串是否是原字符串的子串

知识

 strstr函数是返回d的地址的

那么就从d开始打印字符串

 再举个例子

查找到子串 之后返回d的地址  

接着从这个地址开始打印字符串

 倘若不存在该字串时

函数strstr返回的是NULL

实现strstr函数

 不建议p1 p2走 会有误差 

先一个cur指向p1最初指向

当循环一趟之后发现没有相匹配的字串时

我们要让cur往后走一个

为什么不直接让p1往后走呢?

因为可能字符串过于复杂时  会有如图情况的出现

三个b连在一起  所以我们只能再创建一个cur来撑起每一次的循环

当从第一个字符开始不行时

我们把cur往后移一位

 然后把s1赋成cur 

那么我们又可以从第二位开始重新查找

#include<stdio.h>
#include<assert.h>
char* my_strstr(char* arr1, char* arr2)
{
	assert(arr1 != NULL);
	assert(arr2 != NULL);
	char* p1 = arr1;
	char* p2 = arr2;
	char* cur = p1;
	if (*p2 == '\0')
	{
		return p1;
	}
	//cur指向最后\0之前
	while (*cur)
	{
		if ((*p1 == *p2)&&(*p1!='\0')&&(*p2!='\0'))
		{
			p1++;
			p2++;
		}
		//如果当p2都指向最后一个\0时 那么证明肯定找到了
		if (*p2 == '\0')
		{
			//返回这次最初的地址
			return cur;
		}
		cur++;
	}
}
int main()
{
	char arr1[] = "abcdefg";
	char arr2[] = "bcd";
	char *p=my_strstr(arr1, arr2);
	printf("%s", p);
	return 0;
}

 9.strtok

好吧 总结一下strtok函数作用的过程

嗯好

首先我们要明确参数的作用以及指向

str指向第一个字符串的

sep这个是指向分隔符的    @    .    等等的一系列的分隔符

然后

函数strtok开始起作用

过程  这个函数会去这个字符串中去找  肯定是从头字符开始找  这没问题

然后直到找到一个分隔符

我们把这个分隔符改为\0 那么第一次寻找停止

关键来了  我们改完之后要返回一个地址

返回什么呢?当然要返回第一次开始查找的首字符地址

那么我们有了这个地址  

那不就好办了吗?

直接可以打印出zpw

接下来从\0后一个继续寻找找到下一个分隔符    .

那么我们继续上述操作把   .   改为\0

返回首字符 b的地址  那么打印bitedu

接着找直到找到最后的\0

我们停止寻找  返回第三次开始寻找时的首字符t的地址

那么输出tech

 

ok

我们来分析  这个函数的具体使用操作

首先初始化  第一个参数是字符串首元素的地址

那么由上面分析可知

最终把分隔符变成\0  返回值为为第一个字符的地址 

接着就是根据这个字符的地址打印第一个字符串

ret!=NULL  为判断条件

由于分隔符变成了\0

所以下一个strtok的第一个参数应该是NULL

然后到判断条件看是否返回的地址是否为NULL

直到打印最后一个字符串段tech之后

接着再往后找时   

返回值ret接收  会是NULL  那么不符号判断条件

循环结束

10.streror

11.islower 

判断是否为小写字母

若是   返回非0数字

若不是   返回0

 12.  memcpy  memmove

这一系列字符串函数  进行时都具有局限性

像strcpy每一次只能拷贝一个字节的大小 

并且遇到00即\0时  我们就停止下来  如图我们只拷贝了  01 00

因此我们引入

void*类型 可以适用于任何类型的指针

 

 

实现memcpy函数

 size_t num表示要拷贝的字节数

那么由于我们是要一个字节一个字节的拷贝的

但是由于是void*型

因此我们要强制转化为char*类型 那么使其一个字节一个字节的拷贝

边拷贝边向后移动

因此我们只需要拷贝num次即可

 举个例子

把12345  拷贝到34567上去

当拷贝的源头与目的地有一定关联时

会发生覆盖的现象  两个有交集会导致拷贝失败

如何处理重叠拷贝的现象?

用memmove

 实现memmove

当我们在同一个数组中进行拷贝时

难免会出现重叠的现象  倘若我们不使用库函数memmove

那么我们要自己实现它

思路总结:


先设  源头为src   目的地dest指向

当dest<src时  我们要把源头从前向后拷贝

即是从3开始拷贝 一直拷贝到7即可

当dest>src&&dest<src+count时

我们需要把src从后向前进行拷贝  

即是从7开始一直拷贝到3

当dest>src+count时  源头与目的地不重叠

那么从前向后 拷贝也行  从后向前拷贝也可以

由于第三种情况是不覆盖的情况

那么src从前向后  和  从后向前拷贝到dest都可以

那么分类实现时 我们可把 第一 三种情况一组  也可以把二三情况一组

 代码

从后向前拷贝时 比较难

假设我们要拷贝20个字节大小的源头到目的地

那么count先减一 那么变成19

dest src强制转化之后  加上19 再解引用

它们俩分别得到了最后一个字节的内容

再进行赋值

count--  while循环

那么即可实现从后向前拷贝 

总代码

由于第三种情况是不覆盖的情况

那么src从前向后  和  从后向前拷贝到dest都可以

那么分类实现时 我们可把 第一 三种情况一组  也可以把二三情况一组

下面这一种就是把第二 三情况一组

#include<stdio.h>
//                   目的地          源头     拷贝的字节数
void*my_memmove(void*dest,const void*src,size_t count)
{
	char* ret = dest;
	//分为两大种情况
	if (dest < src)
	{
		//把源头src从前往后拷贝
		while (count--)
		{
			*(char*)dest = *(char*)src;
			++(char*)dest;
			++(char*)src;
		}
	}
	else
	{
		//把源头src从后往前拷贝
		while (count--)
		{
			*((char*)dest + count) = *((char*)src + count);
		}
	}
	return dest;
}
int main()
{
	char* arr1[] = "abcdefgh";
	my_memmove(arr1, arr1 + 2, 4);
	return 0;
}

 

 13.memcmp

 当比较时  类型不单单仅是字符型

我们可以比较任何类型的

还有我们要记住比较的单位是字节数

这里的8表示我们比较arr1 和arr2的前八个字节空间大小

相等返回0

arr1>arr2返回非0正数

arr1<arr2返回负数

 14.memset

内存设置函数  它满足任何类型的设置  

arr表示要被设置的内存

1表示这个要被设置为的内容

切记 10是要被设置的字节数   

那么上面这个例子就是意味着  把arr这个整形数组前十个字节的空间大小 设置成1

ok谢谢观看

干了一天  终于把这个章节给复习完了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值