函数

来源:互联网 发布:天刀捏脸数据导出 编辑:程序博客网 时间:2024/06/05 04:15


使用函数可以提高代码的可读性、维护性、扩展性

函数的三要素:函数名、函数形参、函数返回值

函数名也是指针常量,保存函数存放的地址(函数的入口地址)

一.函数的定义和声明

         函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字,函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体。

二.函数参数的传递

例如:

#include <stdio.h>int swap(int left, int right){    int temp;    temp = left;    left = right;    right = left;}int main(){    int left = 5;    int right = 6;    printf("before swap:\nleft = %d, right = %d\n", left, right);    swap(left, right);//函数传参传入实参变量名    printf("after swap:\nleft = %d, right = %d\n", left, right);    return 0;}


#include <stdio.h>int swap(int *left, int *right){    int temp;    temp = *left;    *left = *right;    *right = *left;}int main(){    int left = 5;    int right = 6;    printf("before swap:\nleft = %d, right = %d\n", left, right);    swap(&left, &right);//函数传入实参变量的地址    printf("after swap:\nleft = %d, right = %d\n", left, right);    return 0;}



调用函数的步骤:

1)        通过函数名找到函数的入口地址;

2)        给形参分配空间;

3)        传值:把实参变量对应空间的值传给形参;

4)        执行函数体内的语句;

5)        函数返回并释放空间。

总结:

读(不修改)实参变量对应内存空间的值时,传实参变量名

写(修改)实参变量对应内存空间的值时,传实参变量的地址

主函数的形参:

int main(int argc,char *argv[])

argc:参数的个数

argv:参数的内容

例如:

#include <stdio.h>#include <stdio.h>int main(int argc, char *argv[]){    if(argc != 2)    {        printf("Please input one param!\n");        exit(1);    }    int i;    printf("argc = %d\n", argc);    for(i = 0; i < argc; i++)    {        printf("argv[%d] = %s\n", i, argv[i]);    }}

三.函数返回值

1.      不能返回局部变量的地址或指针

例如:

#include <stdio.h>char *func(){    char src[100] = "hello world";    return src;//返回局部变量的地址}int main(){    char *ptr = func();    printf("ptr = %s\n", ptr);    return 0;}

由于src[100]为局部变量,在栈空间分配,函数执行完就会释放。

#include <stdio.h>char *func(){    char *src = "hello world";    return src;}int main(){    char *ptr = func();    printf("ptr = %s\n", ptr);    return 0;}

由于*src是在rodata段的全局变量,程序执行完才会释放,所以hello world可以成功打印出来。

2.      如何返回多个结果?

利用传出参数可以返回多个结果。

1)        传入参数一般按值传递,即函数中的修改不会反映到调用函数中来。

2)        传出参数一般用按地址传递的方式,此时被调函数中的修改会反映到主函数中。

例如:

#include <stdio.h>char *func(char *src, int *num){    int count = 5;    char *ptr = "hello world";    *num = 5;    return ptr;}int main(){    int num;    char src[100] = "hello";    char *ptr = func(src, &num);    //num为传出参数,src为传入参数    printf("ptr = %s, num = %d\n", src, num);    return 0;}

3.      return VS exit

return 0:是返回给系统,这是编码规范,而不是语法要求。这就相当于你让别人做一件事,别人做完之后回应你是否完整无误地完成一样。如果不加return 0;系统就会猜测你的程序是正常退出还是异常退出,并做相应的处理,无形中影响了系统的运行效率。

exit(1):exit退出整个程序,之后可以跟别的数字,可以用echo $?打印出exit后面的数字。它主要是用于检查程序是否出错,做异常处理,返回给用户。在退出前可以加一些调试信息,方便找出异常的位置。

四.企业级函数的编写

1.      函数的编码规范(华为的编码规范)

1)        对所调用函数的错误返回码要仔细、全面地处理。

2)        防止将函数的参数作为工作变量。

3)        函数的规模尽量限制在200行以内。

4)        一个函数仅完成一件功能。

5)        为简单功能编写函数。

6)        不要设计多用途面面俱到的函数。

7)        尽量不要编写依赖于其他函数内部实现的函数。

8)        避免设计多参数函数,不使用的参数从接口中去掉。

9)        检查函数所有参数输入的有效性。

10)    函数名应准确描述函数的功能。

11)    使用动宾词组为执行某操作的函数命名。如果是OOP方法,可以只有动词(名词是对象本身)。

12)    避免使用无意义或含义不清的动词为函数命名。

13)    函数的返回值要清楚、明了,让使用者不容易忽视错误情况。

14)    除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。

15)    在调用函数填写参数时,应尽量减少没有必要的默认数据类型转换或强制数据类型转换。

16)    避免函数中不必要语句,防止程序中的垃圾代码。

17)    减少函数本身或函数间的递归调用。

18)    当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替。

2.      函数入口参数检查

#include <stdio.h>#include <stdlib.h>#include <string.h>int del_ch_min_count(char *src, int min, int *count){    //入口参数检查    if(src == NULL || count == NULL)    {        printf("The param error!\n");   exit(1);    }    int i;    while(*src != '\0')    {        if(count[*src - 'a'] == min){    for(i = 0; i < strlen(src); i++)    {        *(src + i) == *(src + i + 1);    }    *(src + strlen(src)) = '\0';}else{    src++;}    }}<span style="font-weight: bold;"></span>

 a.接口函数必须进行入口参数检查,因为这些参数是外部传入的;
 b.static修饰的函数是自己内部使用,所以提供给static函数的数据是我们自己内部产生的,而我们认为自己提供的数据我们应该能保证其有效性,所以static函数内部不必参数检查。

五.函数指针、函数指针数组

1.      函数指针(传参是用)

传函数名用函数指针来接

int (*p_func)(int,int);

p_func = add;//函数的入口地址  add== p_func

p_func = &add;//整个函数的地址

例如:

#include <stdio.h>int add(int a, int b){    return a + b;}int cal(int (*p_func)(int, int), int a, int b){    return p_func(a, b);}int main(){    int num1 = 5;    int num2 = 6;    int i;    int result = cal(add, num1, num2);    printf("result = %d\n", result);<span style="font-weight: bold;"></span>}<span style="font-weight: bold;"></span>

回调函数:

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用它所指向的函数时,我们就说这个函数是回调函数。使用回调函数可以提高代码的扩展性和维护性。

int (*p_func)(int,int);

p_func = add;

int cal(int (*p_func)(int,int),int,int);其中(*p_func)(int,int)指向的函数add(int,int)为回调函数。

2.      函数指针数组

无需记住多个函数名,即可实现多个函数的功能.

例如:

#include <stdio.h>int add(int a, int b){    return a + b;}int sub(int a, int b){    return a - b;}int mul(int a, int b){    return a * b;}int cal(int (*p_func)(int, int), int a, int b){    return p_func(a, b);}int main(){    int num1 = 5;    int num2 = 6;    int i;    int result = cal(add, num1, num2);    int (*func_array[3])(int,int);    func_array[0] = add;    func_array[1] = sub;    func_array[2] = mul;    for(i = 0; i < 3; i++)    {        printf("func_array[%d] = %d\n", i, func_array[i](num1, num2));    }}<strong></strong>

六.提高代码可读性、可维护性:

改进模块中函数的结构,降低函数间的耦合度,并提高函数的独立性以及代码可读性、效率和可维护性。优化函数结构时,要遵守以下原则:

(1)不能影响模块功能的实现。

(2)仔细考查模块或函数出错处理及模块的性能要求并进行完善。

(3)通过分解或合并函数来改进软件结构。

(4)考查函数的规模,过大的要进行分解。

(5)降低函数间接口的复杂度。

(6)不同层次的函数调用要有较合理的扇入、扇出。

(7)函数功能应可预测。

(8)提高函数内聚。(单一功能的函数内聚最高)

说明:对初步划分后的函数结构应进行改进、优化,使之更为合理。

 










0 0
原创粉丝点击