C语言中对字符和字符串的处理很是繁琐,但是C语言本身是没有字符串类型的字符串通常存放在常量字符串或者字符数组中。 字符串常量适用于那些对它不做修改的字符串函数。
size_t strlen(const char* str);
'\0'
作为结束标志,strlen函数返回的是在字符串中'\0'
前面出现的字符个数(不包含'\0'
)'\0'
结束相减时易错
)//错误写法
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "abc";
char b[] = "abcd";
if (strlen(a) - strlen(b) >= 0)
{
printf("a比b长\n");
}
else
{
printf("b比a长\n");
}
return 0;
}
//打印结果:
//a比b长
/*
这个打印结果显然是不对的,a的长度为3,b的长度为4.
导致结果出错的原因就是函数的返回类型,size_t是无符号的整型,这两相减的值也是无符号类型不可能为负数的,所以导致了结果错误。
*/
//正确写法
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "abc";
char b[] = "abcd";
if ((int)strlen(a) - (int)strlen(b) >= 0)//2.strlen(a)>=strlen(b)
{
printf("a比b长\n");
}
else
{
printf("b比a长\n");
}
return 0;
}
char* strcpy(char* destination,const char* source);
'\0'
拷贝到目标空间。#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "xxxxxxxxxxxx";
char arr2[] = "hello world";
strcpy(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
//打印结果:hello world
char* strcat(char* destination,const char* source);
'\0'
结束。#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strcat(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
//打印结果:hello world
int strcmp(const char* str1,const char* str2);
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "abc";
char arr3[] = "abc";
int ret = strcmp(arr1,arr2);
printf("arr1:arr2 %d\n",ret);
ret = strcmp(arr2,arr1);
printf("arr2:arr1 %d\n",ret);
ret = strcmp(arr2,arr3);
printf("arr2:arr3 %d\n",ret);
return 0;
}
//打印结果
/*
arr1:arr2 1
arr2:arr1 -1
arr2:arr3 0
*/
char* strncpy(char* destination,const char* source,size_t num);
//代码1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "xxxxxxxxxxxx";
char arr2[] = "hello world";
strncpy(arr1,arr2,5);
printf("%s\n",arr1);
return 0;
}
//打印结果:
//helloxxxxxxx
//代码2
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "xxxxxxxxxxxx";
char arr2[] = "he";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
//打印结果:
//he
char* strncat(char* destination,const char* source,size_t num);
//代码1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1,arr2,3);
printf("%s\n",arr1);
return 0;
}
//打印结果:
//hello wor
//代码2
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1,arr2,10);
printf("%s\n",arr1);
return 0;
}
//打印结果:
//hello world
int strncmp(const char* str1,const char* str2,size_t num);
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "abc";
char arr3[] = "abc";
int ret = strncmp(arr1,arr2,3);
printf("arr1:arr2(3) %d\n",ret);
return 0;
}
//打印结果:arr1:arr2(3) 0
char* strstr(const char* str1,const char* str2);
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "This is a simple string";
char* pch;
pch = strstr(str,"simple");
if(pch!=NULL)
printf("%s\n",pch);
else
printf("找不到\n");
pch = strstr(str,"hello");
if(pch!=NULL)
printf("%s\n",pch);
else
printf("找不到\n");
return 0;
}
//打印结果:
/*
simple string
找不到
*/
char* strtok(char* str,const char* sep);
'\0'
结尾,返回一个指针指向这个标记的指针(strtok函数会改变被操作的字符串,所以在适用strtok函数切分的字符串一个都是临时拷贝的内容并且可修改。)NULL
,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。NULL
,将在同一个字符串中被保存的位置开始,查找下一个标记。NULL
指针。#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "legacy.cplusplus.com/reference/cstring/strtok/?kw=strtok";
char sep[] = "./";
char cpy[60];
strcpy(cpy,str);
for(char* ret = strtok(cpy,sep);ret!=NULL;ret = strtok(NULL,sep))
{
printf("%s\n",ret);
}
return 0;
}
//打印结果:
/*
legacy
cplusplus
com
reference
cstring
strtok
?kw=strtok
*/
char* strerror(int errnum);
库函数在执行的时候,发生了错位会将一个错误码存放在errno这个变量中 errno是C语言提供的一个全局变量.
//打印0~10所对应的错误信息
#include <stdio.h>
#include <string.h>
int main()
{
for(int i = 0;i<=10;++i)
{
printf("%d:%s\n",i,strerror(i));
}
return 0;
}
//打印结果
/*
0:No error
1:Operation not permitted
2:No such file or directory
3:No such process
4:Interrupted function call
5:Input/output error
6:No such device or address
7:Arg list too long
8:Exec format error
9:Bad file descriptor
10:No child processes
*/
函数 | 如果它的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格’‘,换页’\f’,回车’\r’,换行’\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十进制数字0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母af,大写字母AF |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母az或者AZ |
isalnum | 字母或者数字 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
函数 | 功能 |
---|---|
tolower | 将大写字母转换为小写字母 |
toupper | 将小写字母转换为大写指针 |
//大写转小写
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "I Am A Student";
for(int i = 0;str[i];++i)
{
str[i] = tolower(str[i]);
}
return 0;
}
//打印结果:
//i am a student
void* memcpy(void* destination,const void* source,size_t num);
'\0'
的时候不会停止。#include <stdio.h>
#include <string.h>
int main()
{
int arr1[20] = {0};
int arr2[] = {1,2,3,4,5,6};
memcpy(arr1,arr2,sizeof(arr2));
for(int i = 0;i<20;++i)
{
printf("%d ",arr1[i]);
}
return 0;
}
//打印结果:
//1 2 3 4 5 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0
void* memmove(void* destination,const void* source,size_t num);
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = {1,2,3,4,5,6,7,8,9,0};
memmove(arr1+2,arr1,20);
for(int i = 0;i<10;++i)
{
printf("%d ",arr1[i]);
}
return 0;
}
//打印结果:
//1 2 1 2 3 4 5 8 9 0
int memcmp(const void* ptr1,const void* ptr2,size_t num);
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = {1,2,3,4,5,6,7,8,9,0};
int arr2[] = {1,2,4,0};
int ret = memcmp(arr1,arr2,12);
printf("%d\n",ret);
return 0;
}
//打印结果:
//-1
/*
解释:根据函数传递的参数得知要比较前12个字节
arr1的前12个字节:01 00 00 00 02 00 00 00 03 00 00 00
arr2的钱12个字节:01 00 00 00 02 00 00 00 04 00 00 00
在第9个字节时就已经分出大小了
04大于03所以返回-1.
*/
计算字符串长度,要求:遇到'\0'
就停止,不能将'\0'
记录进长度
有三种方法。
方法1:
int my_strlen(const char* str)
{
int count = 0;
while(*str)
{
count+=1;
str+=1;
}
return count;
}
方法2(利用指针减指针的运算)
int my_strlen(const char* str)
{
char* p = str;
while(*str)
{
str+=1;
}
return str-p;
}
方法3 利用递归实现 问题拆分:在求一个字符串长度时,以"hello"为例。我们求它的长度时,因为‘h’的长度肯定是1,所以我们是不是可以转化成求1+“ello”的长度。依次类推还可以写成1+1+“llo”的长度… 最后求到’\0’时,因为’\0’不能作为字符串长度所以+0。依照这个思路写成的代码就是这样。
int my_strlen(const char* str)
{
if(str=='\0')
return 0;
return 1+my_strlen(str+1);
}
实现字符串的拷贝
char* my_strcpy(char* dest,char* src)
{
char* ret = dest;//保存首元素地址方便后续返回复制后的字符串
assert(dest&&src);//预防传递空指针
while(*dest++=*src++)
{
;
}
return ret;
}
功能:追加字符
char* my_strcat(char* dest,const char* src)
{
assert(dest&&src);//防止空指针。
char* ret = dest;//保存起始位置
//因为是在目标函数的最后追加字符串,所以我们要先找到目标函数的'\0'的位置
while(*dest)
{
dest+=1;
}
//找到后开始追加字符,类似于字符拷贝咯
while(*dest++=*src++)
{
;
}
return ret;
}
功能:找子串
char* my_strstr(const char* dest,const char* src)
{
if(!*src)
return dest;//如果src是空的,就直接返回dest就可以了
//利用3指针,定义3个指针,两个指向dest,一个指向src
//指向dest的指针其中一个为保存返回位置的起始地点的指针
//另一个于指向src的指针进行比较。
char* ret = dest;
char* p1 = dest;
char* p2 = src;
while(*ret)
{
p1 = ret;//每次从ret位置向后于p2匹配
p2 = src;//每次没找到就要从头开始于p1匹配
while(*p1!='\0'&&*p2!='\0'&&*p1==*p2)//p1与p2相等才能向后走
//除此之外还要判断是否走到字符串的末尾
{
p1+=1;
p2+=1;
}
//当不满足条件出循环时,如果*p2 == '\0'就说明src字符串已经被全部找到,可以返回了
if(*p2=='\0')
{
return ret;
}
ret+=1;
}
//走到这里就说明前面都没有找到,只能返回NULL了
return NULL;
}
int my_strcmp(char* ptr1,char* ptr2)
{
while(*ptr1&&*ptr2&&*ptr1==*ptr2)
{
ptr1+=1;
ptr2+=1;
}
//出循环就说明不相等了或者有字符串走完了
if(*ptr1-*ptr2>0)
return 1;
else if(*ptr1-*ptr2 < 0)
return -1;
else
return 0;
}
提问:为什么使用void*接收呢? 回答:Void指针 是无具体类型的指针。Void 类型的指针可以接任意类型的地址(这种类型的指针是不能直接解引用操作的,也不能直接进行指针运算的)。 所以用void*接收是没问题的。然后,这个函数不仅可以拷贝整型数组排序,还可以对字符数组,浮点型数组,甚至是结构体数组。这也就造成了不能使用特定类型指针来接收的情况,如果使用了特定的类型,那其它类型就不能被接收了,所以才会选择使用void*来接收。
void* my_memcpy(void* dest,const void* src,size_t num)
{
char* p1 = (char*)dest;
char* p2 = (char*)src;
for(int i = 0;i<num;++i)
{
*p1 = *p2;
p1+=1;
p2+=1;
}
return dest;
}
C语言规定:memcpy只需要实现不重叠的拷贝就可以了 但是vs上memcpy函数实现了重叠拷贝,所以你在vs上让memcpy处理重叠的拷贝也是没问题的,只是不能保证所以的编译器都会这么设计。
memmove和memcpy的功能其实是类似的。 memcpy不会处理重叠数据的, memmove才会处理重叠数据. src在dest前
如图,将src后的20字节(5个元素)拷贝到dest中,正常直接拷贝的话,会出现覆盖的情况如下图:
元素3和4就被覆盖了,程序继续进行下去,也只会在把1、2往后面拷贝,这可不是我们想要的结果。为什么不让没被使用的数据被覆盖,我们要怎么办呢? 其实很简单,我们从后往前拷贝。
这样从后往前拷贝就可以完美避免未使用的值在使用前被覆盖。
src在dest后
如果是这种情况,观察可以发现从前往后拷贝不会影响结果。反而如果是从后往前负值才会影响结果。
确定范围 如果dest大于等于src就从后向前覆盖 其他情况都从前向后覆盖。
void* my_memmove(void* dest, const void* src, size_t num)
{
char* p1 = (char*)dest;
char* p2 = (char*)src;
if (dest >= src)
{
//从后往前覆盖
p1 += num-1;//减1的目的是为了让指针停在一个类型的最后一个字节上
p2 += num-1;
for (int i = 0; i < num; ++i)
{
*p1 = *p2;
p1 -= 1;
p2 -= 1;
}
}
else
{
//从前往后覆盖
for (int i = 0; i < num; ++i)
{
*p1 = *p2;
p1 += 1;
p2 += 1;
}
}
return dest;
}
完