实参与返回值


实参传递是传送值给函数上对应的参数,值会复制一份给参数,来源变量与接受的参数各有一个内存地址,互不相干,例如:

int main(void) { 
    int x = 10; 
    .... 
    printf("%d\n", increment(x));
    printf("%d\n", x);

    return 0; 
} 

int increment(int n) { 
    n = n + 1; 
    return n; 
}

在这个程序片段中,x的值复制给increment函数的参数nn虽然作了递增运算,但是对x的值并无影响,x最后仍是显示 10。

在传值应用上,也可以将变量的地址值取出,传递地址值给指定的指针参数,只要使用&运算符就可以了。

int main(void) { 
    int x = 10; 
    .... 
    printf("%d\n", increment(&x));
    printf("%d\n", x); 

    return 0; 
} 

int increment(int *n) { 
    *n = *n + 1; 
    return *n; 
}

在这个程序中,increment的参数n是个指针,在调用increment函数时,使用取址运算&x变量的地址值传递给指针n,而在函数中,使用取值运算*获取该地址的值,进行递增动作之后再指定给该地址,因此程序最后透过x变量获取的值会是 11。

在函数上定义指针参数之目的,是希望函数中可以有变动同一地址的值,如此一来,调用者可以保留函数中变动的结果。

运用的场景之一是,C 调用函数后只能返回一个值,若在调用函数时,想获取两个以上的运算结果,就可以使用指针参数。

如果函数不返回值,使用void表示不返回任何数值;若函数返回类型不为void,在函数中一定要使用return返回数值,否则编译器会回报错误。

函数也可以返回地址,这意味着调用者可以对该地址取值或变更,例如下面的程序中,在函数中动态配置连续的int空间,并返回空间的地址:

#include <stdio.h>
#include <stdlib.h>

int* ints(int, int);

int main(void) {
    int size = 0;
    int init = 0;

    printf("数组大小:");
    scanf("%d", &size);
    printf("元素初值:");
    scanf("%d", &init);    

    int *arr = ints(size, init);
    for(int i = 0; i < size; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    free(arr);

    return 0;
}

int* ints(int size, int init) {
    int *arr = malloc(size * sizeof(int));
    for(int i = 0; i < size; i++) {
        arr[i] = init;
    }
    return arr;
}

执行结果:

数组大小:5
元素初值:3
arr[0] = 3
arr[1] = 3
arr[2] = 3
arr[3] = 3
arr[4] = 3

由于使用动态配置的方式,被配置的空间在函数执行过后,不会自动清除,可以直接返回地址给调用者,如果是底下范例,数组空间会在函数执行完后清除,编译器会提出警讯,返回指针值也就没有意义,也会造成访问错误:

#include <stdio.h>
#include <stdlib.h>

int* ints(int, int);

int main(void) {
    int size = 0;
    int init = 0;

    printf("数组大小:");
    scanf("%d", &size);
    printf("元素初值:");
    scanf("%d", &init);    

    int *arr = ints(size, init);
    for(int i = 0; i < size; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    free(arr);

    return 0;
}

int* ints(int size, int init) {
    int arr[size];
    for(int i = 0; i < size; i++) {
        arr[i] = init;
    }
    return arr; // warning: function returns address of local variable
}

如果要传递数组给函数,方式之一是明确定义数组类型,这必须包含数组长度:

#include <stdio.h>
#include <stdlib.h>

void printInts(int arr[5]) {
    for(int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    printInts(arr);

    return 0;
}

当然,这就写死了长度信息,另一个方式是指定长度:

#include <stdio.h>
#include <stdlib.h>

void printInts(int len, int arr[len]) {
    for(int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    printInts(5, arr);

    return 0;
}

这是因为 C99 以后,多数编译器支持可变长度的数组类型(variable length array type),然而记得len参数必须先出现,编译器看到后才能用于后续参数。

由于 C11 将可变长度的数组类型标示为非必要功能,如果编译器真的不支持,可以使用传递数组地址的方式:

#include <stdio.h>
#include <stdlib.h>

void printInts(int len, int *arr) {
    for(int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
}

int main(void) {
    int arr[] = {1, 2, 3, 4, 5};
    printInts(5, arr);

    return 0;
}

在传递数组时,C99 以后可以直接传递常量,例如:

#include <stdio.h>
#include <stdlib.h>

void printInts(int len, int *arr) {
    for(int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
}

int main(void) {
    printInts(5, (int[]) {1, 2, 3, 4, 5});

    return 0;
}

(int[]) {1, 2, 3, 4, 5}int[]转型是必要的,编译器需要这项信息知道这是个int数组。


展开阅读全文