总言
C语言:字符串和内存函数使用介绍。
文章目录
- 总言
- 1、求字符串长度:strlen
- 1.1、基本介绍
- 1.2、演示说明
- 1.2.1、strlen输出
- 1.2.2、strlen返回值
- 1.3、模拟实现strlen
- 1.3.1、计数器写法
- 1.3.2、递归写法
- 1.3.3、指针-指针写法
- 2、长度不受限制的字符串函数
- 2.1、字符串拷贝:strcpy
- 2.1.1、基本介绍
- 2.1.2、演示说明
- 2.1.3、模拟实现
- 2.1.3.1、写法1.0
- 2.1.3.2、简化2.0
- 2.2、字符串追加:strcat
- 2.2.1、基本介绍
- 2.2.2、演示说明
- 2.2.3、模拟实现
- 2.2.3.1、写法1.0
- 2.2.3.2、简化2.0
- 2.3、字符串比较:strcmp
- 2.3.1、基本介绍
- 2.3.2、演示说明
- 2.3.3、模拟实现
- 3、长度受到限制的字符串函数
- 3.1、字符串拷贝:strncpy
- 3.1.1、基本介绍
- 3.1.2、使用演示
- 3.2、字符串追加:strncat
- 3.2.1、基本介绍
- 3.2.2、使用演示
- 3.3、字符串比较:strncmp
- 3.3.1、基本介绍
- 3.3.2、使用演示
- 3.2.3、关于字符串比较的一些细节说明
- 4、字符串查找
- 4.1、字符串中找子串:strstr
- 4.1.1、基本介绍
- 4.1.2、使用演示
- 4.1.3、模拟实现
- 4.2、字符串拆分:strtok
- 4.2.1、基本介绍
- 4.2.2、使用演示
- 5、错误信息报告
- 5.1、strerror
- 5.1.1、基本介绍
- 5.1.2、使用演示(errno、perror)
- 6、字串操作函数
- 6.1、总览
- 6.2、使用演示
- 7、内存操作函数
- 7.1、内存块拷贝:memcpy
- 7.1.1、基本介绍
- 7.1.2、使用演示
- 7.1.3、模拟实现
- 7.2、内存块挪动:memmove
- 7.2.1、基本介绍
- 7.2.2、使用演示
- 7.2.3、模拟实现
- 7.3、内存块比较:memcmp
- 7.3.1、基本介绍
- 7.3.2、使用演示
- 7.4、内存块设置:memset
- 7.4.1、基本介绍
- 7.4.2、使用演示
1、求字符串长度:strlen
1.1、基本介绍
1)、使用说明
函数链接
1、字符串以'\0'
作为结束标志,strlen函数返回的是在字符串中 '\0'
前面出现的字符个数(不包含 '\0'
)。
2、参数指向的字符串必须要以 '\0'
结束,否则输出结果不一定正确。
3、注意函数的返回值为size_t
,是无符号整型。
1.2、演示说明
1.2.1、strlen输出
2)、strlen正常输出举例(1)
以常量字符串"abcdefg"
初始化数组,数组中实际存储的是a、b、c、d、e、f、g、\0
。
#include<string.h>
int main()
{
char arr[] = "abcdefg";
printf("%d\n", strlen(arr));
return 0;
}
3)、strlen错误输出举例
arr[] = {'a','b','c','d','e','f','g'}
#include<string.h>
int main()
{
char arr[] = {'a','b','c','d','e','f','g'};
printf("%d\n", strlen(arr));
return 0;
}
关于上述的修改方法:在数组中主动添加’\0’,arr[] = {'a','b','c','d','e','f','g','\0'}
#include<string.h>
int main()
{
char arr[] = {'a','b','c','d','e','f','g','\0'};
printf("%d\n", strlen(arr));
return 0;
}
4)、strlen正常输出举例(2)
问:假如指定数组长度大于实际字符长度,这种情况下strlen的输出结果如何?为什么?
#include<string.h>
int main()
{
char arr[10] = {'a','b','c','d','e','f','g'};
printf("%d\n", strlen(arr));
return 0;
}
原因说明:此处字符数组规定其大小,对于未初始化的元素,默认初始化为0,ASCII为‘\0’,故strlen求得准确值。
1.2.2、strlen返回值
1)、问题分析
要注意strlen的返回值是无符号整型(unsigned int
),如果不注意此细节会引起意想不到的错误:
如下述这段代码,从数值角度,strlen("abc")=3
,strlen("abcdef")=6
,两者相减后结果为负数。
#include<string.h>
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
我们来看实际输出结果:
由于strlen
返回无符号整型,在有符号类型中,3-6=-3
,-3
对应一个很大的无符号整型,是正数。(无符号相减还是无符号数。)
2)、解决方案
其一:直接采用大于小于号判断
#include<string.h>
int main()
{
if (strlen("abc") > strlen("abcdef") )
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
其二:强制类型转换为有符号的int
#include<string.h>
int main()
{
if ((int)strlen("abc") - (int)strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
1.3、模拟实现strlen
1.3.1、计数器写法
1)、写法1.0
size_t my_strlen(const char* str)
{
int count;
for (count = 0; *str != '\0'; str++)
count++;
return count;
}
size_t my_strlen(const char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
2)、写法2.0
1、对于空字符\0而言,其数值为0,故循环判断中, *str != '\0'
, 或者 *str!=0
都可简写为 *str
。
2、*str++
,当前循环过程中不涉及该str的使用变量++是在条件判断后进行。
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);
int count = 0;
while (*str++)
{
count++;
}
return count;
}
1.3.2、递归写法
使用递归写法时,一定要注意递归的两个条件。
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);
if (*str == '\0')
{
return 0;
}
return 1 + my_strlen(++str);
}
1.3.3、指针-指针写法
使用指针-指针的方法时,需要注意最后的返回值。
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);
char* end = str;
while (*end != '\0')
{
end++;
}
return end - str;
}
对while循环的进一步简化。
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);
char* end = str;
while (*end++);
return end - str-1;//此处-1是因为跳出while循环时s又自增了一次
}
这里就能体现出前置自增和后置自增的区别。
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);
char* end = str;
while (*++end);
return end - str;
}
2、长度不受限制的字符串函数
2.1、字符串拷贝:strcpy
2.1.1、基本介绍
函数介绍
1、strcpy拷贝时,源字符串source
必须以 '\0'
结束。
2、strcpy拷贝时会将源字符串source
中的 ‘\0’ 拷贝到目标空间destination
中。
3、因此目标空间必须足够大,以确保能存放源字符串。
4、char * destination, const char * source
表明,目标空间必须可以修改。
5、需要注意其返回值char *
,返回的是目标空间的起始地址,这样做的目的是达到链式访问。比如:
printf("%s\n", strcpy(destination, source));
2.1.2、演示说明
1)、strcpy拷贝时,源字符串必须以 ‘\0’ 结束
演示如下,假如我们使用char arr4[] = { 'a','b','c','d','e'};
这类不含有’\0’的源字符串,结果出错。
#include<string.h>
int main()
{
char arr1[] = "hello";
char arr2[20];
char arr3[10] = { 'f','r','e','e','d','o','m','\0'};
char arr4[] = { 'a','b','c','d','e'};//错误演示一
strcpy(arr2, arr1);
printf("%s\n", arr2);
strcpy(arr2, arr3);
printf("%s\n", arr2);
strcpy(arr2, arr4);
printf("%s\n", arr2);
return 0;
}
2)、strcpy拷贝时会将源字符串中的 ‘\0’ 拷贝到目标空间中
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcd";
char arr2[20]="XXXXXXXXXXXXXXXX";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
3)、常量字符串不能使用strcpy等拷贝:目标空间必须可以修改
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcd";
char* arr2 = "XXXXXXXXXXXXXXXX";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
arr2指向的空间是常量字符串,常量字符串是不能被修改的。
2.1.3、模拟实现
2.1.3.1、写法1.0
#include<assert.h>
char* my_strcpy(char* des, const char* sou)
{
assert(des && sou);
char* point = des;
while (*sou != '\0')
{
*des = *sou;
des++;
sou++;
}
*des = '\0';//解决末尾的'\0'
return point;
}
1、assert(des && sou);
即assert(dest != NULL);
和assert(src != NULL);
的简化写法。注意中间用的是逻辑与。
2、*des = '\0';
,也可以写成*des = *sou;
,由于循环以判断’\0’结束,故需要再末尾将source
中的'\0'
拷贝到destination
中。
2.1.3.2、简化2.0
#include<assert.h>
char* my_strcpy(char* des, const char* sou)
{
assert(des && sou);
char* point = des;
while (*des++ = *sou++);
return point;
}
while (*des++ = *sou++);
这串代码的执行逻辑为:
1、解引用des、sou
2、将其赋值*des = *sou
3、得到结果非’\0’ 时为真,循环继续。
4、des、sou
分别自增。
故当sou=='\0'
时,它会先将其赋值给des
,然后再判断是否进入while
循环,最后都会自增。但因并没有对自增后的des、sou
做修改,不会越界报错。
2.2、字符串追加:strcat
2.2.1、基本介绍
相关函数描述
1、源字符source
串必须以 ‘\0’ 结束,且从目标空间destination
中‘\0’开始追加字符。
2、目标空间必须有足够的大,能容纳下源字符串的内容。
3、目标空间必须可修改。
2.2.2、演示说明
1)、strcat字符串追加演示
#include<string.h>
int main()
{
char arr1[20] = "call me";
char arr2[] = " up!";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
2)、目标空间中没有’\0’的一种错误追加举例:
#include<string.h>
int main()
{
char arr1[] = { 'E','n','j','o','y',' '};
char arr2[] = "yourself.";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
分析: 此代码存在两个问题,其一为数组未初始化长度时,会以实际初始化的元素个数来判定空间大小,故使用strcat
时空间不够大。其二,这种初始化方式导致目标空间结尾没有'\0'
,会发生错误拼接。
3)、验证strcat字符串追加是从'\0'
位置处开始
#include<string.h>
int main()
{
char arr1[20] = "Enjoy \0XXXXXXXXXX";
char arr2[] = "yourself.";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
2.2.3、模拟实现
整体思路:
1、找到destination
中的'\0'
。
2、将source
源字符串拷贝过来,注意包含'\0'
。
总体来说就是,strcpy的基础上多增加了一个找尾工作。
2.2.3.1、写法1.0
#include<assert.h>
char* my_strcat(char* des, char* sou)
{
assert(des && sou);
char* point = des;//解决返回值问题
while (*des != '\0')//循环至目标空间末尾,找到目标空间处的'\0'
++des;
while (*sou != '\0')//拼接工作,将源字符数组拼接到目标字符数组中
{
*des = *sou;
des++;
sou++;
}
*des = *sou;//上述循环后,目标空间缺少末尾\0,故在此处补上
return point;
}
2.2.3.2、简化2.0
#include<assert.h>
char* my_strcat(char* des, char* sou)
{
assert(des && sou);
char* point = des;
while (*des)
des++;
while (*des++ = *sou++);
return point;
}
#include<assert.h>
char* my_strcat(char* des, char* sou)
{
assert(des && sou);
char* point = des;
while (*++des);//此处也可以用前置自增解决
while (*des++ = *sou++);
return point;
}
上述代码存在问题:
这里关键在于编译器如何实现该函数,使用我们模拟实现的my_strcat
自己给自己追加时,由于'\0'
被覆盖,追加不会停止,结果就是越界。
2.3、字符串比较:strcmp
2.3.1、基本介绍
相关函数介绍
1、strcmp
比较的不是字符串长度,而是比较字符串对应位置的字符ASCII码大小,若相同,则比较下一对,直到不同或遇到\0
。
2、对返回值:①第一个字符串大于第二个字符串,则返回大于0的数字;②第一个字符串等于第二个字符串,则返回0;③第一个字符串小于第二个字符串,则返回小于0的数字。
3、C标准里只规定了>0、<0、==0
,没有规定具体数值,例如vs下是1 0 -1,但不能以此惯性思维用于全部编译器,这样在使用判断语句时可能出错。
2.3.2、演示说明
1)、strcmp使用演示和返回值说明
int main()
{
char arr1[] = "freedom";
char arr2[] = "free";
printf("%d\n", strcmp(arr1, arr2));
char arr3[] = "abcde";
char arr4[] = "abcdef";
printf("%d\n", strcmp(arr3, arr4));
char arr5[] = "free";
char arr6[] = "free";
printf("%d\n", strcmp(arr5, arr6));
return 0;
}
此处需要注意其返回值:
以下面这种写法来判断输出结果是错误的。
int main()
{
char arr1[] = "freedom";
char arr2[] = "free";
int ret=strcmp(arr1, arr2);
if (ret == 1)
{
printf(">\n");
}
else if (ret == -1)
{
printf("<n");
}
else if (ret == 0)
{
printf("==\n");
}
return 0;
}
正确写法如下:
int main()
{
char arr1[] = "freedom";
char arr2[] = "free";
int ret=strcmp(arr1, arr2);
if (ret > 0)
{
printf(">\n");
}
else if (ret < 0)
{
printf("<n");
}
else//==0
{
printf("==\n");
}
return 0;
}
2.3.3、模拟实现
一种是直接在外部判断结果:
int my_strcmp(char* s1, char* s2)
{
assert(s1 && s2);
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
return *s1 - *s2;
}
一种是将相等的情况放在while循环内判断。
int my_strcmp(char* s1, char* s2)
{
assert(s1 && s2);
while (*s1 == *s2)
{
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return *s1 - *s2;
}
总而言之写法不唯一。
3、长度受到限制的字符串函数
3.1、字符串拷贝:strncpy
3.1.1、基本介绍
相关函数介绍
1、strncpy
从源字符串中拷贝num
个字符到目标空间。
2、如果源字符串的长度小于num
,则拷贝完源字符串之后,在目标空间后边追加0,直到满足num
个。
3、如果源长度超过 num,则不会在目标末尾隐式附加空字符。
3.1.2、使用演示
1)、源字符串的长度超过num
值
int main()
{
char arr1[] = "hello";
char arr2[] = "beautiful!";
printf("%s\n", strncpy(arr1, arr2, 4));
return 0;
}
当源source长于num时,是不把源中的’\0’一并拷贝过来的。
2)、源字符串的长度小于num
值
int main()
{
char arr1[] = "abcdefghijk";
char arr2[] = "free";
printf("%s\n", strncpy(arr1, arr2, 8));
return 0;
}
3.2、字符串追加:strncat
3.2.1、基本介绍
1、strncat字符串追加,追加后会放置一个’\0’。
3.2.2、使用演示
1)、当num值小于source时
int main()
{
char arr1[] = "abcd\0XXXXXXXXX";
char arr2[] = "freedom";
printf("%s\n", strncat(arr1, arr2, 4));
return 0;
}
2)、当num值大于source时
int main()
{
char arr1[] = "abcd\0XXXXXXXXX";
char arr2[] = "freedom";
printf("%s\n", strncat(arr1, arr2, 9));
return 0;
}
3.3、字符串比较:strncmp
3.3.1、基本介绍
3.3.2、使用演示
int main()
{
char arr1[] = "abcde";
char arr2[] = "abcdeg";
printf("%d\n", strncmp(arr1, arr2, 4));
char arr3[] = "abcde";
char arr4[] = "abcdeg";
printf("%d\n", strncmp(arr3, arr4, 6));
char arr5[] = "abcde";
char arr6[] = "abcdeg";
printf("%d\n", strncmp(arr5, arr6, 8));
return 0;
}
3.2.3、关于字符串比较的一些细节说明
代码如下:
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcd";
if (arr1 == arr2)
{
printf("==\n");
}
else
{
printf("!=\n");
}
printf(" %d\n", "abcd" < "abcdef");
return 0;
}
如下图,事实上,在不适用相应字符串比较函数时,如果直接拿字符串进行比较,这样得出的结果不具有正确性。
1、比如,这里的arr1
和arr2
,实际比较的是数组地址空间。
2、再比如,"abcd" < "abcdef"
,这里实际上比较的仍旧是常量字符串首字符的地址。
4、字符串查找
4.1、字符串中找子串:strstr
4.1.1、基本介绍
相关函数介绍
1、strstr寻找的是首次出现的位置,若找不到,则返回NULL。
4.1.2、使用演示
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "def";
char* ret1 = strstr(arr1, arr2);
if (ret1 != NULL)
{
printf("%s\n", ret1);
}
char arr3[] = "abcdefabcdef";
char arr4[] = "aaa";
char* ret2 = strstr(arr3, arr4);
if (ret2 == NULL)
{
printf("%s\n", ret2);
}
return 0;
}
4.1.3、模拟实现
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* start1 = str1, * start2 = str2;
const char*cur = str1;
while (*cur)//向str1中找str2,若st1遍历完后仍旧没找到,则返回NULL
{
start1 = cur;
start2 = str2;//匹配失败后,要将start2指向还原
while (*start1 && *start2 && (*start1 == *start2))//若start1指向与start2指向内容相同,标记,并往后匹配
{//需要注意长度不等时,其中字符串先后结束的情况
start1++;
start2++;
}
//跳出上述while循环的情况只有两种,一则是匹配失败 *start1 != *start2,一则是其中字符串遇到'\0'
if (*start2 == '\0')
{//若结束时start2为NULL,说明成功在start1中找到子串
return (char*)cur;
}
cur++;//如果匹配失败,则将start1中标记指针向后移一位,重新新一轮匹配
}
return NULL;
}
PS:查找子串可使用KMP算法。
4.2、字符串拆分:strtok
4.2.1、基本介绍
相关函数介绍
char * strtok ( char * str, const char * sep );
1、第二参数sep
是字符串,其定义了用来分隔str
的字符集合。
const char* sep = " #-";
2、第一个参数指定了一个字符串,它包含了0个或者多个由sep
字符串中一个或者多个分隔符分割的标记。
char arr[] = "Life is#too-short for#long-term grudges.";
3、strtok
函数根据给定的sep
,在str
中找到匹配的标记,并将其用 \0
结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok
函数切分的字符串一般都是临时拷贝的内容并且可修改。)
char arr[] = "Life is#too-short for#long-term grudges.";
char arr[] = "Life\0is\0too\0short\0for\0long\0term\0grudges.";
4、函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
5、函数的第一个参数为 NULL ,函数将从同一个字符串中被保存的位置开始,查找下一个标记。
6、如果字符串中不存在更多的标记,则返回 NULL 指针。
4.2.2、使用演示
int main()
{
char arr[] = "Life is#too-short for#long-term grudges.";
char tmp[100] = { 0 };
strcpy(tmp, arr);
const char* sep = " #-";
char* str = NULL;
for (str = strtok(tmp, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
printf("\n");
return 0;
}
以下为strtok会改变字符串的调试演示:因此在使用该函数时,若不想改变源字符串,则可临时拷贝一份数据。
5、错误信息报告
5.1、strerror
5.1.1、基本介绍
相关函数介绍
1、返回错误码所对应的错误信息。
5.1.2、使用演示(errno、perror)
1)、VS2019下strerror错误信息举例演示
strerror
生成的错误字符串,取决于系统和库实现。此处只是简单演示一下:
int main()
{
for (int i = 0; i < 10; ++i)
{
printf(" %d: %s\n", i, strerror(i));
}
return 0;
}
2)、使用演示(二)
比如下述这段代码,我们使用malloc
动态开辟空间,如果其返回值为NULL
,说明开辟失败,这时候错误信息会被保存在errno
中,我们就可以调用strerror
来查看。
#include<limits.h>
#include<errno.h>
int main()
{
int* p = (int*)malloc(INT_MAX);
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
return 0;
}
3)、上述报错,使用perror函数演示
perror是将错误信息打印出来,而strerror本身只是拿到错误信息(若要打印则需要相对的打印函数 )。
int main()
{
int* p = (int*)malloc(INT_MAX);
if (p == NULL)
{
//printf("%s\n", strerror(errno));
perror("malloc");//此处传入的字符串是自定义的
return 1;
}
return 0;
}
6、字串操作函数
6.1、总览
相关函数介绍
6.2、使用演示
1)、判断是否为数字
#include<ctype.h>
int main()
{
printf("%d\n", isdigit('6'));
printf("%d\n", isdigit('2'));
printf("%d\n", isdigit('a'));
return 0;
}
2)、判断是否为字母
#include<ctype.h>
int main()
{
printf("%d\n", isalpha ('6'));
printf("%d\n", isalpha('E'));
printf("%d\n", isalpha('a'));
return 0;
}
使用上述库函数就不用我们自己写函数来判断字符是否为字母了:
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
{
}
3)、大小写转换
#include<stdio.h>
#include<ctype.h>
void str_toupper(char arr[])
{
int i = 0;
while (arr[i])
{
arr[i] = toupper(arr[i]);
i++;//ps:此处i++不能直接在while判断语句中使用
}
}
void str_tolower(char arr[])
{
int i = 0;
while (arr[i])
{
arr[i] = tolower(arr[i]);
i++;
}
}
int main(void)
{
char arr[20]="";
scanf("%s", arr);
str_toupper(arr);
printf("大写:%s\n", arr);
str_tolower(arr);
printf("小写:%s\n", arr);
return 0;
}
//例如:
//abdf446AEFC
//大写:ABDF446AEFC
//小写:abdf446aefc
7、内存操作函数
7.1、内存块拷贝:memcpy
7.1.1、基本介绍
相关函数介绍
1、函数memcpy
从source
的位置开始向后复制num
个字节的数据到destination
的内存位置。
2、这个函数在遇到 '\0'
的时候并不会停下来。其总是拷贝num
字节。
3、如果source
和destination
有任何的重叠,复制的结果都是未定义的。
7.1.2、使用演示
1)、memcpy的常规使用举例:source
和destination
无重叠
int main()
{
int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
int arr2[5] = { 0 };
memcpy(arr2, arr1, 20);
return 0;
}
2)、使用memcpy时,source
和destination
有任意重叠的情况演示
通常情况下,memcpy
在作用于重叠空间时结果未定义,比如用我们自己实现的memcpy
来演示结果如下:
因此才有了后续将学习到的函数:memmove
但是有的编译器会将该memcpy
的实现效果完善到与memmove
相似的程度,因此不存在重叠空间拷贝错误的问题。以下是VS2019的演示结果:
7.1.3、模拟实现
void* my_memcpy(void* des, void* sou, size_t num)
{
assert(des && sou);
void* point = des;
while (num--)
{
*(char*)des = *(char*)sou;
des = (char*)des + 1;
sou = (char*)sou + 1;
}
return point;
}
*(char*)des = *(char*)sou;
:需要注意,由于void*
类型的指针不能直接解引用,此处使用了强制类型转换。选择char*
类型,可以做到将指针访问权限控制在一字符,由单字符的挪动,从而达到整体数据的挪动。
以及,需要注意,此处解引用是在强制类型转换之后。
des = (char*)des + 1;
、sou = (char*)sou + 1;
:用于达到指针遍历自增的目的。同样的,void*
的指针无法自增,此处将其强制类型转换为char*
指针。
++(char*)des;
++(char*)sou;
上述这种写法在一些编译器下无法成功运行。
7.2、内存块挪动:memmove
7.2.1、基本介绍
1、区别于memcpy
,memmove
函数处理的源内存块和目标内存块是可以重叠的。
2、如果源空间和目标空间出现重叠,就得使用memmove
函数处理。
7.2.2、使用演示
int main()
{
int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
int arr2[5] = { 0 };
memmove(arr1+2, arr1, 20);
return 0;
}
int main()
{
int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
int arr2[5] = { 0 };
memmove(arr2, arr1, 20);
return 0;
}
7.2.3、模拟实现
分析思路如下:
选择原地挪动数据,根据memcpy中的模拟实现可知,存在数据覆盖问题。因此我们分情况讨论。
1、将des<sou时分为一类,此时挪动数据采取先挪动前面的数据,再挪动后面的数据(从前往后挪动)的方式;
2、将des>sou时分为一类,此时挪动数据采取先挪动后面的数据,再挪动前面的数据(从后往前挪动)的方式
void* my_memmove(void* des, void* sou, size_t num)
{
assert(des && sou);
void* point = des;
if (des < sou)//先挪动前再挪动后
{
while (num--)
{
*(char*)des = *(char*)sou;
des = (char*)des + 1;
sou = (char*)sou + 1;
}
}
else//先挪动后再挪动前
{
while (num--)
{
*((char*)des + num) = *((char*)sou + num);
}
}
return point;
}
7.3、内存块比较:memcmp
7.3.1、基本介绍
相关函数链接
7.3.2、使用演示
int main()
{
int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };
int arr2[5] = { 0,1,2,3,0X11223305 };
printf("%d\n",memcmp(arr2, arr1, 20));
return 0;
}
7.4、内存块设置:memset
7.4.1、基本介绍
相关函数链接
1、memset是以字节为单位,对指定内存初始化值为value 。
7.4.2、使用演示
int main()
{
int arr2[5] = { 0X11111111,0X22222222,0X33333333,0X44444444,0X55555555 };
memset(arr2, 0, 16);
return 0;
}
一定要注意这里的以字节为单位设置值value
:
int main()
{
int arr2[5] = { 0X11111111,0X22222222,0X33333333,0X44444444,0X55555555 };
memset(arr2, 6, 16);
return 0;
}