函数内变量的传递
情况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函数吗? 不可以!
#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;
}