目录
一、前言
二、通性易错点
二、strlen
1.知识点
2.自定义实现strlen
三、strcpy
1.知识点
3.自定义实现
四、strncpy
1.知识点
2.自定义函数实现
五、memcpy
1.知识点
3.自定义实现
六、memmove
1.知识点
2、自定义实现
七、strcat
1.知识点
2.自定义实现
八、strncat
1.知识点
2.自定义实现
九、strcmp
1.知识点
2.自定义实现
十、strncmp
1.知识点
2.自定义实现
十一、strstr
1.知识点
2.自定义实现
十二、字符串分类函数
十三、字符转换函数
一、前言
这篇博客是作者知识的阶段性总结,较为基础也较为全面,梳理字符函数和字符串函数。内容形式上有些重复,看起来枯燥,所以选取你需要的学习吧。如果对你起到一点点的帮助,博主的文章写的也是很值得的。
二、通性易错点
以下几个字符串函数共同的易错点,写在这里,就不在后面重复赘述。
①C语言中用字符数组和常量字符串储存字符串。区别在于,常量字符串不可以修改(所以指向常量字符串的指针最好加const修饰,限制对它修改的错误行为),而字符数组可以,所以我们应该选用字符数组储存字符串
②如果字符串函数依托\0作为函数结束标志,那么字符串中必须以\0结尾,以下形式则不可以
char str[]={'a','b','c'};
③长度不受限制的字符串函数(strcpy,strcat,strcmp),遇到\0函数才会停止,如果目标数组空间不足容纳源数组,程序会崩溃
(以上演示将str2拷贝到str1的过程,其他函数类似)
二、strlen
1.知识点
①strlen的返回值为size_t类型,size_t是 C语言定义的一种非负整数,所以判断下方代码结果
int main()
{
char str1[] = "abcd";
char str2[] = "abcde";
if ((sizeof(str1)-sizeof(str2)) > 0)
printf("hello");
else
printf("hi");
}
非负数相减结果不为负,所以为hello
②字符串以\0结束,strlen返回输入地址到\0之间的字符个数
字符串本质是一个数组,里面存入各个元素和自动加入的‘\0’。而单个字符的存入则不会自动加入'\0',如上图的情况,strlen求出的长度就是随机值。
2.自定义实现strlen
//my_strlen
//方法一:count++
int my_strlen_1(char*str)
{
int count = 0;
while (*str != '\0')
{
str++;
count++;
}
return count;
}
//
//方法二:递归
int my_strlen_2(char*str)
{
if (*str != '\0')
{
return (1 + my_strlen_2(++str));//注意这里应该是前置加加,否则会死循环。
}
else
return 0;
}
//方法三:指针相减
int my_strlen_3(char*str)
{
char*start = str;
char*end = str;
while (*end != '\0')
{
end++;
}
return end - start;
}
int main()
{
char arr[] = "abcd";
int len = my_strlen_3(arr);
printf("%d\n", len);
return 0;
}
三、strcpy
1.知识点
①功能:将src的内容拷贝到des内(会拷入src的‘\0’)
②因为会拷贝入\0,所以打印时显示出类似覆盖的效果,但实际不是覆盖
调试一下就可以知道具体情况
3.自定义实现
char* my_strcpy(char*des, const char*src)//src不需要修改,const修饰更加安全规范
{
assert(des);//断言更加规范,避免传入空指针
assert(src);
char*start = des;
while (*des++ = *src++);//巧妙的在解决赋值的问题下又能满足我们判断的要求
return start;
}
int main()
{
char str1[10] = "abcdefg";
char str2[] = "xxx";
printf("%s\n",my_strcpy(str1,str2));
}
四、strncpy
1.知识点
①功能:解决strcpy长度不受限制的情况,由count指定拷贝的元素个数
②特殊:count大于src长度时,多余的count打印\0
2.自定义函数实现
//my_strncpy
char* my_strncpy(char*str1,const char*str2, size_t count)
{
assert(str1 && str2);//防止输入空指针
char*start = str1;
while (count-- && (*str1++ = *str2++))//右边的括号不可以省,因为=的优先级低于&&
{
;
}
while (count--)//前后置还是要想清楚
{
*str1++ = '\0';
}
return start;
}
int main()
{
char str1[30] = "abcdefg66666666666";
char str2[] = "xyz666";
char*ret=my_strncpy(str1,str2,8);
printf("%s\n",ret);
return 0;
}
五、memcpy
1.知识点
①功能:strcpy和strncpy都依托于\0作为结束标志,仅限于字符类型,较为局限。而memcpy适用与任何类型的数据拷贝,他依赖于我们输入的count(拷贝的字节数,单位字节)作为限制条件
②易错:count表示的是字节数,而不是拷贝的元素数。所以如果处理int类型的数据,4个一组才能拷贝完整的一个元素(考虑数据的储存注意大小端问题,例如1储存为 0x 01 00 00 00 )。
3.自定义实现
若对void*这类写法不熟悉,可参考博主之前的文章,里面有详细描述,本文篇幅较长不再赘述。c学习笔记——自定义qsort函数_罅隙的博客-CSDN博客
若对优先级和结合性搞不清,博主之前做过简单明了的总结,可以参考一下
C经典书籍笔记——C陷阱与缺陷之优先级
由于char*的步长为1Byte,满足我们一个字节一个字节拷贝的需求,所以我们选择强制类型转化为char*类型
//my_memcpy
void*my_memcpy(void*des,const void*src,size_t count)
{
assert(des && src);
char*pc = des;
while (count--)
{
*(char*)des = *(char*)src;
//注意 (char*)des++写法错误,运行后置加加时,此时des已经变回了(void*)类型
++(char*)des;
++(char*)src;//(char*)++src错误,因为单目操作符的结合性自右向左
}
return pc;
}
int main()
{
char str1[] = "abcdef";
char str2[] = "xyz";
char*ret=my_memcpy(str1,str2,2);
printf("%s\n",ret);
return 0;
}
六、memmove
1.知识点
①memmove的出现解决memcpy不能处理源字符串与目标字符串存在重叠的情况
②分析:拷贝过程出现问题的部分在于重叠部分。若从前往后拷贝,src的内容拷贝到des内时,会将位于des内的src数据覆盖造成数据缺失。所以这种情况下我们从src的后往前拷贝
总结:若 src<des,从后往前拷贝;若des<src,从前往后拷贝;若两者没有交集,随意
2、自定义实现
//my_memmove
void*my_memmove(void*des,const void*src,size_t count)
{
assert(des && src);
char*ret = des;
if (des > src)//后->前
{
while (count--)
{
*((char*)des + count) = *((char*)src + count);//不能写成+=,这里des和src的位置不能变
}
}
else//前->后
{
while (count--)
{
*((char*)des) = *((char*)src);
++(char*)des;
++(char*)src;
}
}
return ret;
}
int main()
{
char str1[] = "abcdefghi";
printf("%s\n",my_memmove(str1+2,str1,4));
return 0;
}
七、strcat
1.知识点
①功能:在des后面追加字符串src。des的第一个\0被scr的首元素覆盖,以此往后拷贝。
②特殊:不可以给自己追加
(原因分析:给自己追加时,\0首先被首元素覆盖,该字符串\0丢失,函数无法正常停止,一直往后追加,出现非法访问错误)
2.自定义实现
//my_strcat
char*my_strcat(char*des,const char*src)
{
assert(des && src);
char*start = des;
while (*++des);//这里后置加加会指向\0下一个的元素
while (*des++ = *src++);
return start;
}
int main()
{
char str1[30] = "abcde";//注意str1数组要足够大,不然strcat会强推
char str2[] = "xyz666666";
printf("%s",my_strcat(str1,str2));
return 0;
}
八、strncat
1.知识点
①功能:将src内count个字符追加到des后
②特殊:若count个数大于src,则追加到src的‘\0’就停止追加了,不同于strncpy会补\0。但不管怎么样,最后都会额外加上一个‘\0’
2.自定义实现
//my_strncat
char*my_strcat(char*des,const char*src,int n)
{
assert(des && src);
char*pc = des;
while (*++des);
while (n-- && (*des++ = *src++));
*des = '\0';
return pc;
}
int main()
{
char str1[30] = "abcde";
char str2[] = "xyz666666";
char*ret=my_strcat(str1,str2,3);
printf("%s",ret);
return 0;
}
九、strcmp
1.知识点
①功能:比较两个字符串的大小(一位一位的比较字符大小,直到比出大小。比较字符大小实际比的是ASCLL码的大小。如abcdkkk<abcedaaa,因为abc一一比较都相同,而e>d,所以后者更大,后面也不再继续比较了)
②str1 > str2 返回值大于0
str1 = str2 返回值等于0
str1 <str2 返回值小于0
(特殊:在vs编译器下,大于0用1表示,小于0用-1表示。其他编译器则不一定)
2.自定义实现
//my_strcmp
int my_strcmp(const char*str1,const char*str2)
{
assert(str1 && str2);
while (*str1 && *str1++ == *str2++);//直到找到不同的才有比较的必要并保证找到\0就退出
if (*str1 == *str2)
return 0;
else if (*str1 > *str2)
return 1;
else
return -1;
}
十、strncmp
1.知识点
①功能与strcmp相似,只不过是比较我们指定的前count个字符
2.自定义实现
//my_strncmp
int my_strncmp(const char* str1, const char*str2, size_t count)
{
assert(str1 && str2);
if (!count)//小心开始输入的n为0
{
return 0;
}
while (count-- && *str1++ == *str2++)//找到不同为止
{
;
}
return *str1 - *str2;
}
十一、strstr
1.知识点
①功能:判断一个源字符串是否是另一个目标字符串的子串。找到并返回目标字符串中子串的地址
2.自定义实现
代码的重点在于需要引入cur和str1两个指针,cur就像是一根桩,str1则是从此出发的航船与str2进行匹配。不同于str1的自由,cur每次只能移动一次,否则就会有序列被错过,
//my_strstr
char*my_strstr(const char*p1,const char*p2)
{
assert(p1 && p2);
if (!*p2)//若p2为空的特殊情况
{
return p1;
}
char*cur = (char*)p1;//强制类型转化警告
char*str1 = p1;
char*str2 = p2;
while (*cur)//确认p1已经检查完
{
str1 = cur;
str2 = p2;
while (*str1 && *str2 && !(*str1-*str2))//str1==str2的另一种写法
{
str1++;
str2++;
}
if (!*str2)
{
return cur;
}
cur++;
}
return NULL;//找不到则返回空指针
}
int main()
{
char str1[] = "abcdefgh";
char str2[20] = { 0 };
printf("请输入待查找字符串:");
scanf("%s",&str2);
if (my_strstr(str1, str2) != NULL)
printf("是子串,打印结果为:%s", my_strstr(str1, str2));
else
printf("不是子串");
return 0;
十二、字符串分类函数
一张图总结一下(网图,侵删)
十三、字符转换函数
①int tolower(int c)大写转小写
②int toupper(int c)小写转大写