爬爬爬之路:C语言(六) 函数篇

来源:互联网 发布:咸宁淘宝开店培训学校 编辑:程序博客网 时间:2024/06/12 17:58
函数:  比如说
          main()函数        printf()函数         scanf()函数        strlen()  strcpy()  strcmp()  strcat()函数       getchar()函数       arc4random()等

函数定义: 具有特殊功能的代码段          可以有多个输入 但最多只能有一个输出



函数:              返回值类型 函数名 (形式参数列表) {

                            函数体
                            return 返回值;
                       }
               返回值类型 : void int float double char ….
                                   可以是所有的基本数据类型 也可以没有返回值
                                   若没有返回值 用void表示 此时函数里可以没有return语句


函数的四种形态:   无返回值 无参数,          有返回值 无参数,        有返回值 有参数,           无返回值 有参数
形态一:  无返回值无参数
void myPrintf() {
   
printf("我是一个无返回值无参数的函数\n");
}

形态二: 有返回值 无参数
int returnValue() {
   
   
return 10;
}

形态三: 有返回值 有参数
//计算两个整数的和  
//形式参数列表需要给出形式参数的类型,  给出多个形式参数时需要用逗号隔开   且参数名不能相同  不同参数类型可以不同
int sumNumber(int num1,int num2){
   
return num1 + num2;
}

形态四: 无返回值 有参数的函数
void printSum (int num1,int num2) {
   
printf("%d", num1 + num2);
}

调用函数的方法:
优先在main函数里调用函数
形态一:
               int main(int argc,constchar* argv[]) {
   
              myPrintf();
   
              return0;
        }

形态二:
        int main(int argc,constchar* argv[]) {
            // 有返回值的函数 用与其相同类型的变量接收函数返回值
            int number = returnValue();
            // 再对返回值进行处理
            printf("%d", number);
           return0;
        }

形态三:
        int main(int argc,constchar* argv[]) {
           //给出和函数形式参数个数及,类型相同的参数
            int sum = sumNumber(10,11);
            printf("%d", sum);
           return0;
        }

形态四:
        int main(int argc,constchar* argv[]) {
 
            printSum(5,6);
            return 0;
        }
注意: 可以在调用有返回值函数时不用相同类型变量接收它,  但是这样这个函数就毫无意义  因为函数实现的功能是通过返回值告诉主程序的

函数中 return的作用
     1.无返回值时, 直接结束  return都不执行
     2.有返回值时, 结束的同时 把值返回去



函数参数的两种形式:
1.实际参数     函数调用时的参数
2.形式参数     函数声明时的参数
函数调用时  相当于实际参数赋值给形式参数  参数间的传递是一个值的传递而不是地址的传递

注意: 数组作为参数时  不但要传入数组, 还要传入数组的长度(数组的元素个数)
void bubbleSort(int a[],int length) {

   
for (int i =0; i < length -1; i++) {
       
for (int j =0; j < length -1- i; j++) {
           
if (a[j] > a[j + 1]) {
               
int temp = a[j];
                a[j] = a[j +
1];
                a[j +
1] = temp;
            }
        }
    }
}
int main(){
    int a[10] = {0};
   
for (int i =0; i <10; i++) {
        a[i] =
arc4random() % (50-10+ 1) +10;
       
printf("%d ",a[i]);
    }
    printf("\n");
    bubbleSort(a, 10);  // 调用函数  同时给出数组长度
    for(int i =0; i <10; i++) {
       
printf("%d ", a[i]);
    }
   return0;
}

分析原因   a 代表 a[0]的地址  即 &a[0]   所以实参仅仅将数组首地址的地址传给了形参, 形参只知道首地址的位置, 但是形参并不知道这个数组具体多长,  所以还需要告诉形参数组的长度


函数写在哪?
     首先得知道3点:
          1. 函数包括声明, 实现, 调用 三部分才是完整的
          2. main函数是主函数, 本质也是一个函数
          3 .一个函数的内部是不能定义另一个函数的  只能调用

     C语言的程序入口是main函数, 代码的执行是从上到下的. 系统编译的顺序也是如此. 系统会从整个程序文件的第一行开始编译, 直到发现main函数以后  开始执行main函数内容 而main 函数之前没有出现过的内容 也就是还未编译到的内容,  系统是不会发现的. 所以函数必须把其声明写在main函数之前, 否则在main函数内是无法调用的
     所以
          1. 自定义函数只能写在main函数外面
          2. 函数必须得在main函数前声明

所以可以这么写一个函数
// 声明和实现同时完成
void fun( int x, int y) {
     …;
}
int main (){
     fun(x, y);
     return 0;
}

或者
// 先声明 再在另一个地方实现
void fun(int x, int y);
int main() {
     fun(x, y);
     return 0;
}
void fun(int x, int y) {
     …;
}

函数的嵌套调用: 
// 声明一个函数比较两个数的大小返回大的数
int biggerNumber(int num1, int num2) {
   
return num1 > num2 ? num1 : num2;
}

// 比较三个数,返回最大值
int sortThreeNumber(int num1, int num2,int num3) {
    // 调用比较2个数的函数
    int max =biggerNumber(num1, num2);
    max =
biggerNumber(max, num3);
   
return max;
}

// 比较四个数,返回最大值
int sortFourNumber(int num1, int num2,int num3,int num4) {
    // 调用比较3个数的函数
    int max =sortThreeNumber(num1, num2, num3);
    max =
biggerNumber(max, num4);
   
return max;
}

// 比较五个数,返回最大值
int sortFiveNumber(int num1, int num2,int num3,int num4,int num5) {
    // 调用比较4个数的函数
    int max =sortFourNumber(num1, num2, num3, num4);
    max =
biggerNumber(max, num5);
   
return max;
}

int main(int argc, constchar * argv[]) {

   
printf("5个数中最大数是:%d",sortFiveNumber(37,45,7,9,55));
   
return0;
}
结果是 :55

以上4个函数 有3个函数都是调用已有的函数来帮自己完成特定功能 明显简化了代码和逻辑



函数的递归调用: 
     递归调用就是自己调自己.
     注意: 递归一定要有出口.

计算阶乘: s = 5!

int function(int n) {  //本函数用于计算n阶乘的值

   
if (n ==0 || n ==1) { //n = 0,或者n = 1阶乘的结果是1
       
return1;
    }
else {//n > 1 n阶乘就等于  (n-1)的阶乘乘以n
       
return n *function(n -1);
    }
}
计算过程: 将n = 5 传进函数function 即 function(5)
     1. 5 != 0 && 5 != 1    所以结果等于  5 * function(4)
     2. 4 != 0 && 4 != 1    所以结果等于  5 * 4 * function(3)
     3. 3 != 0 && 3 != 1    所以结果等于  5 * 4 * 3 * function(2)
     4. 2 != 0 && 2 != 1    所以结果等于  5 * 4 * 3 * 2 * function(1)
     5. 1 != 0 但是 1 == 1  所以return 1, 结果等于 5 * 4 * 3 * 2 * 1


输入数字n 计算 (1 * 1)! + (2 * 2)! +  +((n - 1) * (n - 1))! + (n * n)!的值
// 递归方法
int recursive(int num) { // num为给定的数字n
    int sum = 1;  // 用于储存计算结果值
    if (num ==1) { // 当num = 1 时, 出口
        sum = 1;   // 算式的值为 (1 * 1)! = 1
    } else// 当num > 1 即算式不止一元时
        for (int i = 1; i <= num * num; i++) {
            sum *= i;   // 当前大小num = i下, sum = (i*i)!
        }
        sum += recursive(num - 1);  // sum = (i*i)! + ((i-1)*(i-1))!  递归调用
    }
    return sum;
}
int main(int argc, constchar * argv[]) {
    printf("%d",recursive(4)); //调用递归方法
    return 0;
}

事实上, 递归方法虽然代码短,  但是极耗计算机资源, 通常不建议使用.

递归方法通常都可以改写成普通调用方法的形式, 代码如下:

int factorial(int num) {  // 本函数用于计算单个数字阶乘的计算方法
    int sum =1;
   
for (int i = 0; i < num * num; i++) {
        sum *= (i +
1);
    }
   
return sum;
}
int sumOfFormula(int num) {  // 本函数用于计算 N个元素之和
    int sum =0;
   
for (int i = 0; i < num; i++) {
        sum += factorial(i + 1);  // 调用阶乘计算方法
    }
   
return sum;
}
int main(int argc,constchar * argv[]) {
    printf("%d\n",sumOfFormula(4));//调用普通方法
    return 0;
}



变量的作用域: 
     变量根据其有效范围可分为全局变量和局部变量
     注意: 外部变量和局部变量可重名, 但是再局部变量作用域内只能访问局部变量

简单而言 变量的作用域是在其所在的最近的大括号内, 它声明后的所有地方
如 
int a;
(地点一: main 外)
int main() {
     int b;
     (地点二: main 内)
     if(1) {
          int c;
          (地点三: if语句内)
          for (int i = 0; i < 2; i++ ){
               int j;
               (地点四: for循环内)
          }
     }
}

变量a 在main外, 整个程序中, main外层没有大括号了所以 a的作用域是全局, 在代码中的任意地点均可访问.
变量b 在main内, 它所在最近的大括号即是main函数的大括号,  所以它的作用域在main 以内, 在地点一处不可调用变量b, 其余地点均可
变量c 在if内, 它所在最近的大括号即是if语句, 所以它的作用域是再if 以内, 在地点一地点二处不可调用, 其余地点均可访问.
变量i 和j 都在for循环内, 不太一样的是i实在 for循环的小括号内的, 大括号外. 因而i 比j 的作用域大了那么一点点, 因为 i++ 实在for循环的大括号全部执行完以后再执行的 , i , j均只能在地点四调用, 其他地点均无法访问


引申:  声明和实现的分离  有利于代码的查询更利于代码的封装保密
          从函数抽象到类 同类型的函数放在一个类中
具体:  在一个文件中完成函数的声明,  在另一个文件中完成函数的实现 在Xcode环境内OC语言体现在
          .h 文件里写函数声明, .m文件里写函数实现 以后会提
类调用其他类方法  需要引入头文件
引入头文件分两种方法
#import <xxxx.h>
#import “xxxx.h"
<>表示引入系统头文件, “” 表示引入自定义头文件
0 0
原创粉丝点击