目录
strlen函数
strlen模拟
strcpy函数
strcpy模拟
strcat函数
strcat模拟
strcat函数能否自我追加
strcmp函数
模拟实现strcmp函数
strncpy函数
strncat函数
strnmcp函数
strstr函数(查找子串函数)
strstr函数模拟实现
编辑 strtok函数
sterror函数
字符分类函数
memcpy函数
模拟实现memcpy函数
memmove函数
模拟实现memmove
memcmp函数
memset函数
size_t strlen ( const char * str );
返回类型为size_t,参数类型为char *
注意事项:1.字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包 含 '\0' )。2.参数指向的字符串必须要以 '\0' 结束。3.注意函数的返回值为size_t,是无符号的( 易错 )
int main()
{
char a[] = "123456";
printf("%d",strlen(a));
return 0;
}
由于返回值是无符号整形,所以这里str2>str1,俩个无符号整形相加减结果还是无符号整形
#include
#include
size_t my_strlen(const char* arr)
{
assert(arr != NULL);
size_t a = 0;
while (*arr++ != '\0')
{
a++;
}
return a;
}
int main()
{
char arr[] = "abcde";
size_t n = my_strlen(arr);
printf("%d", n);
return 0;
}
char* strcpy(char * destination, const char * source);
用源里面的内容覆盖目标里面的内容 ,返回类型为char *,参数类型为char *,返回值为目标字符串(目标空间的起始地址)
注意事项:
源字符串必须以 '\0' 结束。会将源字符串中的 '\0' 拷贝到目标空间。目标空间必须足够大,以确保能存放源字符串。目标空间必须可变
该函数在遇到源内容里面的\0时,会停止获取\0后面的字符,并将\0和\0之前的内容拷贝到目标地址里(这个是具有覆盖功能的)
这种情况下调试会报错,因为字符串常量不能被修改
#include
#include
char* my_strcpy(char* des,const char* sour)
{
assert(des&&sour);
char* tmp = des;
while (*des++ = *sour++)
{
;
}
return tmp;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = {0};
my_strcpy(arr2,arr1);
printf("%s", arr2);
return 0;
}
char * strcat ( char * destination, const char * source );
返回值为目标空间的起始地址,返回类型为char *,参数类型为char * ,该函数为字符串追加函数
注意事项:源字符串必须以 '\0' 结束。目标空间必须有足够的大,能容纳下源字符串的内容。目标空间必须可修改。
这个在追加时,遇到源字符串里的\0,会将\0和\0之前的字符追加到目标字符串里
#include
#include
char* my_strcat(char *dest,const char *sorc)
{
assert(dest && sorc);
char* tmp = dest;
while (*dest != '\0')
{
dest++;
}//找到目标字符串里的\0
while (*dest++ = *sorc++)
{
;
}
return tmp;
}
int main()
{
char arr1[20] = "hello ";
my_strcat(arr1,"world");
printf("%s", arr1);
return 0;
}
答案是不行,原因如下:
int strcmp ( const char * str1, const char * str2 );
返回类型为int,参数为char *
str1
str1=str2,返回0
str1>str2,返回大于0的数
这个函数是如何进行比较的?
a和a比较,ASCII码值相等,然后比较下一个字符
b和b比较,ASCII码值相等,然后比较下一个字符
c和c比较,ASCII码值相等,然后比较下一个字符
d和\0比较,d的ASCII码值大于\0的ASCII码值,然后返回一个>0的数字
注意:strcmp是从放第一个字符开始比较ASCII码值,若不相等,则会结束,strcmp不是比较字符串长度的
#include
#include
int my_strcmp(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);
while (*arr1!='\0'&& * arr1 == *arr2)
{
arr1++;
arr2++;
}
return (*arr1 - *arr2);
}
int main()
{
char arr1[] = "abc";
char arr2[] = "abcdefg";
int c=my_strcmp(arr1, arr2);
if (c > 0)
printf(">");
else if (c == 0)
printf("=");
else
printf("<");
return 0;
}
char * strncpy ( char * destination, const char * source, size_t num );
跟strcpy一样,只不过多了一个参数,返回值类型为 char *,返回值为目标起始地址
,这个参数是用来限制拷贝字节数的。
拷贝过程中,若遇到\0,则会跟\0一起拷贝过去
如果拷贝的时候,源字符串个数不够,则会补充\0
char * strncat ( char * destination, const char * source, size_t num );
返回值类型为char *,返回值为目标首地址,与strcat相比,多了一个参数,这个参数是用来限制被追加的字节数的
通过这个,我们可以看到在追加结束时,函数会自动补一个\0
int strncmp ( const char * str1, const char * str2, size_t num );
返回值类型为int,多了一个参数,这个参数是用来限制str2的字节个数的,这个函数也是比较ASCII码值,而不是比较字符串长度
char * strstr ( const char *str1, const char * str2);
返回值为类型为char *,参数类型为char *
这是查找str1里面有没有出现str2这个字符串,str2作为字串,如果在str1里面找到了子串,则返回子串在母串里的第一个字符的地址,如果没有找到子串,则返回空指针NULL
这里我们可以看到,返回的是子串在母串中的起始位置
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* s1 = str1;
char* s2 = str2;
char* p = str1;
while (*p)
{
s1 = p;
s2 = str2;
while (*s1 == *s2&&*s1!='\0'&&*s2!='\0')
{
s1++;
s2++;
}
if (*s2 == '\0')
return p;
p++;
}
return NULL;
}
int main()
{
char str1[] = "132456879";
char str2[] = "56";
char *ret=my_strstr(str1, str2);
printf("%s", ret);
return 0;
}
char * strtok ( char * str, const char * sep );(字符串切割函数)
sep 参数是个字符串,定义了用作分隔符的字符集合, sep中放分隔符第一个参数指定一个字符串,它包含了 0 个或者多个由 sep 字符串中一个或者多个分隔符分割的标记。 第一个参数是待切割的字符串,里面可有分隔符,也可以没有strtok 函数找到 str 中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。 找到分割符后,将分隔符变为\0,并且返回a这个位置(注: strtok函数会改变被操作的字符串,所以在使用strtok 函数切分的字符串一般都是临时拷贝的内容 并且可修改。) 使用strtok进行切割时,最好临时拷贝一份,不要破坏原数据strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记, strtok 函数将保存它在字符串 中的位置。 当地一个参数不为NULL时,找到第一个标记(上面标记为@),并把它变为\0,同时它会保存这个分隔符的位置,然后返回a的地址strtok 函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 当记住上面的位置之后,下一次寻找分隔符,将会从上面所记住的位置开始找,也就是说传参的时候在找到第一个分隔符之前传参要穿地址,找到第一个分隔符之后传参就要传空指针如果字符串中不存在更多的标记,则返回 NULL 指针, 没有标记时就返回空指针
#include
#include
int main()
{
char a[] = "123@456#789";
char b[] = "@#";
char* ret = strtok(a, b);
printf("%s\n", ret);
ret = strtok(NULL, b);
printf("%s\n", ret);
ret = strtok(NULL, b);//第三次找到末尾\0,也会停下来
printf("%s\n", ret);
return 0;
}
当俩个分隔符挨着的时候,程序获取不到任何东西,它会直接跳过第二个分隔符
char * strerror ( int errnum );
errnum 错误码,
C语言中程序执行失败的时候,都会返回一个错误码,sterror是来查看错误码的具体含义的
#include
#include #include int main() { //errno:全局错误码存放的全局变量 FILE *pf=fopen("test.txt", "r"); if (pf == NULL) { printf("%s", strerror(errno)); return 1; } return 0; }
函数 | 如果它的参数符合下列条件就返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车'\r',制表符'\t',或垂直制表符'\v' |
isdigit | 十进制数字0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或数字a~z,A~Z或0~9 |
ispunct | 标点符号,任何不属于数字或字母的图像字符(可打印符号) |
isgraph | 任何图像字符 |
isprint | 任何可打印字符,包括图像字符和空白字符 |
void * memcpy ( void * destination, const void * source, size_t num );
内存拷贝函数,将源内容拷贝到目标内容中区,并可以设置所拷贝的字节大小,参数为void *,返回目标的起始地址 ,是为了使这个函数具有通用性,void *不能直接解引用,也不能直接进行指针相减
void* my_memcpy(void* a,const void* b, size_t s) { assert(a && b); void* ret = a; while (s--) { *(char*)a = *(char*)b; a=(char*)a+1; b=(char*)b+1; } return ret; } int main() { int arr1[] = { 1,23,54,165,56,1,51,65 }; int arr2[] = { 165,195 }; my_memcpy(arr1, arr2, 8); for (int i = 0; i < 8; i++) printf("%d ", arr1[i]); return 0; }
这里最好采用a=(char*)a+1,这个写法比较通用,当写成(char *)a++之后,这个在有些编译器上跑不过去
memcpy无法实现通过自我拷贝,改变自身的值,因为:这是arr1,将它的值传给arr1+2,arr1每传一个数,原数据就会被改变一次,这里arr1走了四次,原数据就被改变了四次,然后接着赋值
我们用memcpy函数却能实现,自我拷贝,这里不是我们的模拟函数有问题,而是memcpy函数不用来处理重叠的内存数据处理,应该使用memmove,只不过这里的memcpy能力比较强,完成了memmove应该做的事情
void * memmove ( void * destination, const void * source, size_t num );
跟memcpy参数一样,返回值为目标首地址
#include
#include
int main()
{
int arr1[] = { 1,23,54,165,56,1,51,65 };
int arr2[] = { 165,195 };
memmove(arr1 + 2, arr1, 12);
for (int i = 0; i < 8; i++)
printf("%d ", arr1[i]);
return 0;
}
当dest
当两块位置没什么关联的时候,从4-8拷贝或者从8-4拷贝都可以
当dest>src时,应该从8-4进行拷贝,即从后向前拷贝
#include
#include #include void* my_memmove(void* des, const void* src, size_t num) { assert(des && src); char* ret = des; if (des < src) { while (num--) { *(char*)des = *(char*)src; des = (char*)des + 1; src = (char*)src + 1; } } else { while (num--) { *((char*)des + num) = *((char*)src + num); } } return ret; } int main() { int arr1[] = { 1,23,54,165,56,1,51,65 }; int arr2[] = { 165,195 }; my_memmove(arr1+2, arr1, 12); for (int i = 0; i < 8; i++) printf("%d ", arr1[i]); return 0; }
int memcmp ( const void * ptr1,const void * ptr2,size_t num );
比较方法跟strcmp一样。只不过这里是一个字节一个字节进行比较
void *memset( void *dest, int c, size_t count );
返回值是目标首地址 ,给目标地址设置值,值的具体内容在参数里是int c,size_t是要设置的字节数,中间的int参数可以是字符也可以是整数,因为都是以ASCII码形式存储的
这个函数是以字节为单位的,这里将每个字节都改为了1