目录
七.memcpy,memmove和memset (内存函数)
<3>memcpy拷贝数组,如果num不是4的倍数该是什么样子呢?
在使用以下函数时,都需要引用头文件:#include<string.h>
一.strlen函数(求字符串长度函数)
1.函数的介绍
size_t strlen(const char *str);
注意:
- 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(包括'\0')
- 参数指向的字符串必须要以'\0'结束,不然就是随机值了
- 注意函数的返回值为size_t,是无符号的(易错)(typedef unsigned int size_t)
strlen是求字符串长度的,求出的长度是不可能是负数的,所以返回类型设置为size_t
由于strlen返回类型是size_t,上面strlen(arr1)-strlen(arr2)属于size_t-size_t=size_t
len1=strlen(arr1)=3,len2=strlen(arr2)=9,len1-len2=3-9=-6,但是无符号整型,所以结果还是6,运行结果始终是大于。
总结:长度短-长度长同样>0,因为strlen是size_t类型,结果都是无符号整型
2、模拟实现strlen(计数器、递归、指针方式)
2.1.计数器
利用计数器实现很简单,循环只要不遇到'\0'
,就继续循环。
#include<stdio.h>
#include<string.h>
#include<assert.h>
int my_strlen(const char*str)
{
int count = 0;
while (*str)
{
str++;
count++;
}
return count;
}
int main()
{
char arr[20] = "chenhuangqing";
int len= my_strlen(arr);
printf("%d\n",len);
return 0;
}
2.2指针-指针
前面介绍过,指针-指针得到的是元素之间的个数,利用这个原理,就能实现strlen
int my_strlen(const char* str)
{
assert(str);
const char* start =str;
while (*str)
{
str++;
}
return str - start;
}
int main()
{
char arr[20] = "chenhuangqing";
int len=my_strlen(arr);
printf("%d\n",len);
return 0;
}
2.3递归
如果*str
不为'\0'
就可以return 1 + my_strlen(++str)
这样每次都可以+1,直到遇到字符串的结束标志'\0'
,结束递归。
int my_strlen(const char* str)
{
assert(str != NULL);
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[20] = "chenhuangqing";
int len=my_strlen(arr);
printf("%d\n",len);
return 0;
}
二、strcpy和strncpy函数(字符串拷贝函数)
1.strcpy函数介绍(长度不受限制)
char* strcpy (char * destination, const char * source )
- strcpy函数字符串source(包括终止字符'\0')复制到字符串destination中。复制或附加字符串时不执行溢出检查,如果源字符串和目标字符串重叠,strcpy行为是未定义的。
- .源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。(需要将源空间arr2复制到目标空间arr1中,但是目标空间没有足够大,则编译器会报错)
-
目标空间char arr1[3]=" "; 源空间char arr2[10]="abdasd" 这样编译器会报错
- 目标空间必须可变(指针和const是不可以修改的,无法满足目标空间必须可变原则)
char *p="asba";
char arr2[]="ssa ajsj" 编译器会崩,因为*p是不可不修改的,目标空间只有可变才能从源头给目标中去。
1.1模拟实现strcpy
char* my_strcpy(char* des, const char* src)
{
assert(des&&src);
char* ret =des;//记录目标空间的起始地址
while (*des++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = "";
char arr2[20] = "chenhuangqing";
char*ret=my_strcpy(arr1, arr2);
printf("arr1:%s\n", ret);
return 0;
}
注意:strcpy的返回值是目标字符串的起始地址
2.strncpy函数介绍(长度受限制)
char *strncpy (char *destination,const char*source,size_t num);
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
上面代码只拷了abc没有拷贝\0;
2.1模拟实现strncpy
复制字符串的同时也得控制要小于等于size_t num,如果大于num个就直接复制为'\0'即可
len代表复制多少字符到目标空间中去,复制字符串的前提有2个(len=0和源字符串遇到'\o'结束)
如果先遇到'\0',但是len!=0,那么就将剩下的len个都设置成'\0'
char* my_strncpy(char* arr1, const char* arr2, int len)
{
char* ret = *arr1;//记录目标数组的首地址
assert(arr1 && arr2);
while (len&&(*arr1++=*arr2++))//复制字符串,并考虑n小于或等于被拷贝字符串数目的情况
{
len--;//如果len=0了则表示已经赋值好了所需要的字符串,如果后面的等于零,表示已经复制完了<=len的字符串
}
if (len)//如果n大于被拷贝字符串数目,须将除被拷贝字符的其他内容用空字符进行补充
{
while (len)
{
*arr1 = '\0';
len--;
}
}
return ret;
}
int main()
{
char arr1[20] = "xxxxxxxxxxxxxxxx";
char arr2[] = "abcdef";
char*ret=my_strncpy(arr1,arr2, 10);
printf("%s\n", arr1);
return 0;
}
三.strcat和strncat函数(连接字符串)
1.strcat函数介绍
char * strcat ( char * destination, const char * source );
- 源字符串必须以 '\0' 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 字符串自己给自己追加,如何? 自己给自己追加就会使'\0'覆盖掉,所以不成立
1.1模拟实现strcat
先到达源字符串的终点位置,然后追加,将追加的字符串连接到源字符串中去
char* my_strcat(char* des, const char* src)
{
assert(des && src);
char* start = des;
//找到目标空间'\0'
while (*des)
{
des++;
}
//追加,复制字符串
while (*des++= *src++)
{
;
}
return start;
}
int main()
{
char arr[20] = "hello hello ";
char*ret=my_strcat(arr, "world");
char* p = "world";
printf("%s\n", arr);
return 0;
}
2.strncat函数介绍
char * strncat ( char * destination, const char * source, size_t num );
将追加字符串中的前4位,将目标字符串前num个字符连接到source字符串后面
四.strcmp和strncmp(比较字符串)
1.strcmp函数介绍
int strcmp ( const char * str1, const char * str2 );
- 标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字 1
第一个字符串等于第二个字符串,则返回等于0的数字0
第一个字符串小于第二个字符串,则返回小于0的数字 -1
vs环境下 小于0是-1,等于0是0,大于0是1
1.1模拟实现strcmp
int my_strcmp(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);
while (*arr1 == *arr2)//如果俩者相等就进循环
{
if (*arr1 == '\0')
//如果其中一个是'\0'就返回0
{
return 0;
}
arr1++;
arr2++;
}
return *arr1 - *arr2;//遇到不相等就可以指针相减
}
int main()
{
char arr1[10] = "abc";
char arr2[10] = "abc";
int ret = my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
2.strncmp函数介绍
int strncmp ( const char * str1, const char * str2, size_t num );
俩个字符串常量相比较,比较到第num个字符为止就结束,哪怕num之前相等,num后不相等,都不比较了。
五.strstr(字符串查找)
1.strstr函数介绍
KMP算法
查找一个大串中是否存在一个小串字符串
char * strstr ( const char *str1, const char * str2);
- 函数搜索字符串str2在字符串str1中的第一次出现
- 该函数返回字符串的其余部分
- 如果未找到所搜索的字符串,则返回false
找到了就将后面的字符都打印出来,直到遇到'\0'
1.1模拟实现strstr(KMP算法)
char *my_strstr(char* str1, char* str2)
{
assert(str1 && str2);
if (*str2 == '\0')
{
return (char*)str1;
}
const char* s1 = str1;
const char* s2 = str2;
const char* cp = str1;//记录str1的首地址
while (*cp)
{
s1 = cp;//遇到不相等的值,cp回到s1的位置,,然后cp负责走,s1负责留在原位置
s2 = str2;//遇到不相等的值,str2又回到重新位置
while (*s1!='\0'&&*s2!='\0'&& * s1 == *s2)
//如果其中一个遇到'\0'就直接跳出循环,如果俩个相等就加加判断下一个是不是也是相等的,同时cp++
{
s1++;
s2++;
}
if (*s2 == '\0')//如果s2遇到'\0'了就说明大串已经遍历完成了,返回的是bcdef这小串
{
return (char*)cp;
}
cp++;
}
return NULL;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bc";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("找不到\n");
}
else
{
printf("%s\n",ret);
}
return 0;
}
六.strerror和perror(错误信息)
1.strerror函数介绍
char * strerror ( int errnum );
#include<errno.h>
返回错误码,所对应的错误信息
在c语言中的库函数运行的时候,如果发生错误,就将错误码存在一个变量中,这个变量是:error
我们要将错误码翻译成错误信息
strerror的作用在于就是可以明确指明此物是否可以找到,会给你明确的答复,文件text.exe这个是表示没有存在此文件的。No such file or directory----没有这个文件
2.perror函数介绍
perror是直接打印错误信息,在打印错误信息前会先打印自定义信息
perror=printf+strerror的组合
strerror只是将errno错误信息传给strerror,并没有自定义信息
perror会去打印自定义函数,而且会报出错误信息
七.memcpy,memmove和memset (内存函数)
1.memcpy(内存拷贝函数)
void * memcpy ( void * destination, const void * source, size_t num );num代表字节
strcpy只是对字符串的拷贝,并不能拷贝其他类型的字符串,memcpy可以实现
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。
<1>memcpy拷贝数组中前5个元素
<2>memcpy拷贝数组中中间5个元素
<3>memcpy拷贝数组,如果num不是4的倍数该是什么样子呢?
这应该打印出什么呢?
17个字节,int占4个字节,4*4=16,为什么打印5个元素,因为vs是小端存储
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00这里是16个字节
第17个字节是 05,所以17个字节是05,所以打印出就是5个元素。
1.2.memcpy的模拟实现
注意:
1.首先对于字节是char类型的,需要对其强制类型转换为char类型
2.其次对地址往后移一位是要注意的
++(char*)dest dest=(char*)dest+1
后置++是不行的; 不能dest++因为即使你强制类型转化为char*,但是地址++,还是int*类型
实现过程:
那如果出现内存重叠该如何拷贝呢?
我们要知道拷贝是(拷贝一个,读取一个,并不是一次性拿完再放上去)
所以接下来我们就看看memmove拷贝函数来解决这个问题
2.memmove(解决内存重叠的拷贝函数)
void * memmove ( void * destination, const void * source, size_t num );
- 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
- 如果源空间和目标空间出现重叠,就得使用memmove函数处理
2.1模拟实现memmove
<1>.dest<src
先3给1,4给2,是从前往后走
<2>dest>=src
先拿7给8,6给7,从后往前走
总结:
当目标数组起始位置小于源数组起始位置,那么从前往后走、
当目标数组起始位置大于等于源数组起始位置,那么从后往前走
代码如下:
void* my_memmove(char* des, const char* src, size_t num)
{
assert(des && src);
void* ret = des;
if (des < src)
{
//从前往后走
while (num--)
{
*(char*)des = *(char*)src;
++(char*)des;
++(char*)src;
}
return ret;
}
else
{
//从后往前走
while (num--)//num=20
{
*((char*)des + num) = *((char*)src + num);//num--后是19
}
return ret;
}
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int len = sizeof(arr1) / sizeof(arr1[0]);
my_memmove(arr1, arr1 + 2, 20);
for (int i = 0; i < len; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
总结:
当dest<src时,就用memcpy
当dest>=src时,就用memmove
第一个参数是dest,第二个参数是src,如果第一个小于第二个,就用memcpy,反之用memmove
3.memcmp(内存比较函数)
3.1.memcmp函数介绍
int memcmp ( const void * ptr1, const void * ptr2,size_t num);
- 比较从ptr1和ptr2指针开始的num个字节
-
- 第一个元素大于第二个元素,则返回大于0的数字 1
- 第一个元素等于第二个元素,则返回0 0
- 第一个元素小于第二个元素,则返回小于0的数字 -1
vs环境下 小于0是-1,等于0是0,大于0是1;
3.2.memcmp的应用
4.memset(内存设置函数)
大多数情况下都是给置为0,将数组中所有元素都置为0
将改为0,则表示改为'\0',当打印的时候,第一个遇到了'\0'就直接结束了,打印的都是空的。
答案在明天,每天的明天。