学习目标:
进一步认识C语言
学习内容:
1:函数的声明:
(a):告诉编译器有一个函数叫什么;参数是什么(且给出类型);返回值是什么(且给出类型)
(b):声明一般在使用之前,要满足先声明后使用
(c):声明一般在头文件中
(d):如果一个函数定义在了调用之后,则必须声明。但一般不会这样。
工程惯例:(多人协作方便;便于封装和隐藏,封装一般卖静态库.lib文件)
①:在头文件夹内建立头文件
②:源文件夹内建立函数的定义,及函数测试
③:测试函数,此时要包含上述头文件
上述两个头文件,编译结束后,进行链接,测试函数则会根据已包含的头文件在另一个定义函数的源文件内找函数定义。
2:函数的递归(“我调我自己”。目的:大事化小)
如:接受一个无符号整形,按顺序打印它的每一位:
#include <stdio.h>
void print(unsigned int x)
{
if (x > 9)
{
print(x / 10);
}
printf("%d ", x % 10);
}
int main()
{
int i = 1234;//被打印的数
print(i);
return 0;
}
上述案例可看出函数递归的两个必要条件:
①:存在限制条件,满足条件才可递归,不满足时递归结束
②:每次递归后就进一步接近限制条件
再之:每一次函数调用都会在内存中开辟空间,包括main()函数,如果不加限制条件,会可能导致栈溢出,栈里一般放局部变量和形式参数,函数调用也会在栈中占空间,此空间有限。
练习:编写函数,要求不创建临时变量,可求出字符串长度
int my_strlen(char* s)
{
if (*s != '\0')
{
return 1 + my_strlen(s + 1);
}
else
{
return 0;
}
}
int main()
{
char arr[] = "abcdef";
int ret=my_strlen(arr);
printf("%d", ret);
return 0;
}
note:字符指针加一,地址向后跳1个字节;整型指针加一,地址向后跳4个字节,其他类似。
3:函数的迭代(循环)与递归(“我调我自己”)
举一个区别:如两种方法求n的阶层:
迭代:
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
int ret = 1;
printf("请输入一个正整数\n");
scanf("%d", &i);
for (j = 1; j <= i; j++)
{
ret = ret * j;
}
printf("该数的阶乘为:%d\n", ret);
}
递归:
#include <stdio.h>
int fac(int x)
{
if (x >= 2)
{
return x * fac(x - 1);
}
if (x == 1)
{
return 1;
}
}
int main()
{
int i = 0;
printf("请输入一个正整数:>");
scanf("%d", &i);
int ret = fac(i);
printf("该数的阶乘为:%d\n", ret);
return 0;
}
练习:1,求第n个斐波那契数:(斐波那契数:后一个数为前两个数之和,前两位数为1)
法一:
#include <stdio.h>
int fib(int x)
{
if (x <= 2)
{
return 1;
}
else
{
return fib(x - 1) + fib(x - 2);//位次高了的话,会造成大量的重复计算!
}
}
int main()
{
int i = 0;
printf("请输入想找到的对应位次的斐波那契数:>");
scanf("%d", &i);
int ret=fib(i);
printf("%d\n", ret);
return 0;
}
法二:
int fib(int x)
{
int a = 1;
int b = 1;
int c = 1;
while (x > 2)
{
c = a + b;
a = b;
b = c;
x--;
}
return b;
}
int main()
{
int i = 0;
printf("请输入想查找的对应位次的斐波那契数:>");
scanf("%d", &i);
int ret = fib(i);
printf("%d\n", ret);
return 0;
}
conclusion:递归虽好,但并不一定是最好的!
4:getchar():int getchar(void);
①:返回值为什么是整型:因为它返回的是读取字符的ASCII值!
②:getchar()在读取(文件)结束或者失败的时候会返回EOF(-1)。
③:getchar目前最常用的地方在哪:清空缓冲区
举例:模拟输入密码并确认
#include <stdio.h>
#include <stdio.h>
int main()
{
char password[20] = { 0 };
int ret = 0;
printf("请输入密码,并按回车结束输入:>");
scanf("%s", password);//password起存,存至空格、回车、Tab键
printf("请确认密码:(Y/N):>");
while (getchar() != '\n')//清空缓冲区,空语句
{
;
}
if ((ret = getchar()) != 'Y')
{
printf("确认失败\n");
}
else
printf("确认成功\n");
}
note:
①:%s:作为输入控制符,在缓冲区内只能一次读字符,遇到空格、回车、Tab键都跳不过去,所以一旦出现这类情况,即使它们后面还有字符,%s也读不到!
②:%d:上述情况可跳过,被读取或者跳过的地方都会被释放(从缓冲区)。
③:getchar()什么都拿!
④:字符作逻辑判断使用的都是ASCII值!
⑤:scanf()取字符串放进字符串数组中时,后面的‘\0’也存于数组中了!
练习:写一串代码:实现:输入字符中的0-9数字都依次被打印出来
#include <stdio.h>
int main()
{
int ch = 0;
while ((ch = getchar()) != '\n')
{
if (ch < '0' || ch>'9')
{
continue;
}
else
{
putchar(ch);
}
}
return 0;
}
练习:字符串逆序,递归实现:
非递归:(错误示范)
#include <stdio.h>
void reverse_string(char* s,int x)
{
int left = 0;
int right = x - 2;//调试发现为18,而main()函数中的arr未被初始化的位置都为0,下述交换完是不会打印的,因为%s碰到数字就停了!
while (left < right)
{
char tem = 0;
tem = s[left];
s[left] = s[right];
s[right] = tem;
left++;
right--;
}
}
int main()
{
char arr[20] = "abcdef";//研究对象
int sz = sizeof(arr) / sizeof(arr[0]);
reverse_string(arr,sz);
printf("%s\n", arr);
return 0;
}
正确非递归:
#include <stdio.h>
#include <string.h>
void reverse_string(char* s)
{
int len = strlen(s);//strlen会读出s开始至'\0'的字符个数
int left = 0;
int right = len - 1;
while (left < right)
{
char tem = 0;
tem = s[left];
s[left] = s[right];
s[right] = tem;
left++;
right--;
}
}
int main()
{
char arr[20] = "abcdef";
reverse_string(arr);
printf("%s\n", arr);
return 0;
}
递归:
#include <stdio.h>
#include <string.h>
void reverse_string(char* s)
{
int len = strlen(s);
char tem = s[0];
s[0] = s[len - 1];
s[len - 1] = '\0';
if (strlen(s + 1) >= 2)
{
reverse_string(s + 1);
}
s[len - 1] = tem;
}
int main()
{
char arr[20] = "abcdef";
reverse_string(arr);
printf("%s\n", arr);
return 0;
}
练习:计算一个无符号整形数字的各位之和(递归实现)
#include <stdio.h>
int s(int x)
{
if (x > 9)
{
return x % 10 + s(x / 10);
}
else
return x;
}
int main()
{
int i = 0;
scanf("%d", &i);
int ret = s(i);
printf("%d\n", ret);
return 0;
}
练习:递归实现n的k次方
#include <stdio.h>
double s(int x, int y)
{
if (y > 0)
{
return x * s(x, y - 1);
}
else if (y == 0)
{
return 1;
}
else
return 1.0 / s(x,-y);
}
int main()
{
int i = 0;
int j = 0;
scanf("%d %d", &i, &j);
double ret = s(i, j);
printf("一数的二数次方为:%lf\n", ret);
return 0;
}
附上几个小知识点:
①:全局变量的字符数组只指定大小,不初始化,默认都为0
②:局部变量的字符数组只指定大小,不初始化,默认为随机数
③:数组中元素地址连续存放,元素类型不同,可能对应间隔的元素地址相差的字节数不同。联想成胖瘦,不管胖一点还是瘦一点,大家都是挨着坐的!
④:数组下标变大是随着地址由低到高变化的。
学习时间:
2021.9