目录
1,函数
什么是函数?函数(function)是完成特定任务的独立程序代码单元。
函数组成:修饰符(可以不带),返回类型,函数名,参数列表,{ },返回值(当返回类型为void时,表示无返回值)。
举例:
static int get_max(int a, int b ) //对于get_max()来讲,a,b为形式参数
{
if (a > b)
return a;
else
return b;
}
static --- 修饰符
int --- 返回类型
get_max --- 函数名
(int a,int b) --- 参数列表
{ }内的是函数体,内部的return 是返回值。
1,实参,形参
函数在调用的时候,实参传给形参,其实实参是形参的一份临时拷贝,改变形参不能改变实参
static int get_max(int a, int b ) //对于get_max()来讲,a,b为形式参数
{
if (a > b)
return a;
else
return b;
}
int main()
{
int x = 0, y = 0; //在这时,对于get_max()来讲,x,y为实际参数
scanf("%d %d", &x, &y);
int i = get_max(x, y);
printf("%d", i);
return 0;
}
在main()函数中,x和y是实际参数。在get_max中,a和b是形式参数。
2,传值调用,传址调用
函数的参数列表可以既可以传递变量也可以传递指针。
区别是:
传值调用:当传递变量的时候,函数形参会在内存中复制一份实参的数值给形参,不会对原有数值造成影响。
传址调用:函数的参数列表内是指针,指向变量地址,在函数内可以直接通过指针解引用得到实参,可以在这个基础上对参数进行修改。
示例:
void exchange1(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
void exchange2(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 1, y = 2;
printf("x = %d y = %d\n", x, y);
exchange1(x, y);
printf("x = %d y = %d\n", x, y);
exchange2(&x, &y);
printf("x = %d y = %d\n", x, y);
return 0;
}
输出:
x = 1 y = 2
x = 1 y = 2
x = 2 y = 1
可以看到,第一次传值调用,并不会交换x,y的值;第二次传址可以成功交换
优缺点:
1,传址调用有可能会更改实参的值,传值不会修改;当在需要修改实参的时候,必须使用传址;为了防止在不需要的时候改变实参的值,我们可以在指针前加上const。
2,传址调用不需要复制一份实参,占用内存比传值调用低,效率更高。
综上:一般来讲,使用传址调用优势更加明显,当不需要更改实参时,记得加const。
3,函数定义和声明
示例:函数声明
void fun(void);
示例:函数定义
void fun(void)
{
;
}
函数定义在使用之前,不会报错,假如使用在定义之前,则需要加上函数声明,否则会报错。
函数在使用之前要么声明要么定义。
函数定义是一种更强的声明。
4,递归
递归的两个必要条件:
1,存在限制条件,当满足这个限制条件后。递归便不再继续
2,每次递归调用后,会越来越接近这个极限
递归出错的原因:
1,不满足两个条件;2,递归次数太多,栈溢出
简单介绍一下内存
我们需要知道内存布局是怎样的:
内存布局分为栈区;堆区;静态区
栈区:负责存储局部变量、函数形参,调用函数时的返回值等临时变量
在函数结束时,这些局部变量就会被销毁
堆区:动态内存分配的malloc/free;calloc;realloc;
直到程序结束,或者手动释放(free())才会释放内存空间
静态区:存储全局变量;静态变量
直到程序结束,自动释放。
每一次函数(包括main函数)调用,都要在栈区内存开辟一份空间,调用完毕,释放该内存空间,所以使用递归是需要做到两点:
1,不能死递归,必须有跳出条件,每次递归逼近跳出条件
2,递归层次不能太深
递归举例:递归计算从1加到N
//递归计算从1加到N
int cal(int n)
{
if (n == 1)
{
return 1;
}
return n+cal(n - 1);
}
int main()
{
int i = 100;
printf("%d\n", cal(i));
return 0;
}
输出:
5050
不适合递归的举例:计算第N个斐波那契数 -- 1;1;2;3;5;8;13;21;34;55....
int fib(n)
{
if (n <= 2)
{
return 1;
}
else
{
return fib(n - 1) + fib(n - 2);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
int z = fib(n);
printf("%d", z);
return 0;
}
输出:当计算第十个数时,很快就有结果了
10
55
当计算第50个数的时候,等很久都没有结果
50
这是因为
返回值是两个数值函数,这回导致计算量指数上升,当计算第50个斐波那契数时,计算量接近2^50次。
当递归的方式,计算次数太多时,效率太低,用循环(迭代)效率比较高
修改代码:
int fib(n)
{
int a = 1, b = 1, sum = 1;
while (n > 2)
{
sum = a + b;
a = b;
b = sum;
n--;
}
return sum;
}
int main()
{
int n = 0;
scanf("%d", &n);
int z = fib(n);
printf("%d", z);
return 0;
}
输出:
46
1836311903
5,一些代码题
1,将字符串反序输出(迭代)
void reverse_string(char * str)
{
int len = strlen(str);
int temp = *str;
*str = *(str + len - 1);
*(str + len - 1) = '\0';
if (len > 3)
{
reverse_string(str + 1);
}
*(str + len - 1) = temp;
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s", arr);
return 0;
}
输出:
fedcba
2,sizeof()操作符
sizeof(s= 2+5)
sizeof内部的表达式不参与运算
sizeof在编译期间运行,内部的表达式不会执行,程序执行的时候,sizeof直接计算好结果,直接输出结果4了,并不会执行内部的语句
int main()
{
int a = 1, b = 2, c = 3;
char i = 1;
printf("%zd", sizeof(i = a + b));
printf("%d", sizeof(-i));
return 0;
}
输出:
14
sizeof在计算字符串(字符数组)长度时,会计算“\0”; 但是strlen()在计算字符串长度是,不会计算“\0”
strlen() 函数 --- 计算字符串长度,寻找\0前出现的字符个数
sizeof 操作符--- 计算(变量/类型)所占空间大小,单位是字节
3,两个字符串比大小:
char A={"abc"}
char B={'a','b','c'}
A比B长,但是strlen的计算结果会出错,造成B看起来比A长
int main()
{
char A[] = { "abc" };
char B[] = { 'a','b','c' };
char C[] = { 'a','b','c',"\0" };
printf("%d\n", sizeof(A));
printf("%d\n", sizeof(B));
printf("%d\n", sizeof(C));
printf("%d\