第四章函数和递归
自定义函数和结构体
- 执行函数遇到了
return
语句会直接推出这个函数。但是如果,没有return
语句,则会返回一个不确定的值。 - 算法竞赛中,
main()
函数的返回值总是0。 - 一个函数也可以调用其他的函数
- 即使最终答案在所选择的数据类型范围之内,计算的中间结果仍然有可能溢出。
- 题目“暗示”的方法要好好思量,有可能有坑,所以这时候要深入的分析题目,不能贸然行事。看似简单的题目才是最为致命的题目。
- 对复杂的表达式尝试着进行化简,减少计算量,能有更大几率的避免中间结果有溢出。
- 谓词 :判断一个事务是否具有某一性质的函数。最好将谓词命名为
is_xxx
的形式,一目了然。 - 对于编写的函数,尽量做到能够正确的计算所有的合法参数 ,如果函数有缺陷,一定要清楚的标明,避免误用。
函数的调用与参数的传递
-
注意作用域,A函数的形参和局部变量不能访问B函数的局部变量。另外要注意的是,传值和传递值这两个的区别。
-
调用栈 :C语言中用来描述函数之间的调用关系。调用栈的理解对今后的学习和编程至关重要!
-
一个字节的地址称为变量的地址,C语言的变量都是放到内存中的,每一个字节都有一个称为地址的编号。
-
*a
是a
指向的变量,而不是a
指向变量所拥有的值。所以(*a)++
和*a = *a+1
他们表示的意思是a
所指向的变量自增1。 -
*
的优先级低于++
所以,*a++
和(*a)++
所表达的意思是完全不一样的。 -
不要滥用指针,易造成混乱。
-
当数组作为参数传递给函数时 : 将数组的首地址和数组的大小传入函数即可;例如:
int sum(int *a ,int n)
其中,a
是数组的首地址,n是数组的大小。 -
除了把数组的首地址作为实参外,还可以利用指针加减法把其他元素的首地址传递给函数。
a++
则表示,此时a
指针指向了a[1]
。 -
对于处理数组的函数来说,如果要从a[0]运算到a[n],最长见的方法就是将数组的首地址和尾地址作为参数传入函数。例如:
//计算数组的元素和 //解法一: int sum(int* begin, int* end){ int n = end - begin; int ans = 0; for(int i = 0; i < n; i++){ ans += begin[i]; } return ans; } //解法二,推荐使用且最普遍使用的写法 int sum (int* begin,int* end){ int *p = begin; int ans = 0; for(int *p = begin ; p != end;p++){ ans += *p; } }
-
将数组作为指针传入函数,数组的内容是可以修改的
把函数作为函数的参数
- 排序问题就是将函数作为函数的参数传递的一个非常典型的应用!
- 指向常数的“万能”指针:
const void *
它可以通过强制类型转换变成任意类型的指针。 stdlib.h
中的qsort
函数就是一个将函数作为参数传递的例子
void qsort(void * base , size_t num,size_t size , int (*comparator)(const void *,const void * ));
对应的是待排数组的首地址、元素个数、每个元素的大小和一个指向函数的指针。- 在算法竞赛中一般不使用
qsort
函数,而是使用C++中的sort
函数
递归
- 函数自己调用自己即递归。
- 编写递归函数的时候要注意避免写出无限递归函数,即,要写出递归函数的终止条件。
- C语言中,调用自己和调用其他函数并没有本质上的区别。
- “段”(segmentation) :“段”是指二进制文件的区域,所有某种特定类型信息被保存在里面。可以用
size
程序得到可执行文件中各个段的大小。 - “正文段”用于储存指令,“数据段”用于储存已经初始化的全局变量,“BSS段”用于储存未赋值的全局变量所需的空间。
- 调用栈所在的段则称之为“堆栈段”,同样,它也有自己的大小,不能被越界访问,如果越界,则会出现段错误。
竞赛题目例题笔记
- 自项向下编程即编写程序时,先写出框架,再写细节。
- 自底向上编程即编写程序时,先写函数,再写主程序。
- 自项向下编程对于编写复杂的软件来说有着独特的优势,但是,在竞赛中,采用自底向上的编程方式更加常用。
- 尽量减少全局变量的使用;变量名的选取不能和库函数相冲突。
练习题还没开始刷,捣鼓java大作业中