好了,值此新春佳节,我先给各位看官拜年啦!
过去一年,大家都在各自的人生轨道上奋力前行,尝遍酸甜苦辣,好在我们都站在了新一年的起点。
新岁已至,愿各位往后的日子,身体健健康康,无病无灾,纵遇风雨,也能从容应对;
万事皆能顺遂心意,梦想逐一实现,所有的期待都不会落空,奔赴充满希望的未来 。
先补充一下上期内容的彩蛋。内存函数和字符函数的补充。
内存函数
1. memcpy使⽤和模拟实现
memcpy
- 使用: memcpy 函数用于从源内存地址复制指定字节数的数据到目标内存地址,通常用于在不同的内存区域之间进行数据复制,比如数组之间、结构体之间等。其函数原型为 void *memcpy(void *dest, const void *src, size_t n) ,其中 dest 是目标内存地址, src 是源内存地址, n 是要复制的字节数。
c
#include <stdio.h>
#include <string.h>
int main()
{
int sourceArray[] = { 1,2,3,4,5 };
int destinationArray[5];
memcpy(destinationArray, sourceArray, sizeof(sourceArray));
for (int i = 0; i < 5; i++)
{
printf("%d ", destinationArray[i]);
}
return 0;
}
结果是1 2 3 4 5
- 模拟实现
c
#include <stdio.h>
#include <string.h>
void* my_memcpy(void* dest,const void *src,size_t n)
{
// 对空指针进行处理
if (dest == NULL || src == NULL)
{
return NULL;
}
// 进行强制类型转换,方便按字节操作
char* d = (char*)dest;
const char* s = (const char*)src;
// 循环复制每个字节
while (n--)
{
*d++ = *s++;
}
// 返回目标内存地址
return dest;
}
int main() {
char src[] = "Hello, World!";
char dest[20];
// 使用自定义的my_memcpy函数
my_memcpy(dest, src, strlen(src) + 1);
printf("复制后的字符串: %s\n", dest);
return 0;
}
结果是 复制后的字符串:Hello,World!
size_t 是无符号类型,这意味着它只能表示非负整数,取值范围从0开始,最大值取决于具体的编译器和操作系统平台。
用途
- 数组操作:在访问数组元素或计算数组大小时经常使用 size_t 。比如定义一个函数来获取数组的元素个数,函数返回值类型可以是 size_t 。
- 字符串处理:在处理字符串时, size_t 常用于表示字符串的长度。例如,C标准库中的 strlen 函数返回的就是 size_t 类型,表示字符串的字符个数(不包括字符串结束符 \0 )。
- 内存管理:在动态内存分配函数如 malloc 、 calloc 等中,参数通常使用 size_t 来指定要分配的内存字节数。
2. memmove使⽤
memmove
- 使用: memmove 函数和 memcpy 功能相似,但它可以正确处理源内存区域和目标内存区域重叠的情况。其函数原型为 void *memmove(void *dest, const void *src, size_t n) 。
c
#include <stdio.h>
#include <string.h>
int main()
{
char str[15] = "abcdefg";
memmove(str + 3, str + 2, 5); // 将 str[2] 开始的 5 个字符移动到 str[3] 开始的位置(包括str[2])
str[8] = '\0'; // 确保字符串的结束符放在正确的位置
printf("%s\n", str);
return 0;
}
结果是 abccdefg
#include <string.h>
int main()
{
char str[] = "abcdefg";
// 源和目标内存区域有重叠,将str从第4个字符开始的4个字符向前移动2个位置
memmove(str + 2, str + 4, 2);
printf("%s\n", str);
return 0;
}
结果是 abefefg
3. memset函数的使⽤
- 使用: memset 用于将一段内存区域设置为指定的值。
c
#include <stdio.h>
#include <string.h>
int main()
{
char str[10];
memset(str, 'A', sizeof(str));
printf("Filled string: %s\n", str);
return 0;
}
4. memcmp函数的使⽤
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello";
char str2[] = "Hello World";
int result = memcmp(str1, str2, 5);
if (result == 0)
{
printf("The first 5 bytes are equal\n");
}
return 0;
}
结果是 The first 5 bytes are equal
字符函数的补充
以下是 strncpy 、 strncat 、 strncmp 函数的使用示例:
strncpy函数
strncpy 函数用于将一个字符串的指定长度的字符复制到另一个字符串中。
c
#include <stdio.h>
#include <string.h>
int main()
{
char src[] = "Hello, World";
char dest[20];
// 将src字符串的前5个字符复制到dest中
strncpy(dest, src, 5);
dest[5] = '\0';
printf("Copied string: %s\n", dest);
return 0;
}
strncat函数
strncat 函数用于将一个字符串的指定长度的字符追加到另一个字符串的末尾。
c
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20] = "Hello";
char src[] = ", World";
// 将src字符串的前6个字符追加到dest中
strncat(dest, src, 6);
printf("Concatenated string: %s\n", dest);
return 0;
}
strncmp函数
strncmp 函数用于比较两个字符串的前 n 个字符。
c
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "apple";
char str2[] = "app";
// 比较str1和str2的前3个字符
int result = strncmp(str1, str2, 3);
if (result == 0)
{
printf("The first 3 characters are equal\n");
}
else if (result < 0)
{
printf("str1 is less than str2\n");
}
else
{
printf("str1 is greater than str2\n");
}
return 0;
}
在 strncpy 、 strncat 、 strncmp 函数中,参数 n 是有一定限制的:
strncpy
- n 应大于等于0。若 n 为0,则不进行复制操作。实际使用中,要确保 n 不超过目标字符串的可容纳长度,否则会导致缓冲区溢出,引发未定义行为,比如程序崩溃或数据被破坏。
- 若源字符串长度小于 n , strncpy 会在目标字符串后补零,直到复制满 n 个字符。
strncat
- n 同样应大于等于0,为0时不进行追加操作。
- 要保证 n 加上目标字符串已有的长度不超过目标字符串的总长度,否则会超出目标字符串边界,造成缓冲区溢出等问题。
- 若源字符串长度大于 n ,则只追加 n 个字符,不会自动在追加内容后补零。
strncmp
- n 需大于等于0,为0时函数总是返回0。
- 通常 n 应小于等于两个字符串中较短字符串的长度,若 n 大于较短字符串长度,函数会在到达较短字符串末尾后,继续比较剩余字符,将未比较部分视为'\0'。
函数递归是什么呢
简单来说就是函数自己调用自己。其中递归中的递就是递推的意思,归就是回归的意思
思想就是大事化小。
递归的限制条件
递归在书写的时候,有2个必要条件:
•递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。
•每次递归调⽤之后越来越接近这个限制条件。
递归是一种编程技巧,在解决问题时,将一个大问题分解为一个或多个相似的小问题,然后通过不断地调用自身来解决这些小问题,直到达到终止条件。
求n的阶乘(注意这里的n是正整数)
公式:n!=n*(n-1)!
然后呢,比如说求5的阶乘
递推先
5! = 5 * 4! n * Divide(n - 1)
4! = 4 * 3! n * Divide(n - 1)
3! = 3 * 2! n * Divide(n - 1)
2! = 2 * 1! n * Divide(n - 1)
1! = 1 * 0! n * Divide(n - 1)
注意这里的n是正整数,所以这个可以为限制条件即,0!返回值为1(return 1)
所以开始回归
1! = 1 return 1
2! = 2 return 2
3! = 6 return 6
4! = 24 return 24
5! = 120 return 120
#include<stdio.h>
int Factorial(int n)
{
if (n == 0)
{
return 1;
}
else
{
return n * Factorial(n - 1);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
int result = Factorial(n);
printf("%d\n", result);
}
打印⼀个整数的每⼀位
输⼊⼀个整数m,按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234 输出:1 2 3 4
输⼊:520 输出:5 2 0
如果是个位就直接打印,对吧,可如果是两位数,三位数…呢,那就大事化小,转化成个位处理,如何实现呢,每次%10可以得到最后一位,那递是顺序,归是逆序,不妨利用归得到4 3 2 1,结果自然是1 2 3 4。
以1234为例
递推先
123+4
12+3+4
1+2+3+4
通过printf("%d",n%10);解决打印
通过n/10,实现1234→123→12→1
自己调用自己四次,直到是个位,限制条件出来了,n≥10,n<10的话就打印(区分Print 和 printf)
1234 Print(123)
123 Print(12)
12 Print(1)
打印1(限制条件)
开始回归
然后- 想象你要爬一段有很多层的楼梯,每层楼梯都有一个任务要完成(每层楼梯对应一次递归调用)。当你爬到最顶层(到达递归终止条件),完成顶层的任务后,你需要按原路返回,依次完成每一层楼梯剩下的任务。
所以回到12,由于不用继续递归了,执行打印语句,得到2,类似的得3,4完成了
void Print(int n)
{
if (n >= 10)
{
Print(n / 10);
}
printf("%d", n % 10);
}
int main()
{
int n = 0;
scanf("%d",&n);
Print(n);
}
以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题。所以如果不想使⽤递归,就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)。
计算 n 的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的。
int Factorial(int n)
{
int result = 1;
for (int i = 1; i <= n; i++)
{
result *= i;
}
return result;
}
int main()
{
int n = 0;
scanf("%d", &n);
int result = Factorial(n);
printf("%d\n", result);
}