前言
大步走在人生的道路上,沿途风光月霁,荆棘也开成了蔷薇
本章我们一起来学习内存系列函数+字符分类函数+字符转换函数
一、内存拷贝 — memcpy
1.memcpy函数的了解
void* — 通用类型的指针,可以接受任意类型数据的地址
注意:
拷贝的时候拷贝的是字节所以如果拷贝int类型中一个数字需要拷贝4个字节才等于一个整形
2.memcpy函数模拟实现
因为我们把源数据拷贝到目标空间希望看见目标空间里的数据然后把目标空间的地址返回去,所以返回数据写成void*
memcpy函数返回的是目标空间的起始地址
void — 通用类型的指针,可以接受任意类型数据的地址*
但是这种指针不能直接解引用和++ --运算
#include<stdio.h> #include<assert.h> void* my_memcpy(void* dest, const void* src, size_t num) { void* ret = dest; assert(dest && src); while (num--) { //如果转换成整形类型,拷贝的时候拷贝7个字节解引用一次四个字节++跳过四个字节再解引用一次四个字节,访问了八个字节所以会出问题所以我们使用char*指针 //但是这种强制类型转换只是临时的dest src下面进行时还是void*的,所以我们写成下面形式 *(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 }; int arr2[8] = { 0 }; my_memcpy(arr2, arr1, 20); return 0; }
3.思考:
答案:
1 2 1 2 1 2 1 8 9 10
#include<stdio.h> #include<assert.h> void* my_memcpy(void* dest, const void* src, size_t num) { void* ret = dest; assert(dest && src); while (num--) { *(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, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]);//打印的arr1所以是1212 } return 0; }
1 2 3 4 5 6 7 8 9 10
源头数据:1 2 3 4 5(绿色圈里)
目标数据:3 4 5 6 7(红色圈里)
当把源头数据拷贝到目标数据时目标数据3变成1,4变成2,再拷贝3的时候源头数据的3已经变成了1所以就把1再次拷贝到进去覆盖5同理2覆盖61覆盖7所以最终打印arr1出来是1 2 1 2 1 2 1 8 9 10;
所以我们发现:在内存重叠的时候,使用memcpy可能出现意想不到的效果~
建议:在内存重叠的情况,使用memmove函数
二、内存拷贝 — memmove
1.memmove函数的了解
由下面代码结果我们可以看出上面内存拷贝的问题用memmove得到了实现
2.memmove函数模拟实现
在拷贝的时候,我们发现1拷贝到3的位置2拷贝到4的位置;但是当把3拷贝到6的位置时3这个数据已经被1覆盖4的位置被数据2覆盖最终导致打印出来是1 2 1 2 1 2 1 8 9 10;那怎么办呢?
我们是不是可以倒着拷贝呢?我们将5拷贝到7的位置,4拷贝到6的位置,3拷贝到5的位置,2拷贝到4的位置,1拷贝到3的位置这样数据就不会被覆盖打印出就变成了1 2 1 2 3 4 5 8 9 10;
所以我们遇到这种情况就按倒着拷贝吗
那我们再来看看下面这幅图,当倒着拷贝的时候7拷贝到5,6拷贝到4,当把5拷贝到3的位置时5已经被7覆盖所以显然不行,最终会得到7 6 7 6 7 6 7 8 9 10
那我们正着拷贝的时候,3拷贝到1,4拷贝到2,5拷贝到3,6拷贝到4,7拷贝到5显然是可以的,没有发生数据覆盖情况
所以我们该怎么做呢
(1).结论一:
我们知道这是一个数组左边是低地址,右边是高地址,当dest小于src且与src重叠时,我们先拷贝源头的最左边的数字也就是先拷贝3也就是正着拷贝(从前向后);
(2).结论二:
当dest大于src且与src重叠时,我们先拷贝源头的最右边的数字也就是先拷贝7到9也就是倒着拷贝(从后向前);
(3).结论三:
当dest大于或小于src且与src没有重叠时,我们先拷贝就不需考虑从哪边开始拷贝且从哪边都可以拷贝;
(4).最终的模拟实现:
C语言:memcpy拷贝不重叠的内存;重叠的就交给memmove
1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
2.如果源空间和目标空间出现重叠,就得使用memmove函数处理。
#include<stdio.h> #include<assert.h> void* my_memmove(void* dest, const void* src, size_t num) { char* ret = dest; assert(src && dest); if (dest < src) //从前->后拷 { while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else //从后->前拷 { while (num--) { *((char*)dest+num) = *((char*)src+num); } } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1, arr1+2, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
三、内存比较 — memcmp
memcmp函数的了解
比较从ptr1和ptr2指针开始的num个字节
我们通过代码来例举:
当比较16个字节也就是4个整形也就是前四位数时,它们是相同的,无可置疑应该返回0,结果也确实是0;
#include<stdio.h> int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 1,2,3,4,6 }; int ret = memcmp(arr1, arr2, 16); printf("%d\n", ret); return 0; }
那当我们把16个字节改成17个字节呢还是0吗?
答案是-1;这是为什么呢?
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00
存在小端存储当比较字节数为17时,刚好比较到了05和06所以arr1<arr2返回<0的数,但是当你把5改成7时就返回了>0的数
四、内存设置 — memset
memset函数的了解
注意:
以字节为单位设置的
#include<stdio.h> int main() { char arr[] = "hello world"; memset(arr, 'x', 5); printf("%s\n", arr); return 0; }
五、字符分类函数
iscntrl | 任何控制字符 |
isspace | 空白字符:空格’‘,换页\f,换行’\n’,回车’\r,制表符’\t’或者垂直制表符’v’ |
isdigit | 十进制数字0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A ~ F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或 A~Z |
isalnum | 字母或者数字,a ~ z,A ~ Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
六、字符转换函数
int tolower ( int c );
int toupper ( int c );
#include<stdio.h> int main() { printf("%c\n", toupper('a'));//小写转换成大写 printf("%c\n", tolower('A'));//大写转换成小写 return 0; }
例题:
输入的字符全部转换成小写
#include<stdio.h> #include<ctype.h> int main() { char arr[128] = { 0 }; gets(arr); int i = 0; while (arr[i]) { if (isupper(arr[i])) { arr[i] = tolower(arr[i]); } printf("%c", arr[i]); i++; } return 0; }
总结
Ending,今天的内存系列函数内容就到此结束啦~,如果后续想了解更多,就请关注我吧。