strlen函数是用于求字符串长度的(不包括\0),例如字符串“abc”的长度为3,在代码中我们如何使用strlen函数呢?
#include <stdio.h>
int main()
{
const char*str1 = "abcdef";
const char*str2 = "bbb";
if(strlen(str2)-strlen(str1)>0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0; }
程序结果如图:
我们的程序的结果可能会感到吃惊,字符串str2的长度为3,字符串str1的长度为6,那为什么最后打印的却是str2>str1呢?
经过查阅资料发现,strlen函数返回值的类型为size_t(无符号整型),因此无符号整型之间的运算结果仍然是无符号整型。3-6的确等于-3,而最终打印的是-3的补码,而-3是二进制原码得到的。-3的原码为10000000000000000000000000000011而-3的补码为01111111111111111111111111111101,是个正数,因此strlen(str2)-strlen(str1)的结果是大于0的。
strlen的特点及注意事项:
方法一:计数器法
#include <stdio.h>
#include <string.h>
int my_strlen(const char* str)
{
int count = 0;
while (*str++!='\0')
{
count++;
}
return count;
}
int main()
{
char* str1 = "abcdef";
int ret = my_strlen(str1);
printf("%d", ret);
return 0;
}
代码运行结果:(以下的运行结果跟下图一致)
方法二:递归方法
#include <stdio.h>
#include <string.h>
int my_strlen(const char* str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
int main()
{
char* str = "abcdef";
int ret = my_strlen(str);
printf("%d", ret);
return 0;
}
方法三:指针减指针的方式
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
int main()
{
char* str = "abcdef";
int ret = my_strlen(str);
printf("%d", ret);
return 0;
}
先看strcpy函数的返回值类型与传值。
查阅资料我们可以看到,strcpy函数传入的第一个参数为目标地址处,第二个参数为源地址处,返回值类型为char*类型。
例如以下代码:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcdef";
char str2[] = "zjr";
strcpy(str1, str2);
printf("%s", str1);
return 0;
}
代码执行结果如图:
strpy函数的特点及注意事项:
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))
{
;
}
return ret;
}
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcdef";
char str2[] = "zjr";
my_strcpy(str1, str2);
printf("%s", str1);
return 0;
}
在my_strcpy函数的形参中的源字符串地址使用了const的原因是:保证修改的是目的地字符串的内容而不是源字符串的内容。my_strcpy函数内部使用了assert函数的目的是保证传入的目的地字符串地址与源字符串地址都不为空指针,如果为空指针则程序代码执行时会报错。
如上代码运行结果为:
简单介绍下assert函数:
头文件为assert.h
传入的为空指针就会报错,否则不影响程序执行。
strcat函数的返回值类型为char*,可以返回被追加的字符串的起始地址。而第一个参数为目的地字符串的地址,第二个参数为源字符串的地址。
利用strcat函数所需的头文件:
strcat函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcd";
char str2[] = "efgh";
char* ret = strcat(str1, str2);
printf("%s", ret);
return 0;
}
代码运行结果为:
strcat函数的特点及注意事项:
char *my_strcat(char *dest, const char*src)//保证源字符串不被修改
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;//先到目的地字符串的\0地址处
}
while((*dest++ = *src++))//先解引用后++,因此最后能将源字符串内的\0也追加进去
{
;
}
return ret;
}
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcd";
char str2[] = "efgh";
char* ret = my_strcat(str1, str2);
printf("%s", ret);
return 0;
}
代码执行结果:
strcmp函数的返回值类型为int,第一个参数为一个字符串首字母的地址,第二个参数为另一个字符串首字母的地址,依次往后作比较,这strcmp函数比较的是字符串的内容,而不是字符串的长度。
对于strcmp函数的返回值,C语言规定如果第一个字符串的内容小于第二个字符串的内容,则返回小于0的值;如果第一个字符串的内容等于第二个字符串的内容,则返回0,如果第一个字符串的内容大于第二个字符串的内容,则返回大于0的值。
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcd";
char str2[] = "abc";
int ret = strcmp(str1, str2);
printf("%d", ret);
return 0;
}
代码运行结果:(结果虽然返回大于0的值都为1,但是每个编译器此处规定返回大于0的值有所不同,此处会有差异)。
假如以上例子对strcmp函数是比较字符串内容不够清晰,以下例子能更好地观察:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcd";
char str2[] = "abq";
int ret = strcmp(str1, str2);
printf("%d", ret);
return 0;
}
代码运行结果:
#include <stdio.h>
#include <assert.h>
my_strcmp(const char* s1, const char* s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1 - *s2;//比较字符串内容
}
#include <string.h>
int main()
{
char str1[] = "abcd";
char str2[] = "abq";
int ret = my_strcmp(str1, str2);
printf("%d", ret);
return 0;
}
代码运行结果:
strcpy、strcat、strcmp
都是长度不受限制的字符串函数,所以就显得不够安全。所以就有了strncpy、strncat、strncmp
函数。
strncpy函数的返回值类型为char*类型,该函数第一个参数为目的地字符串的首字母地址,第二个参数为源字符串的首字母地址,第三个参数为需要拷贝过去的字符个数。
使用strncpy函数所需的头文件:
strncpy的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcd";
char str2[] = "kpl";
char* ret = strncpy(str1, str2, 2);
printf("%s", ret);
return 0;
}
代码运行结果:
#include <stdio.h>
#include <string.h>
char* my_strncpy(char* dest, char* src, size_t count)
{
while (count--)//先使用count再减1
{
*(dest + count) = *(src + count);
}
return dest;
}
int main()
{
char str1[20] = "abcd";
char str2[] = "kpl";
char* ret = my_strncpy(str1, str2, 2);
printf("%s", ret);
return 0;
}
strncat函数与其他的受限制的库函数的返回值类型与参数几乎一样,此处不再进行说明。
使用strncat函数所需头文件:
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strncat(char* dest, char* src, size_t count)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
dest++;//到目的地字符串的\0位置
}
int s =count ;
while (count--)
{
*(dest + count) = *(src + count);//字符串追加
}
*(dest+s) = '\0';
return ret;
}
int main()
{
char str1[20] = "abcd";
char str2[] = "kpl";
char* ret = my_strncat(str1, str2, 2);
printf("%s", ret);
return 0;
}
strncmp函数的返回值类型为int型,返回值的规则跟strcmp函数相同,此处不再细讲。第一个与第二个参数:因为两个字符串只是用来作比较,如果修改则不满足strncmp函数的使用规则,因此在前面两个参数前面加上const保证字符串内容不被修改。第三个参数为比较的字符个数。
使用strncmp函数所需的头文件:
strncmp函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcd";
char str2[] = "abkl";
int ret = strncmp(str1, str2, 2);
printf("%d", ret);
return 0;
}
代码运行结果:
#include <stdio.h>
#include <assert.h>
#include <string.h>
int my_strncmp(char* s1, char* s2, size_t count)
{
assert(s1 && s2);
while (count--)
{
if (*s1 == *s2)
{
s1++;
s2++;
}
}
return *s1 - *s2;
}
int main()
{
char str1[] = "abcd";
char str2[] = "abkl";
int ret = my_strncmp(str1, str2, 2);
printf("%d", ret);
return 0;
}
代码执行结果:
strstr函数的返回值类型为char类型,第一个参数是被查找的字符串,第二个参数是在第一个参数中需要查找的字符串。例如:第一个字符串为i am a student,第二个字符串为a,则返回的char类型用字符串形式打印的结果为am a student 。
使用strstr函数所需的头文件:
strstr函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcdefdgh";
char str2[] = "d";
char* ret = strstr(str1, str2);
printf("%s", ret);
return 0;
}
代码执行结果:
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* str1, const char* str2)
{
char* s1, * s2;
char* cp = str1;
if (*str2 == '\0')
{
return str1;
}
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)//开始遍历寻找
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cp;
}
cp++;//当字符查找途中发现有不相同的字符,则从刚开始查找处的下一个字符开始查找
}
return NULL;
}
int main()
{
char str1[] = "abcdefdgh";
char str2[] = "d";
char* ret = my_strstr(str1, str2);
printf("%s", ret);
return 0;
}
代码执行结果:
查找字符串有两种情况:
第一种:例如在abcd字符串中查找cd,则两个字符串一 一对应一次即可找到。
第二种:需要遍历几次才可能找到。
当第一次遍历时查找到字符c时才发现被查找的字符串中没有bbc的字符串,但实际上被查找的字符串中是有的,只需要再多遍历一次即可。并且从下一个字符处开始寻找。
strtok函数的返回值类型为char*类型,即返回字符串的地址。第一个是用来存放待分割的字符串地址,第二个是用来存放分割符的集合地址。
使用strtok函数的头文件:
strtok函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "by@bite.cn";
char str2[200] = { 0 };
char sep[] = "@.";
strcpy(str2, str1);
char* ret = NULL;
for (ret = strtok(str2, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s ", ret);
}
}
注:
由于strtok函数使用情况较少,所以这里没有对strtok进行模拟实现
strerror函数返回值类型为char*,参数为int。
strerror函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
FILE* pFile;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
printf("Error opening file unexist.ent: %s\n", strerror(errno));
//errno: Last error number
return 0;
}
因为strcpy与strncpy函数都是字符串间的拷贝,对于整型、结构体等其它类型的拷贝均不适用,因此memcpy函数的拷贝油然而生。
因为能够使得多种相同类型之间可以拷贝,因此memcpy函数的返回值为void* 型,第一个参数也为void* 型,是被拷贝的地址;第二个参数也为void* 型,是待拷贝内容的地址;第三个参数是size_t(无符号整型)的count,这里的count是整个需要拷贝的元素大小(单位:字节)。
memcpy函数的使用:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[100] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[100] = { 0 };
int i = 0;
memcpy(arr2, arr1, 10 * sizeof(int));//arr1的大小为40个字节,可以利用sizeof计算
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
代码运行结果:
memcpy函数的特点:
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* dst, const void* src, size_t count)
{
void* ret = dst;
assert(dst);
assert(src);
while (count--) {
*(char*)dst = *(char*)src; //每次复制一个字节的内容
dst = (char*)dst + 1;
src = (char*)src + 1;
}
return(ret);
}
int main()
{
int arr1[100] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[100] = { 0 };
int i = 0;
my_memcpy(arr2, arr1, 10 * sizeof(int));
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
代码运行结果:
在本程序中是把两个没有内存重叠的两个数组进行的相互拷贝,但是在内存出现重叠的情况时,memcpy函数在某些编译器下是无法实现的,所以我们在下面引入memmove函数。
使用memmove函数所需的头文件:
memmove函数的返回值与参数跟memcpy函数一样,此处不再细讲。
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* dst, const void* src, size_t count)
{
void* ret = dst;
assert(dst);
assert(src);
if (src > dst)
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dst + count) = *((char*)src + count);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
my_memmove(arr1+4,arr1+2 ,16);
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
代码执行结果:
如果用我们模拟实现memcpy函数的代码的结果为:
可见此处连续出现了三次3 4,这是什么原因呢?
以下用图解来帮助大家理解:
从前往后拷贝的意思是需要拷贝的内存内容是从前往后拷贝的,即从3到6开始拷贝,如果从后往前拷贝则是从6到3进行拷贝。因为5和6被赋值为3和4后再进行拷贝,则在5和6位置处拷贝的仍然是3,4的内容。
那么有哪几种情况呢?怎么解决?
一共有三种情况:
我们发现,当src大于dst时,只能从前往后拷贝,否则会出现上面用memcpy函数执行的错误的结果。当src小于dst时,只能从后往前拷贝,并且被拷贝的内存内容也是从后往前被拷贝。当第三中情况dst大于src并且没有内存内容没有重叠时,从前往后或者从后往前拷贝都是可以的,因此我们可以将这三种情况用两种解决方案解决。理解memmove模拟函数中的以下代码极为关键。
if (src > dst)
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dst + count) = *((char*)src + count);//count进入循环内部已经减1,刚好到最需要拷贝的内容的最后一个值处
}
}
如果这篇文章对你有帮助,麻烦点个赞支持下谢谢!
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/ZJRUIII/article/details/120404397
内容来源于网络,如有侵权,请联系作者删除!