文章目录
一、strlen的使用和模拟实现
1.1. strlen的使用
strlen是求字符串长度的函数,它的返回类型是无符号整型,参数是用来接收第一个字符的地址的指针
size_t strlen ( const char * str );
注意:
- 字符串以字符 '\0’作为结束表示,strlen返回的是\0之前字符的个数,不包括 \0
- 函数参数指向的字符串必须以 \0 结束,不然strlen找不到\0就会一直计算,程序会崩溃;
- 函数参数的返回类型为size_t无符号整型,因此返回值不能为负数;
例题:结果打印什么?
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if(strlen(str2)-strlen(str1)>0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
strlen(str2)-strlen(str1)=-3
-3的补码在32位条件下为:
11111111 11111111 11111111 11111101
strlen返回的是无符号整型,因此符号位无效,换算成十进制位为一个很大的大于0的整数,因此打印的是str2>str1
1.2. strlen的模拟实现
strlen的模拟实现有三种:计数器方法、指针-指针、递归
1.2.1. 计数器方法模拟实现strlen
思路:定义一个计数器count,如果指向字符串首元素的指针*str!='\0'
,那么count+1,str+1,直到找到 \0,返回count即为字符串元素的个数
size_t my_strlen(const char* str)
{
assert(str);
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "abcdefg";
int ret=my_strlen(arr);
printf("%d ", ret);
return 0;
}
1.2.2. 指针-指针方法模拟实现strlen
思路:用指针str1记录最后一个字符的地址,然后str1-str就是首末元素地址之间元素的个数
size_t my_strlen(const char* str)
{
assert(str);
char* str1 = str;
while (*str1)
{
str1++;
}
return str1 - str;
}
int main()
{
char arr[] = "abcdefg";
int ret=my_strlen(arr);
printf("%d ", ret);
return 0;
}
1.2.3. 递归方式模拟实现strlen
该方法可以不用创建临时变量就可以模拟实现strlen
思路:
size_t my_strlen(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str+1);
}
int main()
{
char arr[] = "abcdefg";
int ret=my_strlen(arr);
printf("%d ", ret);
return 0;
}
二、strcpy的使用和模拟实现
2.1. strcpy的使用
strcpy是用来拷贝字符串的函数,作用是将源字符串拷贝到目标字符串中,返回的是目标字符串的首元素地址,类型是char*,有两个参数,分别是目标字符串的首元素地址destination和源字符串的首元素地址 source
char* strcpy(char * destination, const char * source );
注意:
- destination是目标字符串,source是要拷贝的字符串;
- 字符串必须以 \0结尾;
- 会将源字符串中的 \0也拷贝到目标空间;
- 目标空间必须足够大,以确保能存下源字符串;
- 目标空间必须可以修改,不能是常量字符串;
2.2. strcpy的模拟实现
char* my_strcpy(char* dest, const char* str)
{
assert(dest && str);
char* src = dest;
while (*dest++=*str++)
{
;
}
return src;
}
int main()
{
char arr[] = "abcdef";
char* p = "nll";
char* ret=my_strcpy(arr, p);
printf("%s", ret);
return 0;
}
三、strcat的使用和模拟实现
3.1. strcat的使用
char * strcat ( char * destination, const char * source );
strcat是字符串拼接函数,可以将源字符串拼接到目标字符串的后面,返回目标字符串起始位置的地址.
注意:
- 源字符串必须以 \0 结尾;
- 目标空间字符串也必须以 \0结尾,不然不知道应该在什么位置进行拼接;
- 目标空间必须足够大,能容得下拼接后的字符串;
- 目标空间必须可修改
3.2. strcat的模拟实现
思路:先找到目标空间字符串的 \0,找到要拼接的位置,然后进行字符串拷贝
char* my_strcat(char* dest, char* str)
{
assert(dest && str);
char* ret = dest;
while (*dest)//找到拼接位置
{
dest++;
}
while (*dest++ = *str++)//进行字符串拷贝
{
;
}
return ret;
}
int main()
{
char arr[20] = "abcdef";
char* p = "nll";
char* ret=my_strcat(arr, p);
printf("%s", ret);
return 0;
}
四、strcmp的使用和模拟实现
4.1. strcmp的使用
int strcmp ( const char * str1, const char * str2 );
strcmp是比较字符的函数,比较的是在对应位置上字符的ASCII码值的大小,并不在乎字符串长度的大小
如果第一个字符串大于第二个字符串,则返回一个大于零的数字;
如果两字符串相等,返回零;
如果第一个字符串大于第二个字符串,则返回一个大于零的数字;
4.1. strcmp的模拟实现
int my_strcmp(char* str1, char* str2)
{
assert(str1 && str2);
while (*str1==*str2)//对应位置字符相等进入循环
{
if (*str1 == '\0')//如果走到'\0'位置还是相等,str1++,str2++就会越界访问,所以要添加终止循环的条件
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char* str1 = "abcdef";
char* str2 = "nll";
int ret=my_strcmp(str1, str2);
printf("%d", ret);
return 0;
}
五、strncpy、strncat、strncmp的使用
5.1. strncpy的使用
char * strncpy ( char * destination, const char * source, size_t num );
- num表示将num个字符从源字符串中拷贝到目标空间;
- 如果源字符串中字符个数少于num个,则拷贝完成字符串后,在后面追加\0,直到num个.
示例:
int main()
{
char arr[20] = "helloxxxxxx";
char* p = "world";
strncpy(arr, p, 8);
printf("%s", arr);
return 0;
}
从调试窗口可以看出,将“world”拷贝到arr后,向后追加了3个\0
5.2. strncat的使用
char * strncat ( char * destination, const char * source, size_t num );
将source前num个字符追加到dest字符串的结尾,再追加一个 \0;
若source指向的字符串的个数少于num个时,只会将字符串内容追加到目标字符串结尾,再追加一个\0.
5.3. strncmp的使用
int strncmp ( const char * str1, const char * str2, size_t num );
比较str1和str2的前num个字符,如果相等就继续向后比较,最多比较num个字符,如果提前发现不一样就提前结束.
六、strstr的使用和模拟实现
6.1. strstr的使用
char * strstr ( const char * str1, const char * str2);
在str1中寻找str2,返回str2在str1中第一次出现的位置,字符串的比较匹配不包括\0,以\0作为结束标志
6.2. strstr的模拟实现
- 简单情况,在目标字符串中只需要寻找一次
先比较*str1
和*str2
,不相等,让str1++
,str2不动,在进行比较,若不想等,str1就一直+1,直到两者相等,之后str1++,str2++,在进行比较,若相等,str1和str2一直+1,直到str1或str2指向\0的位置,就找完了,然后返回str2与str1两个字符第一次相等的地址
注意:
这时候str1和str2都已经移动到末尾的位置,两者第一次相等的位置没有被记录下来,因此需要创建一个指针变量来记录这个位置的地址;此外,在创建两个指针变量来代替str1和str2,尽量让str1和str2始终不变
char* cur = str1;
char* s1 = NULL;
char* s2 = NULL;
while(*cur)//*cur的值不能为\0,如果为\0,说明目标空间字符串已经全都比较完了,跳出循环说明没有找到
{
s1=cur;//把记录的地址再赋给s1
s2=str2;
while(*s1!='\0'&&*s2!='\0'&&*s1==*s2)
{
s1++;
s2++;
}
if(*s2=='\0')
return cur;
cur++;//记录str1进行比较时候的位置的地址
}
这里
while(*s1!='\0'&&*s2!='\0'&&*s1==*s2)
判断条件中*s1!='\0'
有两种情况
- a b c d e f \0
d e f \0
s1和s2同时走到\0的位置,s2走到\0,说明比较完了,已经找到了,直接返回第一次比较时候位置的地址;- b c d \0
b c d e f \0
s1走到d后,s1++,s2++,这时s1指向\0,s2指向e,e和\0 不相等,那么跳出while(*s1!='\0'&&*s2!='\0'&&*s1==*s2)
的循环,执行cur++,这时cur指向c,在进行比较,不相等,再cur++,直到cur走到\0,跳出大循环
- 复杂情况,需要比较多次
s1走到第一个b后,此时cur指向第一个b,
*s1==*s2
,s1++,s2++,相等,再++,还相等,再++,此时s1指向的b和s2指向的c不相等,那么说明s1从第一b的位置开始找,一定是找不到str2的,因此,s1应该在后一个位置开始比较。这时候跳出循环,cur++,指向第二个b,s1回到第二个b的位置,s2回到str2的位置重新开始寻找。
若找不到,继续重复以上步骤;若找到,此时s2指向\0位置,跳出循环,返回cur.
3. 找不到的情况
这种情况下,s1始终找不到s2,最终s1指向末尾\0,跳出循环,返回NULL
最终的代码实现:暴力查找
char* my_strstr(char* str1, char* str2)
{
assert(str1 && str2);
char* s1 = NULL;
char* s2 = NULL;
char* cur = str1;
if (*str2 == '\0')
return (char*)str1;
while (*cur)
{
s1 = cur;
s2 = str2;
while (*s1!='\0'&&*s2!='\0'&&*s1==*s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)cur;
cur++;
}
return NULL;
}
int main()
{
char str1[] = "This is a simple string";
char* str2= "is";
char* ret= my_strstr(str1, str2);
if (*ret == NULL)
printf("没找到");
else
printf("%s \n", ret);
return 0;
}