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谢谢观看
干了一天 终于把这个章节给复习完了