C语言函数的定义、声明、分类以及调用(一)

1、函数的定义、声明、分类以及调用

1.1、函数声明:如果函数声明在main()函数之后,需要进行函数声明,反正不需要,并且函数声明可以在函数体外

返回值类型 函数名(类型1 形参1,类型2 形参2);
返回值类型 函数名(类型1,类型2...);

#include <stdio.h>
#include <math.h>

    void printLog(int a);
    int getNum(int);//声明函数
    void main() {
        int b= 10; 

        printLog(b);
        printLog(getNum(20));
        printf("半径是4,圆的面积是:%0.7lf\n",atan(1.0)*4*4);//库函数求圆面积
    }

    void printLog(int a) {
        printf("打印参数=%d\n",a);
    }

    int getNum(int b){
        return b;
    }

1.2、函数分类:
1、来源分类为库函数和用户自定义函数
2、有无返回值分为有返回值函数和无返回值函数。返回值为int类型的,声明的时候可以省略不写。不会报错,但是不美观。在 C 语言中,由于参数传递是按值传递而非按引用传递,函数返回一个数组需要使用指针的方式进行传递或返回。因此,在不使用指针的情况下,函数无法返回一维数组或二维数组类型的值。
3、参数传递角度分为无参函数和有参函数

冒泡排序写法

#include <stdio.h>
#include <math.h>

#define X 12
//#define Y 4

    void printLog(int a);
    int getNum(int);
    void sortArray(int [X]);
    void printArray(int [X]);

    void main() {
        int b= 10; 

        printLog(b);
        printLog(getNum(20));
        printf("半径是4,圆的面积是:%0.7lf\n",atan(1.0)*4*4);

        int c[X]= {11,20,15,44,34,65,23,72,9,120,88,111}; 
        sortArray(c);
        printArray(c);
    }

    void printLog(int a) {
        printf("打印参数=%d\n",a);
    }

    int getNum(int b){
        return b;
    }

    void sortArray(int m[X]){
        int tmp;
        for (int i=1;i<=X-1;i++){
            for(int j=0;j<X-i;j++){
                if (m[j] > m[j+1]) {
                    tmp = m [j];
                    m[j] = m[j+1];
                    m[j+1] = tmp;
                }
            }
        }
    }

    void printArray(int m[X]){
        int tmp;
        for (int i=0;i<X;i++){
                printf("%d ",m[i]);
        }
    }

1.3、函数的形参实参分别占据独立的内存单元,参数传递的是值传递,形参的改变不会影响实参。

    void swap(int a,int b){
        printf("交换前:a=%d,b=%d\n",a,b);
        int tmp = 0;
        tmp = a;
        a = b;
        b = tmp;
        printf("交换后:a=%d,b=%d\n",a,b);
    }

2、数组作为函数参数:(1)数组元素作为函数实参; (2)数组名作为函数实参

1、使用数组元素作为实参,在循环语句控制下将数组元素a[i]的值传递给形参x,并将其值减32后保存数数组元素b[i]中,调用函数时,形参x的值发生了改变,而实参a[i]的值并没有发生变化。

#include <stdio.h> 

    char change(char a);

    void main(){
        printf("以数组作为函数实参\n");
        change('m');
        int i=0;
        char a[] ="hello",b[10];
        for (;a[i]!='\0';i++)
            b[i]= change(a[i]);
        b[i]='\0';
    }

    char change(char a){
        printf("将小写字母转化为大写字母\n");
        printf("转化前小写字母:%c\n",a);
        printf("转化后大写字母:%c\n",a-32);
        return a-32;
    }

2、数组名作为函数的实参
数组名作为函数实参时,形参应当用数组或者指针。由于数组名表示数组的首地址,因此实参向形参传递的不是数组的值,而是实参的首地址,这样的形参数组和实参数组共占用相同的内存单元。

(1)定义函数时,形参数组可以省略数组的长度,直接写成char b[ ]。
(2)实参为数组名,如在change(a);语句中的a就是数组名。调用函数时,会将实参数组a的首地址传递给形参数组b,因此形参和实参的对应元素(如a[0]和b[0],…a[9]和b[9])共占用同一内存单元。
(3)由于形参数组和实参数组共占用相同的内存单元,因此改变形参数组b[ ]的值,实参数组a[ ]的值也会跟着改变。
多维数组名也可以作为函数实参,此时被调用函数中形参数组可以指定每一维的大小,也可省略第一维的大小,但第二维的大小不能省略,而且要和实参数组第二维的大小相同。二维数组是由若干个一维数组组成的,在内存中,数组按行存放,因此在定义二维数组的时候,必须指定列数(第二维),以便系统能够区分每一行。

#include <stdio.h> 

    char change(char a);
    void superChange(char b[]);

    void main(){
        printf("以数组作为函数实参\n");
        puts("调用前函数的值:");
        change('m');
        int i=0;
        char a[] ="hello",b[10];
        for (;a[i]!='\0';i++)
            b[i]= change(a[i]);
        b[i]='\0';
        puts("调用后函数的值:");
        puts(b);

        superChange(a);
    }

    char change(char a){
        //printf("将小写字母转化为大写字母\n");
        //printf("转化前小写字母:%c\n",a);
        //printf("转化后大写字母:%c\n",a-32);
        return a-32;
    }

    void superChange(char b[10]){
        int i=0;
        for (; b[i]!='\0'; i++){
            b[i]=b[i]-32;   
        }
        printf("调用函数的结果:");
        puts(b);  
    }

函数参数传递的方式有两种: 按值传递:参数传递时将实参的值传递给了形参,形参和实参各占独立的内存单元。在这种结合方式下,即使形参的值在函数中发生了变化,也不会影响到实参。 按址传递:参数传递时将实参的地址传递给形参,形参和实参占用相同的内存单元。在这种结合方式下,如果形参的值改变了,实际上实参的值也会改变。