按值调用
即用普通变量作函数参数(程序将函数调用语句中的实参的一份副本传给函数的形参,实参的值是不能在被调函数内被修改的)
以两数互换(swap)程序为例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int a, int b);
int main()
{
int a, b;
printf("input a,b:");
scanf("%d,%d", &a, &b);
swap(a, b);
printf("in main():a=%d,b=%d\n", a, b);
return 0;
}
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
printf("in swwap():a=%d,b=%d\n",a, b);
}
①在主函数中调用swap()函数,先进行实参向形参的单向传递,②在swap()函数中利用临时变量将形参x和y的值互换,③当swap()函数执行完毕,程序的控制流程从swap()返回主函数。
①由于形参x,y是局部变量(其在栈上分配内存,在执行函数调用时,系统在栈上为函数内的局部变量及形参分配内存,函数执行结束,自动释放这些内存),则swap()函数执行结束,为其分配的存储空间就被释放(所谓释放内存,其实就是将内存中的值恢复为随机值(即乱码)),②则形参x,y的值变为随机值,且不能在它们作用域之外(每个变量仅在定义它的语句块内有效,且拥有自己的存储空间)去访问它们
③主函数不能去访问x和y,同时形参x,y的值无法反向传给实参,所以主函数中实参a,b的值没有变化
总结:函数的参数传递是"单向的值传递",即只能将实参的值单向传递给形参,而不能反向将形参的值(注意是值!!!)传给实参,形参值的改变也不会影响实参,其根本原因是实参和形参占据着不同的存储单元
!!在按值调用时,为了得到函数修改的形参的值,可利用return语句从被调函数返回被修改的形参值(注意返回的应是一个值!!,如果返回的是动态局部变量的地址,输出其中的数据必然为乱码(动态局部变量都是在栈上创建内存的,在函数调用结束后就被自动释放了,释放后的内存中的数据将变成随机数)),但return仅限于从函数返回一个值,需要函数返回多个值时,就要用到模拟按引用调用的方法
模拟按引用调用
(常用的从函数中返回修改了的数据值的方法,即将地址值传递给被调函数的形参,只有得到变量的地址值,才能利用间接寻址方式在函数中改变相应的地址单元中的数据值,那么相应实参所对应开辟空间里的值会改变,但其地址仍不变)
⒈用数组作函数参数(将数组在内存中的首地址传给函数的形参,然后在函数中利用形参得到的数组元素地址值,对数组元素进行间接寻址来修改数组元素值)
以将学生成绩从高到低排序为例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define N 40
int readscore(int score[]);
void printscore(int score[], int n);
void datasort(int score[], int n);
int main()
{
int n, score[N];
n = readscore(score);
printf("total students are %d\n", n);
datasort(score, n);//将数组在内存中的首地址传给形参
printf("sorted scores:\n");
printscore(score, n);
return 0;
}
int readscore(int score[])
{
int i = -1;
do
{
i++;
printf("input:\n");
scanf("%d", &score[i]);
} while (score[i] >= 0);
return i;
}
void printscore(int score[], int n)
{
for (int i = 0; i < n; i++)
{
printf("%d\t", score[i]);
}
}
void datasort(int score[], int n)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (score[j] > score[i])
{
int temp = score[j];
score[j] = score[i];
score[i] = temp;
}
}
}
}
使用数组名作为函数的实参,实际上是将实参数组score的首地址传给函数相应的形参,于是形参数组与实参数组共享同一段存储单元,则对形参数组元素值的改变,也就相当于是对实参数组元素值的改变。
⒉用指针变量作函数参数(将变量在内存中的地址值传给函数的形参,从而使函数的形参指向变量的值,函数使用间接寻址运算符*改变形参指向变量的值,相应实参所对应开辟空间里的值会改变,但其地址仍不变))
以两数交换程序为例:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
void swap(int* x, int* y);
int main()
{
int a, b;
printf("input a,b:\n");
scanf("%d,%d", &a, &b);
printf("before swap:a=%d,b=%d\n", a, b);
swap(&a, &b);//将变量的地址传给函数形参
printf("after swap:a=%d,b=%d\n", a, b);
return 0;
}
void swap(int* x, int* y)
{
int temp = *x;
*x = *y;
*y = temp;
}
①在主函数中调用swap()函数,先进行实参向形参的单向传递(此时传递的是地址),②在swap()函数中利用临时变量将形参x和y的指向的变量的值互换,但形参接收的地址值没有改变,③当swap()函数执行完毕,程序的控制流程从swap()返回主函数。
实际上是将变量首地址传给函数相应的形参,于是形参与实参共享同一段存储单元(共用同一地址)
总结:函数的参数传递是"单向传递",即只能将变量地址单向传递给形参,而形参不能返回函数中动态分配的内存的首地址给实参(即地址值是不变的),但由于改变相应的地址单元中的数据值,那么相应实参所对应开辟空间里的值也会改变,注意其地址仍不变(即swap()函数执行完后,主函数的相应变量a,b的地址单元中的数据值发生了改变,但变量a,b的地址值不变)
动态内存分配函数
动态分配的内存不会在函数调用结束后被自动释放,必须使用free()才能释放
以输入一个字符串并将其显示在屏幕上程序为例:
使用动态内存分配函数:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
char* getstr(char* s);
int main()
{
char* ptr = NULL;
ptr = getstr(ptr);//是ptr指向动态分配的内存的首地址
puts(ptr);
free(ptr);//释放ptr指向的动态分配的内存
return 0;
}
char* getstr(char* s)//返回字符指针的函数
{
s = (char*)malloc(80);//申请动态分配的内存
scanf("%s", s);
return s;//返回动态分配内存的首地址
}
模拟按引用调用(以指针变量作函数形参)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void getstr(char* s);
int main()
{
char s[80];
char* ptr =s;//指针初始化,是ptr指向数组s的首地址
getstr(ptr);//以地址值为实参传递给形参
puts(ptr);
return 0;
}
void getstr(char* s)
{
scanf("%s", s);//地址值不变,改变相应地址单元中的数据值
}
!!下面来展示错误代码(是错误的):
⒈
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void getstr(char* s);
int main()
{
char* ptr =NULL;//指针变量初始化为空指针
getstr(ptr);//以地址值为实参传递给形参
puts(ptr);
return 0;
}
void getstr(char* s)//指针形参接收实参传过来的是空指针
{
scanf("%s", s);//地址值不变,仍为空指针
}
虽然使用了模拟按引用调用,但是形参s不能返回函数中动态分配的内存的首地址给实参,则实参仍为空指针,于是程序异常终止
⒉
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
char *getstr(void);
int main()
{
char* ptr =NULL;
ptr=getstr();
puts(ptr);//试图使用野指针
return 0;
}
char *getstr(void)
{
char s[80];//定义动态存储类型的数组
scanf("%s", s);
return s;//试图返回动态局部变量的地址
}
在按值调用时,虽然使用了return语句,但返回的不是一个值,而是动态局部变量的地址!!!如果返回的是动态局部变量的地址,输出其中的数据必然为乱码(动态局部变量都是在栈上创建内存的,在函数调用结束后就被自动释放了,释放后的内存中的数据将变成随机数)
附:
一般变量使用取地址符,是获取其地址,即为指向该变量的指针,整个数组是一块连续的内存单元,数组名就是数组的首地址,指向该数组的第一个元素的指针,只不过是常指针而以,则不用再加取地址符。
C语言的字符串为数组形式,故对字符串输入不需要加地址符,而单独的字符,整型变量等的名称不能直接表示地址,故需要加地址符!!