函数
函数声明
函数的声明就是告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。函数的声明要出现在函数使用前。声明格式如下。
返回类型 函数名(参数类型 参数名,参数类型 参数名,...);
函数定义
函数的定义就是交代函数的具体实现。在实现一个函数时,可以考虑使用C库提供的函数,也可以自己实现具体功能。函数定义可以理解为对函数声明的具体实现。定义的格式如下。
返回类型 函数名(参数类型 参数名,参数类型 参数名,...)
{
...
doSomething
...
}
函数调用
函数嵌套调用
函数可以嵌套调用,但不可以嵌套定义。最简单的入门程序helloword就是一个函数嵌套调用的例子。
int main()
{
printf("hello world");
return 0;
}
main函数内部嵌套调用了printf函数。
函数链式访问
链式访问就是把一个函数的返回值当作另一个函数的参数。
printf("%d",strlen("hello world"));
strlen函数返回"hello world"的长度,这个长度作为printf函数的参数使用。这样做可以少定义一个变量,节省内存空间。
函数递归
函数调用自身就叫做函数递归。递归的思想在于“大事化小”。递归存在两个必要条件:
- 函数必须有终止条件;
- 每次调用自身后,更加接近这个终止条件。
经典的一个递归例子就是斐波那契数列。下面代码是用递归方式实现斐波那契数列。
int fib(n)
{
if (n == 1 || n == 2)
{
return 1;
}
return fib(n - 1) + fib(n - 2);
}
对比一下非递归形式实现斐波那契数列。
int fib(n)
{
int a = 1,b = 1,c = 0;
int i;
for(i=2;i<n;i++)
{
c = a + b;
a = b;
b = c;
}
return c;
}
对比递归形式,非递归形式代码量少。缺点是当n非常大时,会造成栈溢出,时间复杂度为O(2^n),所以在实际上递归形式效果比非递归形式差得多。
数组
数组定义
数组就是相同元素的集合。不管是一维还是二维数组,数组元素在内存中都是连续存储的。
数组的创建
一维数组的创建
数组元素类型 数组名[数组大小];//数组大小为常量
二维数组的创建
数组元素类型 数组名[数组行大小][数组列大小];//行、列大小为常量
数组的初始化
一维数组的初始化
int arr[] = {
1,2,3};
int arr[10] = {
1,2,3};
char str[] = {
'h','i'};
char str[10] = {
'h','i'};
char str[] = "hi";
数组在创建时如果不指定大小,那么就必须初始化。数组元素个数根据初始化内容确定。
对于上述代码最后三种情况来说,第一种指定了数组中具体存放的元素,元素个数会根据初始化内容来确定,也就是2个字符;第二种情况指定了数组大小为10,前两个元素为’h’和’i’,剩余用’\0’填充;最后一种情况未指定数组大小,并且用字符串字面形式进行初始化,所以与第一种情况不同,存放到数组的元素除了’h’和’i’之外,还有’\0’,因此数组大小为3。
二维数组的初始化
int arr[2][2] = {
1,2,3,4};
int arr[2][2] = {
{
1,2},{
3,4}};
int arr[][2] = {
{
1,2},{
3}};//{3}实际上是{3,0}
二维数里面的每个元素是一个一维数组,因此在初始化时,可以在一个{}内,再用{}为每个一维数组初始化,当为一维数组初始化的元素个数小于一维数组大小时,剩余空间用0初始化。
为二维数组初始化时,列的大小不能省,因为数组会根据每列元素个数和初始化的元素确定行的大小。反之,知道行数不能确定列数。
数组的使用
一维数组的使用
对数组元素的访问采用下标引用操作符[ ]。下标从0开始。数组名是首元素的地址,因此在进行函数传参时将数组名传给函数,在函数内部是无法计算得到数组长度的,在函数内部使用sizeof(数组名)得到的是数组首元素地址的大小。因此在给函数传一个数组时,还要传一个数组长度。
int fun(int arr[],int len)
{
.