=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期

学C的第二十八天【字符串函数和内存函数的介绍(一)】_高高的胖子的博客-CSDN博客

=========================================================================

1 . 函数介绍(续)

(11). memcpy()函数:

从存储区str2拷贝n个字节的数据到存储区str1

函数返回值类型和相关参数:

void * memcpy ( void * destination, const void * source, size_t num );

(参数接收 任意类型的目标空间地址 任意类型的常量源数据地址拷贝的字节数

返回 任意类型的拷贝完成后的目标空间地址

注意事项:

(1).

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置

需要头文件

(2).

这个函数在遇到 ‘\0’ 的时候并不会停下来

(3).

如果sourcedestination有任何的重叠复制的结果都是未定义的

(4).

学会memcpy函数的模拟实现下面第2个模块有


(12). memmove()函数:

从存储区str2移动 n个字节的数据到存储区str1

函数返回值类型和相关参数:

void * memmove( void * destination, const void * source, size_t num );

(参数接收 任意类型的目标空间地址 任意类型的常量源数据地址移动的字节数

返回 任意类型的移动完成后的目标空间地址

注意事项:

(1).

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠

(2).

如果源空间目标空间出现重叠,就得使用memmove函数处理

(3).

学会memmove函数的模拟实现下面第2个模块有


(13). memcmp()函数:

比较从ptr1和ptr2指针开始的num个字节

函数返回值类型和相关参数:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

(参数接收 两个任意类型的常量地址比较的字节数

返回 一个整数

注意事项:

(1).标准规定:

第一个元素 大于 第二个元素,则返回 大于0 的数字

第一个元素 等于 第二个元素,则 返回0

第一个元素 小于 第二个元素,则返回 小于0 的数字

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2 . 库函数的模拟实现(续)

(6). 模拟实现memcpy()函数:

模拟实现的自定义函数:

主函数:

对应代码:

//模拟memcpy()函数:#include #include void* my_memcpy(void* dest, const void* src, size_t num)//返回值为 拷贝结束后的目标空间起始地址//size_t num:参数接收的是 要拷贝的字节数,因为不同类型的数据字节数是不同的{//保存目标空间的原始地址,方便拷贝完后返回原地址:void* ret = dest;//使用断言,确保两指针不是空指针:assert(dest && src);while (num--)//将两个void*指针,转化为char*指针,一个字节一个字节拷贝//所以有几个字节就拷贝几次{//转化为char*指针,一个字节一个字节拷贝://强制类型转化只是临时的:*(char*)dest = *(char*)src;//移动指针拷贝下一位://因为void*指针不能++和*,//所以要转化为char*后+1再赋给dest: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[20] = { 0 };//将arr1的内容拷贝到arr2中,因为是int类型不能使用strcpy函数://使用模拟的自定义函数:my_memcpy(arr2, arr1, 20);//结果:int i = 0;for (i = 0; i < 20; i++){printf("%d ", arr2[i]);}return 0;}


(7). 模拟实现memmove()函数:

模拟实现的自定义函数:

主函数:

对应代码:

//模拟实现memmove函数:#include #include void* my_memmove(void* dest, const void* src, size_t num)//目标空间原数据空间(首元素地址){//保存起始位置:void* ret = dest;//断言:assert(dest && src);//如果源数据空间在目标空间有重叠,则有2种情况://1. 目标空间首地址(dest) 在 源数据空间首地址左边(src) ,// 那就需要 从前往后 进行移动,才不会有源数据被覆盖的情况//2. 目标空间首地址(dest) 在 源数据空间首地址右边(src) ,// 那就需要 从后往前 进行移动,才不会有源数据被覆盖的情况//如果两个空间没有重叠的情况,则 从前往后 还是 从后往前 移动都可以//使用分两种情况即可://void* 不能解引用和+1-1操作,但可以比(地址)大小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;}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

3 . 练习

(1). 杨氏矩阵:

题目:

有一个数字矩阵二维数组),

矩阵的每行从左到右是递增的
矩阵从上到下是递增的
请编写程序在这样的矩阵中查找某个数字是否存在,

要求:时间复杂度小于O(N)

时间复杂度为O(N)的版本:

对应代码:

//正常写法,时间复杂度为O(N):#include int main(){//给出符合杨氏矩阵的二维数组:int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };//输入要找的数:int k = 0;scanf("%d", &k);//在二维数组中一个一个遍历:时间复杂度为O(N)int i = 0; for (i = 0; i < 3; i++)//遍历行{int j = 0;for (j = 0; j < 3; j++)//遍历列{if (arr[i][j] == k)//如果找到了{//进行打印:printf("找到了,它的下标为:%d %d", i, j);return 0; //直接使用return结束程序,//break只能跳出一层循环}}}//未找到则打印未找到:printf("未找到");return 0;}

时间复杂度小于O(N)的版本:

对应代码:

#include void young_tableau_search(int arr[3][3], int k, int* px, int* py){//开始查找,思路://因为是杨氏矩阵,所以一行中最右边的数是最大的//这个最大值如果比要找的值都小的话,那就可以排除这一行//列也是同理// 坐标(0,2),第一行的最大值int x = 0; //二维数组的行int y = *py-1; //二维数组的列while (x = 0)//行最大调整到第二行,列最多调整到第0行{if (arr[x][y]  k)//如果第一行的最大值大于k,{y--; //说明k可能就在这一行,移动列进行查找}else{//找到了就把k的行和列赋给指针px和指针py:*px = x;*py = y;return;}}//自定义未找到的情况:*px = -1;*py = -1;}int main(){//给出符合杨氏矩阵的二维数组:int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };//1 2 3//4 5 6//7 8 9//输入要找的数:int k = 0;scanf("%d", &k);int x = 3; //k的行int y = 3; //k的列//自定义一个函数进行查找:young_tableau_search(arr, k, &x, &y);//参数:二维数组名,要找的值,行数,列数//通过函数的使用情况打印相应情况:if (x==-1 && y==-1)//未找到:{printf("未找到");}else//找到了:{printf("找到了,它的下标为:第%d行 第%d列", x, y);}return 0;}


(2). 字符串左旋:

题目:

实现一个函数,可以左旋字符串中的k个字符


例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB

方法1:左旋一个字符就移动一次剩余的字符

对应代码:

//方法1:左旋一个字符就移动一次剩余的字符//实现一个函数,可以左旋字符串中的k个字符。//例如://ABCD左旋一个字符得到BCDA//ABCD左旋两个字符得到CDAB#include #include #include void left_move(char* str, int k){//断言:str不为空指针assert(str);//左旋k次:int j = 0;for (j = 0; j < k; j++){//存放被左旋字符的地址,从第一个字符开始char tmp = *str;//求字符串长度:int len = strlen(str);//左旋后一个字符后,后面len-1个字符要往前挪一位int i = 0;for (i = 0; i < len - 1; i++){//把后一个字符赋给前一个*(str + i) = *(str + i + 1);//这里就覆盖掉了前面一个字符,末尾空出一个字符}//将左旋的字符移动到尾部*(str + len - 1) = tmp;}}int main(){char arr[] = "ABCD";//输入左旋次数:int k = 0;scanf("%d", &k);//使用自定义函数进行左旋:left_move(arr, k);//参数: 字符串首地址,左旋次数//打印左旋后结果:printf("%s\n", arr);return 0;}

方法2:

把字符串分为两部分,左旋字符为一部分,剩余字符为一部分,分别进行逆序,再整体逆序

对应代码:

//方法2:#include #include #include //定义一个逆序函数:void reverse(char* left, char* right){//断言:两指针不为空assert(left && right);//进行逆序:while (left < right)//两指针中间还有数就继续{char tmp = *left;*left = *right;*right = tmp;left++;right--;}}void left_move(char* str, int k){int len = strlen(str);k %= len;//防止重复操作//求左旋字符进行逆序:reverse(str, str + k - 1);//对剩余字符进行逆序:reverse(str + k, str + len - 1);//整体逆序:reverse(str, str + len - 1);}int main(){char arr[] = "ABCD";//输入左旋次数:int k = 0;scanf("%d", &k);//使用自定义函数进行左旋:left_move(arr, k);//参数: 字符串首地址,左旋次数//打印左旋后结果:printf("%s\n", arr);return 0;}

(3). 字符串旋转结果:

题目:

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串

例如:

给定s1 =AABCDs2 = BCDAA返回1

给定s1=abcds2=ACBD返回0.

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

方法1:

str1每旋转一次就和str2进行对比,不同则继续进行下次旋转

对应代码:

#include #include int is_left_move(char* str1, char* str2){//求字符串长度:int len = strlen(str1);int j = 0;for (j = 0; j < len; j++)//字符串长度为几,最多就旋转几次{//存放被左旋字符的地址,从第一个字符开始char tmp = *str1;//左旋后一个字符后,后面len-1个字符要往前挪一位int i = 0;for (i = 0; i < len - 1; i++){//把后一个字符赋给前一个*(str1 + i) = *(str1 + i + 1);//这里就覆盖掉了前面一个字符,末尾空出一个字符}//将左旋的字符移动到尾部*(str1 + len - 1) = tmp;//到这里就旋转了1次,//每旋转1次就使用strcmp判断是否和str2相同:if (strcmp(str1, str2) == 0){return 1;//找到返回1}}//未找到就返回0return 0;}int main(){char arr1[] = "ABCDEF";char arr2[] = "CDEFAB";//使用自定义函数:int ret = is_left_move(arr1, arr2);//根据自定义函数返回结果进行打印:if (ret == 1){printf("Yes\n");}else{printf("No\n");}return 0;}

方法2:

把str1变成 “str1+str1”,如果“str1+str1” 中有str2,那就可以通过str1旋转得到str2

对应代码:

#include #include int is_left_move(char* str1, char* str2){int len1 = strlen(str1);int len2 = strlen(str2);//判断两字符串长度是否相同,不同的话肯定不能通过旋转得到:if (len1 != len2){return 0;}//通过strncat在str1上再追加str1:strncat(str1, str1, len1);//判断追加后的str1中有没有str2,有则可以通过旋转得到:if (strstr(str1, str2) == NULL){return 0;}else{return 1;}}int main(){char arr1[20] = "ABCDEF";char arr2[] = "CDEFAB";//使用自定义函数:int ret = is_left_move(arr1, arr2);//根据自定义函数返回结果进行打印:if (ret == 1){printf("Yes\n");}else{printf("No\n");}return 0;}