1、了解命令行参数
2、深入理解递归
C语言的命令行参数是指在程序运行时,通过命令行传递给程序的额外信息。当我们在终端或命令提示符中执行一个C语言程序时,可以在程序名后面添加参数,这些参数将被传递给程序并在程序内部使用。
在C语言中,命令行参数主要通过
main
函数的参数来接收。main函数也是一个函数,其实也可以携带参数的,标准的main
函数定义如下:
main
函数的参数包括argc
和argv
,分别表示命令行参数的数量和参数字符串数组。
argc
(argument count)是一个整数,表示命令行参数的数量,包括程序名称本身。至少会有一个参数,即程序的名称。argv
(argument vector)是一个指向指针的数组,每个指针指向一个以空字符结尾的字符串,表示一个命令行参数。例如,执行以下命令行:
在程序内部,
argc
的值将是3,argv
数组将包含3个字符串指针:
argv[0]
指向字符串"./myprogram"
,表示程序的名称。argv[1]
指向字符串"arg1"
,表示第一个命令行参数。argv[2]
指向字符串"arg2"
,表示第二个命令行参数。通过访问
argc
和argv
,程序可以根据命令行参数的数量和内容,进行相应的逻辑处理。常见的用法包括:
- 读取命令行参数的值,可以使用
argv
数组的索引来访问特定位置的参数字符串。- 对命令行参数进行解析和处理,例如将字符串转换为数字、进行条件判断等。
- 根据命令行参数的值来执行不同的程序逻辑,例如根据参数选择不同的操作或配置。
总之,命令行参数为C语言程序提供了一种从外部传递信息的方式,使得程序在运行时可以根据不同的参数执行不同的操作。
#include
int main(char argc, char* argv[])
{
int i = 0;
for (i = 0; i < argc; i++)
{
printf("%s", argv[i]);
}
}
这样我们就给程序带上了命令行参数。其实呢,
main
函数是有三个参数的,只不过第三个参数通常不写。
第一个参数: argc 是个整型变量,表示命令行参数的个数(含第一个参数)。
第二个参数: argv 是个字符指针的数组,每个元素是一个字符指针,指向一个字符串。这些字符串就是命令行中的每一个参数(字符串)。
第三个参数: envp 是个字符指针的数组,数组的每一个元素是一个指向一个环境变量(字符串)的字符指针
#include
int main(int argc, char* argv[], char* envp[])
{
int i = 0;
while (envp[i] != NULL)
{
printf("%s\n", envp[i]);
i++;
}
return 0;
}
基本认识
- 递归本质也是函数调用,
- 函数调用本质就要形成和释放栈帧
- 根据栈帧的学习,调用函数是有成本的,这个成本就体现在形成和释放栈帧上:时间+空间
- 所以,递归就是不断形成栈帧的过程
理论认识
- 内存和CPU的资源是有限的,也就决定了,合理的递归是绝对不能无限递归下去,合法递归一定是有次数限制的
- 递归不是什么时候都能用,而是要满足自身的应用场景 即:目标问题的子问题,也可以采用相同的算法解决,本质就是分治的思想
- 核心思想:大事化小+递归出口
#include
/*
abcd1234
1+bcd1234
1+1+cd1234
...
*/
int my_strlen(const char* arr)
{
if (!*arr)
return 0;//递归出口
else
return 1 + my_strlen(arr + 1);//大事化小
}
int main()
{
char arr[] = "abcd1234";
printf("%d\n", my_strlen(arr));
return 0;
}
#include
#include
int my_strlen(const char* s)
{
assert(s != NULL);
return *s ? (MyStrlen(++s) + 1) : 0;
}
int main()
{
const char* str = "abcd1234";
int len = my_strlen(str);
printf("len: %d\n", len);
return 0;
}
功能上,代码增加了一个断言,用于确保传入
my_strlen
函数的指针不为空。断言是一种在代码中放置的检查,用于在运行时验证某个条件是否为真。在这里,assert(s)
用于验证传入的指针s
不为空(即不能为NULL)。如果断言条件为假,程序会终止,并输出错误信息。这样可以帮助在调试时快速发现可能出现的问题。
摘自百科
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、 34、……在数学上,斐波那契数列以如下被以递推的方法定义:
F(0)=0,
F(1)=1,
F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
#include
int Fib(int n)
{
if (1 == n || 2 == n) {
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n = 42;
int x = Fib(n);
printf("Fib(%d): %d\n", n, x);
return 0;
}
通过上面的代码做实验我们发现,fib递归版在个数超过几十(我们这里是42)的时候,就已经相当慢了。
#include
#include
//包含该头文件,才能使用win提供的GetTickCount()函数,\
来获取开机到现在的累计时间,此处用它,是因为简单
int Fib(int n)
{
if (1 == n || 2 == n) {
return 1;
}
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n = 42;
double start = GetTickCount();
int x = Fib(n);
double end = GetTickCount();
printf("Fib(%d): %d\n", n, x);
printf("%lf s\n", (end - start) / 1000);
return 0;
}
- 如果数字再大,就非常非常慢
- 为何会这么慢呢?根本原因是因为大量的重复计算
#include
#include
int count = 0;
int Fib(int n)
{
if (1 == n || 2 == n) {
return 1;
}
if (n == 3) {
count++; //不影响运算逻辑,就单纯想统计一下n=3的时候,被重复计算了多少次
}
return Fib(n - 1) + Fib(n - 2);
}
int main()
{
int n = 42;
double start = GetTickCount();
int x = Fib(n);
double end = GetTickCount();
printf("fib(%d): %d\n", n, x);
printf("%lf s\n", (end - start) / 1000);
printf("count = %d\n", count);
return 0;
}
为何会这样??重复计算,意味着重复调用,重复调用意味着重复形成栈帧,创建与释放栈帧,是有成本的。
#include
#include
int Fib(int n)
{
int* dp = (int*)malloc(sizeof(int) * (n + 1)); //暂时不做返回值判定了
//[0]不用(当然,也可以用,不过这里我们从1,1开始,为了后续方便)
if (dp == NULL)
return -1;
dp[1] = 1;//第一个数
dp[2] = 1;//第二个数
for (int i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
int ret = dp[n];
free(dp);
return ret;
}
int main()
{
int n = 42;
double start = GetTickCount();
int x = Fib(n);
double end = GetTickCount();
printf("fib(%d): %d\n", n, x);
printf("%lf ms\n", (end - start) / 1000);
return 0;
}
运行所需时间接近0.0s,只形成一次函数栈帧,消耗的成本非常低。这其实是一种动态规划的算法哦,看起来很神秘,其实这些问题也可以使用改方法解决哦。
#include
#include
int Fib(int n)
{
int first = 1;
int second = 1;
int third = 1;
while (n > 2) {
third = second + first;
first = second;
second = third;
n--;
}
return third;
}
int main()
{
int n = 42;
double start = GetTickCount();
int x = Fib(n);
double end = GetTickCount();
printf("fib(%d): %d\n", n, x);
printf("%lf s\n", (end - start) / 1000);
return 0;
}
这种方法就是一种典型的迭代方法,空间和时间上消耗的成本都非常低。