C语言的函数—参数传递机制

函数内变量的传递

情况1:对于基本数据类型的变量,将变量的值传递过去。

int a = 10;
int b = a;
printf("%d",a);//输出:10
b = 20;
printf("%d",a);//输出:10

情况2: 针对数组,将数组的地址传递过去。

 int arr[5] = {1,2,3,4,5};
    int *arr1 = arr;
    arr1[0] = 10;
    for (int i = 0; i < 5; ++i) {
        printf("%d ",arr[i]);
    }

 情况3:针对于指针,将指针保存的地址传递出去

int i = 10;
int *p = &i;
printf("%d\n",*p);
int *q = p;
*q = 20;
printf("%d\n",*q);

 形参、实参

  • 形参(formal parameter:在定义函数时,函数名后面括号()中声明的变量称为 形式参数 ,简称 形参
  • 实参(actual parameter:在调用函数时,函数名后面括号()中使用的值/变量/表达式称为 实际参数 ,简称 实参

注意:函数在被调用时,给形参动态分配临时存储空间,函数返回释放

分类:形参和实参的功能主要是数据传递,按照传递的是“数据还是地址”,分为值传递 地址传递 两种方式。

不同类型形参的书写格式:

不同形参函数原型的书写格式
传递值得类型形参格式函数原型
值传递:(基本数据类型、结构体、共用体、枚举类型。 )

数据类型 形参名)

例:void func (int a)

void func (int );
址传递:变量指针

数据类型  *形参名)

例:void func (int *a)

void func (int *);
址传递:一维数组

void func( int x [ ], int n)

或:

void func( int *x , int n)

void func( int  [ ], int );

或:

void func( int *, int);

址传递:二维数组int func(int arr[ ][N],int len)int func(int [ ][N],int);
字符串int func(char *p)int func(char *)
指针数组void func(char *string[ ], int n)void func(char *[ ], int );

1.参数的传递机制1:值传递

定义:值传递,又称传值方式、数据复制方式,就是把主调函数的实参值 复制 给被调用函数的 形参 ,使形参获得初始值。接着在函数内对形参值的修改,不影响实参值。

  • 值传递,是 单向传递 ,只能把实参的值传递给形参,而不能把形参的值再传回给实参(除非将函数返回的值重新赋值个实参变量)。
  • 默认传递值的类型:基本数据类型 (整型类型、浮点类型,字符类)、结构体、共用体、枚举类型。 

举例1:

void increment(int a) {
    a++;
    printf("a = %d\n",a); // a = 11
}
int main(){
    int i = 10;
    printf("i = %d\n", i); // i = 10
    increment(i);//这里将i的值传递a,修改a的值,不影响原始变量i。

//若希望获得变化的参数值,可以返回a的值,并赋值给变量i。

    printf("i = %d\n", i); // i = 10
    return 0;
}

 举例2:定义一个函数,交换两个变量的值。

void swap(int a, int b) {//交换两个数位置的函数
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 6, y = 8;
    printf("调用函数之前:x = %d,y = %d\n", x, y); //输出调用swap()函数之前x,y的值
    
    swap(x, y);//调用swap()函数
    printf("调用函数之后:x = %d,y = %d\n", x, y); //输出调用swap()函数之后x,y的值
}
//结果:输出的结果一样,都是x = 6,y = 8。

上述代码,进行值传递时,函数体内部对传入的实参的值进行操作,不会影响函数外部变量的值。

2.参数传递机制2:地址传递

定义:实参将地址传递给形参,二者地址值相同。

在函数内部,通过形参保存地址,修改地址指向的数据的值,会导致函数外实参调用时,此数据也是修改以后的——双向传递

针对的数据类型:指针、数组。

1.简单变量指针作为形参

函数的形参类型是指针类型时,使用该函数时,需要传递指针,或者地址,或者数组给该形参。

例1: (将一个变量的地址传递给形参)

void func(int *a){
    (*a)++;
    printf("%d",*a);//输出:11
}
int main(){
    int a = 10;
    printf("%d",a);//输出:10
    func(&a);
    printf("%d",a);//输出:11
    return 0;
}
因为 传入的是地址 ,函数体 内部对该地址包含的值的操作 会影响到函数外部变量的值

例2:(利用指针实现上面利用函数交换两个数的位置) 

void swap(int *a, int *b) {//注意:写法表示的含义。
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 6, y = 8;
    printf("调用函数之前:x = %d,y = %d\n", x, y); //输出x = 6,y = 8
    
    swap(&x, &y);//调用swap()函数
    printf("调用函数之后:x = %d,y = %d\n", x, y); //输出调用x = 8, y = 6
}

上面代码中,定义函数体的错误写法。

void swap(int *p1, int *p2) { //形参是指针变量
    int *temp;
    temp = p1;
    p1 = p2;
    p2 = temp;
    //printf("*p1 = %d,*p2 = %d\n", *p1, *p2); //*p1 = 8,*p2 = 6
}

 这个代码,只是将函数体内部两个形参的地址交换,两个形参的值发生互换,但是函数体外部变量的值不会改变。

复习: (野指针:函数返回值是一个函数内部变量指针

int* func() {
  int i;
  // ...
  return &i;
}

这种写法错误:因为当函数结束运行时,内部变量就失效了,这时指向内部变量 i 的内存地址就是无效的,再使用这个地址是错误的。

 2.数组作为形参

数组名本身就代表该数组首地址,传数组的本质就是传地址。

说明:

声明一个数组函数:void func( int x [ ], int n){ …………; }

  • 参数1:数组的定义,只需写出中括号即可,不需要限定数组长度。
  • 参数2:数组作为参数的习惯操作。将函数中要操作的数组元素的长度传入(并不是指数组的总长度)。

原因:由于数组名就是一个指针,如果只传数组名,那么函数只知道数组开始的地址,不知道结束的地址,所以才需要把数组长度也一起传入。

 举例1:(定义一个一维数组,通过函数给这个数组赋值)

#define LENGTH 10
void setValue(int arr[],int len){//另一种写法:void setValue(int *arr,int len){……}
    //注意在定义一个传入形参的数组时,一般都会再声明一个形参,用来指定对数组操作的长度。
    for (int i = 0; i < len; ++i) {
        arr[i] *= 10;
    }
}

int main(){
    int arr[LENGTH] = {0};
    setValue(arr,5);
    for (int i = 0; i < LENGTH; ++i) {
        printf("%d",arr[i]);
    }
    return 0;
}

举例2: (定义一个一维数组,利用函数实现数组元素的反转)

void func(int *arr,int len){
    for (int i = 0; i < len/2; ++i) {
        int tempt = arr[i];
        arr[i] = arr[len-1-i];
        arr[len-1-i] = tempt;
    }
}
int main(){
    int num[5] = {0,1,2,3,4};
    func(num,5);
    for (int i = 0; i < 5; ++i) {
        printf("%d",num[i]);
    }
    return 0;
}

 实现上述功能,函数体定义的方式2:

void reverse(int arr[],int len){
    for (int i = 0,j = len -1; i < j; ++i,j --) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

 拓展:定义一个数组函数时,实参与形参的对应关系有以下4种情况:

1 )形参和实参都用数组名
(2)实参用数组名,形参用指针变量
(3)实参形参都用指针变量
4 )实参为指针变量,形参为数组名

3.字符串(字符指针)作为形参

举例1:定义函数,要求字符串作函数参数,统计数字字符出现的个数。

#include<stdio.h>
#define N 100
int digitalCount(char *p) {
    int count = 0;
    for (; *p != '\0'; p++)//解释:
        if (*p >= '0' && *p <= '9')
            count++;
    return count;
}
int main() {
    char strs[N] = "a12bc43hec22b68o";//字符串的声明方式:
    printf("数字个数为 % d个\n",digitalCount(strs)); //8
return 0 ;

4.指针数组作为形参 

指针数组的元素是指针变量,用指针数组能够实现一组字符串的处理。

举例:编写能对多个字符串排序的函数。

#include <stdio.h>
#include <string.h>

void stringSort(char *[], int);//函数原型;
void stringPrint(char *[], int);


int main() {
    char *days[7] = {"Sunday", "Monday",
                     "Tuesday", "Wednesday",
                     "Thursday","Friday", 
                     "Saturday"};//定义一个字符串数组。
    stringSort(days, 7);
    stringPrint(days, 7);
    return 0;
}
//定义一个函数,将字符串数组中的元素按首字母进行排序
void stringSort(char *string[], int n) {
    char *temp;
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++){
            if (strcmp(string[j], string[j + 1]) > 0) {
                temp = string[j];
                string[j] = string[j + 1];
               string[j + 1] = temp;
                }
           }
      }
}
//遍历字符数组中的元素。
void stringPrint(char *string[], int n) {
    for (int i = 0; i < n; i++)
        printf("%s ", string[i]);
}

 练习

例1:定义函数,求一维数组元素的最大值。

原型:int Max(int *p,int n)

功能:在长度为 n、由 p 指向的一维数组中求元素最大值

#include <stdio.h>
int Max(int *p,int n){
    int max = p[0];
    for (int i = 0; i < n; ++i) {
        if(max < p[i]){
            max = p[i];
        }
    }
    return max;
}
int main(){
    int arr[6] = {5,8,4,6,4,3};
    int numMax = Max(arr,6);
    printf("max:%d",numMax);
    return 0;
}

 例2:多维数组名作为形参

 有一个3×4的矩阵,求所有元素中的最大值

#include <stdio.h>
#define M 3
#define N 4
//int maxValue(int [][N],int);
int maxValue(int arr[][N],int len);
int main(){
    int arr[M][N] = {{1,2,3},
                     {2,15,6},
                     {5,8,12}};
    int max = maxValue(arr,M);
    printf("max:%d",max);
    return 0;
}
//定义一个函数,求二维数组中的最大值,有三种方法。
int maxValue(int arr[][N],int len){
    int *p;
    for (p = arr[0],max = *p; p < arr[0]+len*N; p++) {
        if (max < *p) {
            max = *p;
        }
    }
    return max;
}

注意:实参和形参二维数组的列数要保持一致。

void f(int x[ ] [5],int len){

…………;

}

//1、int a[10][5]; 可以传给f函数吗? 可以!

//2、int b[10][3];可以传给f函数吗?   不可以!

举例3:变长数组作为参数
#include <stdio.h>
int sum_array(int a[n],int n) { //报错
 ...}


int sumArray(int n, int a[n]) {//正确
 ...
}

int main() {
    int a[] = {1, 3, 5, 7};
    int sum = sumArray(4, a);
    return 0;
}

变长数组作为参数的函数原型

  • int sumArray(int, int [*]);
  • int sumArray(int, int [ ]);

变长数组作为函数参数的好处:多维数组的参数声明,可以把后面的维度省掉了。

// 原来的写法:int sumArray(int a[ ][4], int n);

// 变长数组的写法:int sumArray(int n, int m, int a[n][m]);

 C++中的引用传递

void f1(int *x){
    (*x)++;
}
int main() {
    int a = 1;
    f1(&a);
    printf("a = %d",a); // a = 2
    return 0;
}

上述代码,通过函数中传入指针变量,修改形参的值,可以实现主函数中实参的值。 

另一种方式:C++中的引用传递

#include <iostream>
void f2(int &x) {
    x++;
}
int main() {
    int a = 1;
    f2(a);
    std::cout << "a = " << a << std::endl; // a = 2
    return 0;
}

其他应用场景: 

情况1:传入结构体变量

void insert(SqList &L,int x){
 //修改L内部data[]数组的内容,则认为修改了L,因此需要传入引
用型L
 //....
}

情况2:传入指针型变量

void f(int *&x){  //指针型变量在函数体中需要改变的写法
 x++; //将指针 x 向后移动一个整数的大小。
}

int *&x 这里 int * 表示指向整数的指针, & 表示引用, 因此 int *&x 表示引用指向整数的指针。这个参数允许传递一个指向整数的指针,并且在函数内部可以改变指针的值。

#include <iostream>
void f(int *&x) {
    x++;
}
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int *ptr = arr; // 将指针指向数组的第一个元素
    std::cout << "Original value: " << *ptr <<
std::endl;
    f(ptr); // 传递指针给函数,函数将指针移动到下一个元素
    std::cout << "New value: " << *ptr << std::endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值