目录
前言
一、最常用的字符串操作函数
1.strlen函数
2.strcpy函数
3.strcat函数
4.strcmp函数
二、字符串查找函数
1.strstr函数
2.strtok函数
三、报错函数
strerror函数
四、内存操作函数
1.memset函数
2.memcpy函数
3.memmove函数
4.memcmp函数
在编写程序时,为了更加方便的对字符串进行操作,我们会用到字符串操作函数。本文带大家认识一些常用的字符串函数和内存操作函数,以及他们的底层代码。了解了他们的逻辑之后以后用起来就能更加的得心应手了。
注:下文中的size_t的意思是无符号整形。const表示被修饰的指针不能被解引用修改(上传到函数的字符串本身不需要被修改时使用)。
函数格式
size_t strlen ( const char * str );
函数作用
计算字符串长度
strlen和sizeof的区别
1.strlen是一个函数,需要头文件
sizeof是一个字符串,可以直接使用 2.strlen只能计算字符串的长度 sizeof可以计算表达式大小
3.strlen不会算入字符串中最后一个'\0'大小 sizeof会算入'\0'大小
strlen的基本逻辑是使用一个指针,向后移动并且计数,遇到'\0'之后停止计数 。
代码实现
size_t my_strlen(const char* star)
{
assert(star);//assert函数断言确定star不是空指针,需要头文件
char* end=star;
while (*end)
{
end++;
}
return end - star;
}
函数格式
char* strcpy(char * destination, const char * source );
函数作用
拷贝字符串
strcpy注意事项
1.源字符串必须以 '\0' 结束。
2.会将源字符串中的 '\0' 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。4.目标空间必须可变。
strcpy函数的基本逻辑是把源头指针解引用后赋值给目的地指针,将源头指针和目的地指针逐个向后移,直到遇到'\0'之后停止。由于会把'\0'传过去,虽然不会把目的地的字符串全部覆盖,但由于'\0',下次打印时,后面空间不会被访问,于是就实现了拷贝的功能。
代码实现
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
函数格式
char * strcat ( char * destination, const char * source );
函数作用
把两个字符串相连
strcat注意事项
1.源字符串必须以 '\0' 结束。
2.目标空间必须有足够的大,能容纳下源字符串的内容。
3.目标空间必须可修改。
strcat的基本逻辑是,首先让目标函数指针找到结尾的'\0',之后把源头的字符串从'\0'开始拷贝,后者逻辑基本和strcpy相同。
代码实现
char* my_strcat(char* dest,const char* src)
{
char* s1 = dest;
while (*s1)
{
s1++;
}
while (*s1++ = *src++)
{
;
}
return dest;
}
注:不可以让一个函数即是目的地又是源头,在目的地中找到'\0'后,对目的地地址解引用之后改变当中的内容,第一次循环就会把'\0'去除,由于目的地和源头在同一个地址,之后就不再有'\0'这个结束符号了,造成死循环。
函数格式
int strcmp ( const char * str1, const char * str2 );
函数作用
比较两个字符串是否相同
strcmp注意事项
第一个字符串大于第二个字符串,则返回大于 0 的数字第一个字符串等于第二个字符串,则返回 0(要特别注意,相等返回0,和真假无关)第一个字符串小于第二个字符串,则返回小于 0 的数字
strcmp基本逻辑,逐个比较字符串的ASSIC大小 ,找到一个不相等就返回,不再看后面的字符。
代码实现
int my_strcmp(const char* str1,const char* str2)
{
assert(str1);
assert(str2);
while (*str1 == *str2)
{
if (*str1 != '\0')
{
str1++;
str2++;
}
else
return 0;
}
return *str1 - *str2;
}
注: strcpy,strcat,strcmp都有返回值而不是void类型,其目的是可以在printf中直接输入这个函数进行使用。(如下代码)
char* my_strcpy(char* dest, const char* src)
{
assert(dest);
assert(src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char str1[20] = "############";
char str2[] = "abcde";
printf("%s", my_strcpy(str1, str2));
return 0;
}
如果函数返回类型变成void ,会有如下报错:
函数格式
char * strstr ( const char *str1, const char * str2);
函数作用
strstr的作用是在第一个字符串中找是否有第二个字符串,如果有返回从相等同位置开始的地址,如果不存在,返回空指针。
代码实现
char* my_strstr(const char* str1,const char* str2)
{
assert(str1);
assert(str2);
if (*str2 == '\0')
return str1;//如果要比较的字符串是‘\0’,返回原数组地址,C语言语法规定
char* p=str1;//创建每次向后移动一个字节的指针
char* s1 = str1;//创建遍历str1和str2的指针
char* s2 = str2;
while (*p)
{
//放在循环内,便于每次比较不成立时下次循环让两个指针在应有的位置
s1 = p;//s1到达遍历到的那个位置
s2 = str2;//s2到str2开头
while (*s1!='\0'&&*s2!='\0' && *s1 == *s2)
{
//同时遍历两个数组,判断是否相等
s1++;
s2++;
}
if (*s2 == '\0')//如果遍历到了‘\0’,说明相等
return (char*)p;
p++;
}
return NULL;
}
函数格式
char * strtok ( char * str, const char * sep );
函数作用
strok的作用是找到第二个字符串在第一个字符串中的位置,并且把它改为'\0',通过循环可以把分开的字符串依次打印出来。由于此函数会修改原字符传,所以一般对已经备份过的字符串进行操作。
strtok注意事项
1.sep参数是个字符串,定义了用作分隔符的字符集合
2.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
3.strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6.如果字符串中不存在更多的标记,则返回 NULL 指针。
函数使用方法演示
int main()
{
char arr[] = "[email protected]";
char buf[200] = { 0 };
strcpy(buf, arr);//备份字符串
const char* p = "@.";
char* str = NULL;
//第一次使用buf的地址,由于此函数会记住第一次结束后的位置,所以之后用NULL指针
for (str=strtok(buf, p); str!=NULL; str=strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
函数格式
char * strerror ( int errnum );
//errnum是报错后返回的错误数字
了解函数作用前须知
1.在C语言中,存在着自身规定的报错数字,例如,平日里在网上看见的网页404就是一个报错数字。每个报错数字有自己的意义。
2.C语言中存在errno的全局变量,在代码程序出现错误时,系统会自动为errno这个全局变量赋值报错数字。使用这个变量之前,需要用头文件
。
函数作用
strerror可以把报错数字翻译成英文,便于调试人员处理问题。(如下图)
函数使用方法演示
#include
int main()
{
FILE* pf = fopen("test.txt", "r");//打开一个文件
if (pf == NULL)//如果不存在这个文件,pf会被置为空指针
{
printf("%s\n", strerror(errno));
return 1;
}
fclose(pf);//关闭文件
pf = NULL;//pf置为空释放空间
return 0;
}
项目文件夹下没有test.txt这个文件
项目文件夹下有test.txt这个文件,报错信息消失
printf("%s\n", strerror(errno));
可以替换为
perror(" ");//在引号中打入文字会在报错信息前输出(如下图)
函数格式
void * memset ( void * ptr, int value, size_t num );
函数作用
ptr为要开辟地址开头位置的指针,memset会把ptr后的内存都初始化为value的值(用ASSIC码表示),num为初始化内存大小的字节数。
函数使用方法演示
#include
#include
int main()
{
char str[10];
memset(str,'a',8);
return 0;
}
运行过后,str处内存会被修改为如下图:
函数格式
void * memcpy ( void * destination, const void * source, size_t num );
函数作用
将source处的数据逐个字节拷贝到destination处,和strcpy不同,memcpy可以拷贝任何类型的数据,而strcpy只能拷贝字符串。
注意事项
1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
2.这个函数在遇到 '\0' 的时候并不会停下来。
3.如果source和destination有任何的重叠,复制的结果都是未定义的。
函数格式
void * memmove ( void * destination, const void * source, size_t num );
函数作用
和memcpy类似,但是memmove可以拷贝重叠的数据。
#include
#include
int main()
{
char str[] = "memmove can be very useful......";
memmove(str + 20, str + 15, 11);
puts(str);
return 0;
}
函数格式
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
函数作用