strlen
首先第一位男嘉宾是我们的strlen函数
首先看一下函数原型:>
strlen函数的参数是要求的字符串的地址,返回值为size_t类型,此处要注意以下几个点:>
- strlen函数的返回值为size_t类型,即无符号整型,会出现以下这种情况:>
if语句里的strlen(arr1) - strlen(arr2),即 3 - 4 = -1, 然而这是一个无符号整型 - 无符号整型的表达式,得到的仍然是无符号整型,-1将会被当作是一个很大的正数,因此这样的话答案就是>了
2.strlen所求的字符串中必须有\0, strlen是通过读取到\0来停止读取,因此若字符串中没有结束标志,其得到的结果只能为随机值
运行结果为:
接下来是strlen的模拟实现,有三种方式,这里我所写的是指针-指针的方式
int my_strlen(const char* start)
{
assert(start);
const char* end = start;
while (*end != '\0')
{
end++;
}
return end - start;
}
strcpy
第二位出场的男嘉宾是我们的strcpy函数,其功能是将一个字符串拷贝到另一个字符串,即是这样:
将arr2拷贝放到arr1中,这样arr1数组存放的也是arr2中的内容,打印出来的结果为:>
其次要注意的几个点是
1.拷贝的源字符串中必须有\0, strcpy同样是以\0作为拷贝的结束标志。
2.拷贝的时候,也会将\0拷贝过去。
下面通过调试来展示一下
现在,我们将第一个数组的字符串内容改变,即
char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
char arr2[] = "hello, world!";
strcpy(arr1, arr2);
printf("%s\n", arr1);
在经过strcpy后
可以看出,\0被拷贝了过去。
接下来是strcpy的模拟实现:
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{
;
}
return ret;
}
*dest++ = *src++这句代码既解决了拷贝问题,同时也将’\0’拷贝了过去。
strcmp
strcmp是我们常用的字符串比较函数,用来比较两字符串之间的大小关系
例如:>
int main()
{
char arr1[] = "LLLLLL";
char arr2[] = "LLLYYY";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
strcmp将每个字符依次比较,在此处, arr1中的L先于arr2中的L比较,一直比较到arr1中的第四个L与arr2中的第一个Y, 此时发现字符L的ascii码值小于字符’Y’,返回一个小于0的数,结果为:>
若我们将代码修改一下,若将arr2中的内容修改为:>
int main()
{
char* arr1 = "LLLLLL";
char* arr2 = "LLLLLL";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
则得到的结果为:>
接下来是strcmp的模拟实现:>
int my_strcmp(const char* s1, const char* s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0')
{
return 0;
}
s1++;
s2++;
}
return *s1 - *s2;
}
此处的return *s1 - *s2也可以修改为以下代码:>
if(*s1 > *s2)
{
return 1;
}
else
{
return -1;
}
strcat
接下来是我们的字符串追加函数,首先,其功能为在字符串后面追加字符串,以一串代码来演示以下:>
int main()
{
char arr1[20] = "LLLLLL";
char arr2[] = "YYYYYY";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
得到的结果为:>
要注意的点是:>
- 被追加的字符串函数中必须有足够大的空间,不然会导致非法访问内存;
- 追加的字符串同时会把’\0’复制过去。
然后就是strcat的模拟实现啦
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "LLLLLL";
char arr2[] = "YYYYYY";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
接下来的三组函数与上面三组类似,分别是strncpy, strncmp, strncat。
首先是我们的strncpy同学,与strcpy相比多了一个参数
此处的第三个参数指明的是要复制多少个字符过去。
接下来是其功能展示:
int main()
{
char arr1[20] = "LLLLLLL";
char arr2[] = "YYYYYYYY";
strncpy(arr1, arr2, 3);
printf("%s\n", arr1);
return 0;
}
得到的结果为:>
我们修改一下代码,改为以下代码:>
int main()
{
char arr1[] = "xxxxxxx";
char arr2[] = "YYY";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
此处第三个参数‘5’已经超过了arr2中字符串的长度,arr1中得到的结果为:>
可以看到,strncpy不仅把arr2中的内容拷贝过去,又在其后面补了个’\0’以此到达第三个参数所要的复制五个字符。
接下来是我自己对strncpy的模拟实现,欢迎各位大佬提出意见!
char* my_strncpy(char* dest, const char* src, size_t count)
{
char* ret = dest;
while (count && (*dest++ = *src++)) // 这里表达式(*dest++ = *src++)必须放在&&的后面,当count为0时
{ //表达式(*dest++ = *src++)才不计算,防止多算一次
count--;
}
while (count)
{
*dest++ = '\0';
count--;
}
return ret;
}
接下来是我们的strncat,与strcat相似,但同样是多了一个参数
第三个参数同样是指明在字符串后面要多追加几个数。接下来我们看其功能
int main()
{
char arr1[20] = "xxxxxxx";
char arr2[] = "YYYYYYY";
strncat(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
得到的结果为:>
可以看出,strncat不仅追加了五个字符,同时还在其末尾追加了’\0’,如果arr2中的字符串长度小于第三个参数所指定的长度呢,请看以下代码:>
int main()
{
char arr1[20] = "xx\0xxxxxxxxx";
char arr2[] = "YYY";
strncat(arr1, arr2, 5);
printf("%s\n", arr1);
return 0;
}
我们以调试的方式来看一下在执行这几行代码之后所得到的结果:>
我们发现,在把arr2中的所有字符追加过去后,即使arr2的字符串长度小于strncat第三个参数所指明的参数,仍然只追加了arr2中的内容,并不会像strncpy那样多放几个’\0’过去。
接下来是自己的strncpy的模拟实现,欢迎各位大佬提出意见!
char* my_strncat(char* s1, const char* s2, size_t count)
{
char* tmp = s1;
size_t len = strlen(s2);
while (*s1)
{
s1++;
}
if (count < len)
{
while (count && (*s1++ = *s2++))
{
count--;
}
*s1 = '\0';
}
else
{
while (len && (*s1++ = *s2++))
{
len--;
}
*s1 = '\0';
}
return tmp;
}
这三组函数中最后一组函数便是我们的strncmp函数了,首先同理,既然名字中多了一个n,那么他也同样多了一个参数。
首先我们来看其功能:>
这里的第三个参数是为了指明要对比两个字符串中的前多少个字符,这里传参传的是4,因此对比arr1和arr2中的前四个字符,得到的结果为:>
若将此处的第三个参数改为5的话,得到的结果是:>
因为arr1的第五个字符’L’与arr2中的第五个字符’Y’并不相等,因此返回小于0的数。
接下来是strncmp自己的模拟实现,欢迎各位大佬提出意见:>
int my_strcmp(const char* s1, const char* s2, size_t count)
{
assert(s1 && s2);
int i = 0;
for (i = 0; i < count; i++)
{
if (*s1 == *s2)
{
s1++;
s2++;
}
else
{
return *s1 - *s2;
}
}
return 0;
}
接下来出场的是:
strstr
即字符串查找函数,找一个字符串是否为另一个字符串的子串。若找到,则返回找到该字串的地址,若找不到,则返回空指针。请看以下范例:>
int main()
{
char arr1[] = "abbbcdef";
char arr2[] = "bbc";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
return 0;
}
else
{
printf("%s\n", ret);
}
return 0;
}
得到的结果为:>
接下来是strstr的模拟实现(kmp算法我还没了解。。。。)
char* my_strstr(const char* str, const char* substr)
{
assert(str && substr);
if (*substr == '\0')
{
return (char*)str;
}
const char* s1 = str;
const char* s2 = substr;
const char* cur = str;
while (*cur)
{
s1 = cur;
s2 = substr;
while (*s1 && *s2 && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cur;
}
cur++;
}
return NULL;
}
接下来是4组内存函数,分别是
memcpy;
memmove;
memset;
memcmp;
首先登场的是memcpy,我们来看其参数
第一个参数是待拷贝的字符串,第二个参数是拷贝的字符串,第三个是拷贝多少个字节,请看以下范例:>
int main()
{
char arr1[] = "abcdef";
char arr2[10] = { 0 };
memcpy(arr2, arr1, 5);
printf("%s\n", arr2);
return 0;
}
此处代码便是将arr1前五个字节的内容拷贝到arr2中。得到的结果为:>
此外,memcpy函数还可以操作其他类型的数组
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 5 * sizeof(arr1[0]));
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
此处代码便是将arr1中的前20个字节的元素拷贝到arr2中,得到的结果为:>
接下来便是memcpy的模拟实现啦:>
void* my_memcpy(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函数,不过memmove函数与memcpy函数非常相似,但memmove函数可以处理重叠问题。(其实vs的memcpy也可以),请看一下代码:>
void* my_memcpy(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;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr1 + 2, arr1, 4 * sizeof(arr1[0]));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
此处我们是想将 1 2 3 4 拷贝到3 4 5 6 本想得到的结果为:1 2 1 2 3 4 7 8 9 10,然而我们得到的结果为:>
此处memmove就可以很好的解决问题,我们修改一下代码:>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr1 + 2, arr1, 4 * sizeof(arr1[0]));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
得到的结果为:>
memmove便可以解决这些重叠的问题,接下来我们重点观看一下memmove的模拟实现:>
首先请看这幅图,如果将1,2,3,4拷贝到3,4,5,6会覆盖3,4的话,我们可以尝试着倒过来拷贝,即
将4拷贝到6,将3拷贝到5,将2拷贝到4,将1拷贝到3,这样就得到我们想要的结果了。
再来看一种情况
这种情况下正着拷贝就可以了,即将3拷贝到1,4拷贝到2,5拷贝到3,6拷贝到4.
最后一种情况:>
两者都可以为src或者dest,这样的话,倒着打印或者正着打印就可以了,因此,我们可以分为两种情况
有这些知识之后,接下来是memmove的模拟实现:>
void* my_memmove(void* dest, const void* src, size_t count)
{
void* tmp = dest;
assert(dest && src);
if (src > dest)
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return tmp;
}
memcmp
接下来是内存比较函数memcmp,即比较每个字节的内容是否相等,与strcmp挺相似的
请看以下代码
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,6,7,8,9,10,11 };
int ret = memcmp(arr1, arr2, 16);
printf("%d\n", ret);
return 0;
}
运行结果为:>
将16改为17的话,得到的结果为:>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[] = { 1,2,3,4,6,7,8,9,10,11 };
int ret = memcmp(arr1, arr2, 17);
printf("%d\n", ret);
return 0;
}
此处返回-1是因为arr1和arr2中第17个字节的内容是不同的,且arr1中的内容小于arr2.
memset
最后出场的是我们的memset函数,即内存设置函数,我们来看其演示效果:>
int main()
{
char arr1[] = "LLLLLLLL";
memset(arr1, 'x', 5);
printf("%s\n", arr1);
return 0;
}
memset即将arr1中前5个字节(在此处为5个字节,由第三个参数决定)设置为’x’字符x,得到的结果为:>
本文完!