本文重点为大家讲解一些常见的字符拆函数的使用,以及实现,比如求字符长度函数strlen、字符串拷贝函数strcpy、字符串拼接strcat、字符串比较strcmp等等函数。
目录
一、strlen函数
strlen函数是求字符串长度的函数,头文件为string.h。 下图MSDN查阅结果;
返回值 :
返回值是size_t类型,size_t类型实际上是无符号整型(unsigned int)
参数:
该函数只有一个参数,类型为 const char*
补充 :const char* str 和char* const str的区别
const修饰指针变量时,可以放在星号左边,也可以放在星号右边,这两种写法有什么区别呢?
当const放在星号左边时, 修饰其指针指向的的数据不可变,当const放在星号右边时,其指针的指向不可变,例
int a = 5;
int b = 2;
const int* pi = &a;
此时我们不能够修改指针指向的那块区域的值,但是我们可以修改指针的指向,即
*pi = 2; //这里为错误代码
pi = &b; //这里为正确代码
1、strlen的使用
#include <stdio.h>
#include <string.h>
int main()
{
char* str = "abcde";
int ret = strlen(str);
printf("%d\n", ret);
return 0;
}
2、strlen的模拟实现
strlen的实现原理:
字符串是以\0结束, 我们模拟实现strlen即是统计\0前有多少个字符即可。
//写法1
size_t MyStrlen1(const char* p)
{
assert(p);
int star = p;
while (*p)
p++;
return (size_t)(p - star);
}
//写法2
size_t MyStrlen2(const char* p)
{
assert(p);
size_t n = 0;
while (*p++)
n++;
return n;
}
方法一是通过指针相减获得中间元素个数方法,方法二是通过计数的方法实现。
为什么参数指针用const修饰呢?
因为我们在计算计算字符串长度时,我们并不期望该函数修改我们原字符串,所以我们用const修饰起来。
为什么使用size_t作为返回值?
因为字符串的长度不可能为负数,用size_t更加严谨。
二、 strcpy与strncpy函数
strcpy和strncpy函数是字符串拷贝函数, 他们唯一的区别是strncpy多了一个用于控制拷贝字符串个数的形参,他们都必须引用头文件string.h,下图为MSDN查阅结果。
函数返回值:
函数返回值的类型是一个字符指针类型,我们希望该函数在结束时返回赋值后目标字符串的起始地址,这样做的目的是方便进行链式访问。
函数的参数:
参数一:char* strDestination,该参数为目标字符串的地址;
参数二:const char* strSource,该参数为复制字符串的地址;
参数三(strncpy):size_t count,该参数在strncpy中才有,该参数用于控制复制字符个数。
1、strcpy和strncpy的使用
#include <stdio.h>
#include <string.h>
int main()
{
char* str1 = "hello world";
char str2[20] = { 0 };
//strcpy的使用
strcpy(str2, str1);
printf("%s\n", str2);
//strncpy的使用
char str3[20] = { 0 };
strncpy(str3, str1, 5);
printf("%s\n", str3);
return 0;
}
使用上述函数是需要注意以下几点:
- 确保目标地址空间足够大,可以容纳源字符串;
- 源字符串中必须以\0结束;
- 目标空间必须可变;
2、strcpy和strncpy的模拟实现
//strcpy
char* MyStrcpy(char* dest, const char* src)
{
assert(dest && src);
char* start = dest;
while (*dest++ = *src++)
{
;
}
return start;
}
注意:strcpy会将源字符串中的'\0'拷贝到目标字符串中
char* MyStrncpy(char* dest, const char* src, size_t count)
{
assert(dest && src);
char* start = dest;
//拷贝字符串
while (*src && count)
{
*dest++ = *src++;
count--;
}
//count大于源字符串长度用\0补
while (count--)
*dest++ = '\0';
return start;
}
三、strcmp和strncmp函数
strcmp和strncmp函数是字符串比较函数,该函数会依次比较相对应位置字符的ASCII值,使用前需要引用头文件string.h,以下为MSDN所查阅资料。
函数参数:
函数有两个参数,均为字符指针
函数返回值:函数返回值为整型,具体还分以下三种情况
- 当第一个字符串大于第二个字符串时,返回一个大于0的数;
- 当第一个字符串等于第二个字符串时,返回0;
- 当第一个字符串小于第二个字符串时,返回一个小于0的数;
1、strcmp和strncmp的使用
#include <stdio.h>
#include <string.h>
int main()
{
char* str1 = "abcde";
char* str2 = "abccd";
printf("%d\n", strcmp(str1, str2));
printf("%d\n", strncmp(str1, str2, 3));
return 0;
}
2、strcmp和strncmp的模拟实现
//strcmp
int MyStrcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
//如果相同则比较下一个,直到找到不同的那对
while (*str1 == *str2 && *str1)
{
str1++;
str2++;
}
return *str1 - *str2;
}
//strncmp
int MyStrncmp(const char* str1, const char* str2, size_t count)
{
assert(str1 && str2);
//比较前count个字符
while (*str1 == *str2 && count--)
{
str1++;
str2++;
}
if (count == 0)
return *(str1 - 1) - *(str2 - 1);
else
return *str1 - *str2;
}
四、strcat和strncat函数
strcat和strncat是字符串追加函数,该函数会将第二个字符串的追加到第一个字符串的后面,使用前需要引用头文件string.h,以下为MSDN所查阅资料;
函数参数:
函数有两个参数均为字符指针。
strncat还有一个追加字符串个数的参数count
函数返回值:
函数的返回值为目标字符串的起始地址。
1、strcat和strncat的使用
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "hello ";
char* str2 = "world";
printf("%s\n", strcat(str1, str2));
printf("%s\n", strncat(str1, str2, 3));
return 0;
}
注:strcat不能自己给自己追加,具体为什么不能,可看下面strcat的模拟实现;
2、strcat和strncat的模拟实现
char* MyStrcat(char* dest, const char* src)
{
assert(dest && src);
//1、找到目标空间\0的位置
char* cur = dest;
while (*cur)
{
cur++;
}
//2、拷贝源头中得数据到\0及之后的空间
while (*cur++ = *src++)
{
;
}
return dest;
}
字符串追加的本质是找到目标字符串的\0然后将其覆盖追加,而如果目标字符串和源字符串是同一个字符串时,如果将\0覆盖就会形成死循环。
char* MyStrncat(char* dest, const char* src, size_t count)
{
assert(dest && src);
//1、找到目标空间\0的位置
char* cur = dest;
while (*cur)
{
cur++;
}
//2、拷贝源头中指定的数据到\0及之后的空间
while ((*cur++ = *src++) && count--)
{
;
}
*cur = '\0';
return dest;
}
可以自己给自己追加,因为strncat会在追加后的字符串后加\0。
五、strstr函数
strstr函数是查询是否存在子字符串的函数,使用前需要引用头文件string.h,以下为MSDN中所查阅资料;
函数的返回值:
如果能找到子字符串,则返回母串第一个出现子串的地址,如果不能找到子串,则返回NULL。
函数的参数:
参数一:const char* string
此参数即为母串
参数二:const char* strCharSet
此参数即为子串
1、strstr函数的使用
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
char* str1 = "abbbcdef";
char* str2 = "bbc";
char* str3 = "abc";
printf("%s\n", strstr(str1, str2));
printf("%s\n", strstr(str1, str3));
return 0;
}
2、strstr函数的模拟实现
char* MyStrstr(const char* str1, const char* str2)
{
char* s1 = str1;
char* s2 = str2;
char* cur = str1;
while (*cur)
{
s1 = cur;
while (*s1 == *s2 && *s1 && *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;
}
cur++;
s2 = str2;
}
return NULL;
}
六、strtok函数
strtok函数是字符串切割函数,他能使某一字符串按照特定的方式切割开来,使用前需引用头文件string.h。以下为MSDN中所查阅资料;
函数的返回值:
函数的返回值为每次函数切割出字符串的字符指针;如果该字符串已经没有可以切割的字符串了,则返回NULL。
函数的参数:
参数一:
该参数又被分为两种情况:
(1)被切割函数的指针,首次调用strtok函数时,我们需要传入该字符串的起始地址,当他找到切割字符串标志的字符时,他会将该字符改为'\0',并记住这个位置。
(2)NULL,第二次调用该函数传NULL即可,他会通过上一次记住的位置继续往后寻找且个字符串标志的字符 ,找到以后则改为'\0',并记住该位置。
参数二:
该参数则为指向切割字符串的字符数组的指针;
1、strtok的使用
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "123 456 7789@qq.com";
char buf1[30];
strcpy(buf1, str1);
char* seq = "@. ";
printf("%s\n", strtok(buf1, seq));
printf("%s\n", strtok(NULL, seq));
printf("%s\n", strtok(NULL, seq));
printf("%s\n", strtok(NULL, seq));
printf("%s\n", strtok(NULL, seq));
return 0;
}
当然实际情况下,我们不知道该字符串要切割几次 ,以下写法更巧妙;
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "123 456 7789@qq.com";
char buf1[30];
strcpy(buf1, str1);
char* seq = "@. ";
char* str = NULL;
for (str = strtok(buf1, seq); str != NULL; str = strtok(NULL, seq))
{
printf("%s\n", str);
}
return 0;
}
注:
1、该函数会修改传入的字符串。
2、当传入非空指针作为参数一时,会终止切割原来字符串,而来切割当前字符串。
七、memcpy和memmove函数
memcpy和memmove函数都用于内存拷贝,他们唯一的区别是memmove可以用于重叠内存的拷贝,使用前需使用头文件string.h。以下为MSDN查阅结果;
函数的返回值:
函数的返回值为目标指针的值;
函数的参数:
参数一:
目标的起始地址;
参数二:
拷贝的起始地址;
参数三:
拷贝的字节数;
1、memcpy和memmove的使用
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
int arr3[10] = { 0 };
int* p = memcpy(arr2, arr1, 20);
memmove(arr3, arr1, 12);
return 0;
}
2 、memcpy和memmove的模拟实现
//memcpy
void* MyMemcpy(void* dest, const void* src, size_t count)
{
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
memmove的模拟实现则需要另外考虑dest指针与src指针重叠的情况
void* MyMemmove(void* dest, const void* src, size_t count)
{
void* ret = dest;
if ((char*)dest > (char*)src)
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
else
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
如果dest大于src就从后往前拷贝,如果dest小于src就从前往后拷贝,如果两者相等,则从前往后和从后往前都可 。
八、memcmp函数
比较两个数据的大小,按字节进行依次比较,使用前需要引用头文件string.h,以下为MSDN中所查阅资料;
函数的参数:
参数一、参数二:
需要比较两块内存空间的起始地址;
参数三:
比较的字节数;
函数的返回值:
该函数的返回值于strcmp相同,有如下三种:
1、memcmp函数的使用
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
int arr3[10] = { 0 };
memcpy(arr2, arr1, 20);
memmove(arr1 + 3, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
2、memcmp函数的模拟实现
#include <stdio.h>
#include <string.h>
int MyMemcmp(const void* p1, const void* p2, size_t count)
{
while (*(char*)p1 == *(char*)p2 && count--)
{
p1 = (char*)p1 + 1;
p2 = (char*)p2 + 1;
}
if (count == 0)
return *((char*)p1 - 1) - *((char*)p2 - 1);
else
return *(char*)p1 - *(char*)p2;
}
以上便是一些基本的字符串及内存函数,在模拟实现以上函数可以让你对上述函数有更深刻的认识,这些函数在未来的运用中也非常重要,以上为小编在学习期间所感悟,希望本文能给大家带来帮助。