目录
一、strlen的使用和模拟实现
1.strlen()函数的介绍
strlen:计算的是字符串str的长度,从字符的首地址开始遍历,以 '\0' 为结束标志,然后将计算的长度返回,计算的长度并不包含'\0'。下面是库中的strlen()函数:
size_t strlen (const char* str);
- 函数的参数为------const char* str:字符指针
- 返回值的类型------size_t:无符号整数(即:unsigned int)
前提知识:
- const关键字:是用来定义常量的,若一个变量被const关键字修饰时,那么这个变量的值就不可以被改变。故:这里的 * str 的值是不能被修改的。
- size_t数据类型:表示C中任何对象所能达到的最大长度,它是无符号整数。
1.使用const关键字:这里只是计算字符串的长度,用const修饰,避免将原字符串修改掉。
2.使用size_t数据类型:这里返回计算的字符串长度,长度最小值为0,不可能为负数。
2.strlen()函数的具体使用
场景一:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[10] = "abcde";
int num = strlen(arr);
printf("数组arr的长度为:%d\n", num);
return 0;
}
输出的结果:
场景二:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = { 'a','b','c','d','e' };
int num = strlen(arr);
printf("数组arr的长度:%d\n", num);
return 0;
}
输出的结果:
上述两种场景输出的结果并不相同
为什么会出现这样的结果,我们在用strlen函数时应该注意什么?
3.strlen函数的注意事项
strlen函数:当计算长度时,只有遇到'\0'才会停止计算,同时计算的长度不包含'\0'。
场景一,arr字符数组中存储的是一个字符串(字符串是以'\0'为结束标志的),那么strlen遍历到字符 'e' 时,再向后遍历,就会遇到'\0',此时strlen停止遍历,返回字符个数:5;
场景二:字符'a'、'b'、 'c' 、'd' 、'e'五个字符依次存储在arr的字符数组中,并没有存储'\0',所以'e'字符后面存储的内容我们并不知道有什么。而strlen函数只有遇到'\0'时才停止,所以返回的个数是一个随机值。
故:我们使用strlen函数时,应该检查字符数组是否以'\0'为结束标志。
4.strlen函数的模拟实现
方式一:
//计数器⽅式
int my_strlen(const char* str)
{
int count = 0;
assert(str);
while (*str)
{
count++;
str++;
}
return count;
}
方式二:
//不能创建临时变量计数器,递归的方式
int my_strlen(const char* str)
{
assert(str);
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
方式三:
int my_strlen(char* s)
{
assert(str);
char* p = s;
while (*p != ‘\0’)
p++;
return p - s;
}
补充知识:assert(断言):是一个宏。
assert 的作用是,将括号中的表达式计算出 ,如果其值为假(即为0),那么它会打印出一条出错信息,然后终止程序的运行。这里的assert是防止指针p指向为NULL。
二、strcpy的使用和模拟实现
1.strcpy()函数的介绍
strcpy()函数:是将一个字符串复制到另一块空间地址中 的函数,‘\0’是停止拷贝的终止条件,同时也会将 '\0' 也复制到目标空间。下面是库中的strcpy()函数声明:
char* strcpy(char* destination,const char* source);
函数的参数:
char* destination---------目标字符串的首地址
const char* source------源地址:被复制的字符串的首地址,用const修饰,避免修改掉被拷贝的字符串
函数的返回值类型:
char*:返回的是目标字符串的首地址
2.strcpy()函数的具体使用
场景一:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[10] = "########";
printf("%s\n", strcpy(arr, "hello"));
return 0;
}
输出的结果:
场景二:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[10] = "**********";
char arr2[10] = "abcd";
printf("%s\n", strcpy(arr1, arr2));
return 0;
}
输出结果:
3.strcpy()函数的注意事项
1.源字符必须以 '\0'结束:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[10] = "**********";
char arr2[] = { 'a','b','c','d' };
printf("%s\n", strcpy(arr1,arr2));
return 0;
}
这里的代码会出错,'\0'是停止拷贝的终止条件,arr2字符数组所在的内存空间后面存储的内容并不知道,不遇到 '\0' 拷贝就不会停止,这就会导致越界访问,程序就会出现问题。
2.目标空间必须足够大,以确保能放源字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[5] = "*****";
char arr2[] = "hello world";
printf("%s\n", strcpy(arr1,arr2));
return 0;
}
这里虽然拷贝成功并将结果输出了,但程序却崩溃了。目标空间太小,不足以放置拷贝的源字符串,会造成溢出的情况
3.目标空间必须可变
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char* str1 = "hello world";
char str2[10] = "*********";
printf("%s\n", strcpy(str1,str2));
return 0;
}
这里的程序也出现了错误。str1指向的是常量字符串,是不可以被修改掉的,目标空间必须是可以被修改的,因为要将拷贝的字符串放在目标空间中。而源字符串可以是能够修改的,也可以是不能修改的,因为strcpy函数的第二个参数已经用const关键字修饰了,保证了拷贝过程中不会被修改。
4.strcpy函数的模拟实现
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
三、strcat 的使用和模拟实现
1.strcat()函数的介绍
strcat的函数原型的声明为:
char*strcat(char* strDestination, const char* strSource);
其返回值为char*,需要传入两个参数
strcat函数可以将一个字符串追加到另外一个字符串的后面
2.strcat函数的具体使用
比如在“hello”后面追加一段“world”就可以用strcat函数来实现
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[30] = "hello ";
char arr2[] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);//追加字符串
return 0;
}
打印结果:
3.strcat函数的注意事项
• 源字符串必须以 '\0' 结束。
• 目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始。
• 目标空间必须有足够的大,能容纳下源字符串的内容。
• 目标空间必须可修改。
• 字符串自己给自己追加,如何?
自己给自己追加
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
//1. 找到目标空间的\0
while (*dest)
{
dest++;
}
//2. 拷贝
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "hello ";
my_strcat(arr1, arr1);
printf("%s\n", arr1);
return 0;
}
这样会一直追加下去,并且会造成访问越界。
4.strcat函数的模拟实现
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest)
{
dest++;
}
while ((*dest++ = *src++))
{
;
}
return ret;
}
四、strcmp的使用和模拟实现
1.strcmp()函数的介绍
strcmp函数是C语言中的字符串比较函数,用于比较两个字符串的大小。
函数原型:
int strcmp ( const char * str1, const char * str2 );
- 该函数接受两个参数,分别为要比较的两个字符串的指针。
- 返回值为整形,表示两个字符串的大小关系。
• 标准规定:
◦ 第⼀个字符串大于第⼆个字符串,则返回大于0的数字
◦ 第⼀个字符串等于第⼆个字符串,则返回0
◦ 第⼀个字符串小于第⼆个字符串,则返回小于0的数字
◦ 那么如何判断两个字符串? 比较两个字符串中对应位置上字符ASCII码值的大小。
2.strcmp()函数的具体使用
strcmp函数的比较规则是按照字典序进行比较,即逐个字符比较。具体的比较过程如下:
- 若两个字符相等,则继续比较下一个字符。
- 若两个字符不相等,则返回它们的ASCII码差值(ASCII码值大的字符串大)。
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "appple";
char str2[] = "banana";
int ret = strcmp(str1, str2);
if (ret == 0)
printf("str1和str2相等\n");
else if (ret > 0)
printf("str1大于str2\n");
else
printf("str1小于str2\n");
return 0;
}
3.strcmp函数的注意事项
1、字符串的比较是按照字典序进行的,因此大小写字母的比较结果不同。
2、在比较字符串时,需要确保字符串以'\0'结尾,否则可能会导致比较结果不正确。
例如这个代码:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "hello";
char str2[] = "hello";
str1[5] = '!';
int ret = strcmp(str1, str2);
if (ret == 0)
printf("str1和str2相等\n");
else if (ret > 0)
printf("str1大于str2\n");
else
printf("str1小于str2\n");
return 0;
}
结果如下:
那为什么会这样呢?
分析如下
在这段代码中,我们将str1字符串的结尾标识符'\0'改成了'!',运行代码后,输出结果为"str1大于str2"。这是因为str1字符串没有以'\0'结尾,所以strcmp函数会继续比较下去,直到遇到str2字符串的结尾标识符'\0'。由于str1字符串比str2字符串长,所以str1被认为是大于str2。
4.strcmp函数的模拟实现
#include <stdio.h>
#include <string.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[] = "appple";
char str2[] = "banana";
int ret = my_strcmp(str1, str2);
if (ret == 0)
printf("str1和str2相等\n");
else if (ret > 0)
printf("str1大于str2\n");
else
printf("str1小于str2\n");
return 0;
}