Day8 函数
函数的基本用法
1.定义
函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值。
一般形式如下:
<数据类型><函数名称>(<形式参数说明>){
语句序列;
return [(<表达式>)];
}
① <数据类型>是整个函数的返回值类型,return [(<表达式>)]语句中表达式的值要和函数的<数据类型>保持一致。如无返回值应该写为void型。
② <形式参数说明>是逗号“,”分隔的多个变量的说明形式。
③ 大括弧内{<语句序列>},称为函数体;<语句序列>是大于等于0个语句构成的。
④ 函数的说明就是指函数原型。其中<形式参数说明>可以缺省说明的变量名称,但类型不能缺省。
⑤ 编写函数注意:函数功能、参数、返回值。
例:
double Power (double x , int n );
double Power (double , int );
2.函数的使用
函数的使用也叫函数的调用,形式如下:
函数名称(<实际参数>);
① 实参就是在使用函数时,调用函数传递给被调用函数的数据,需要确切的数据。
② 函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个词语。对于无返回值的函数来讲,只能形成一个函数调用语句。
③ 函数要先说明后使用(可以不用先实现)
例:编写一个函数显示”Hello everyone!”,然后编写主程序main调用它。
#include <stdio.h>
int hello ( ){
printf(“Hello everyone!”);
return 0 ;
}
int main ( ){
hello ( ) ;
return 0 ;
}
3.程序举例
定义求X的n次方的函数(X是实数,n为正整数)
#include <stdio.h>
double power(double x, int n)
{
double r = 1;
int i;
for (i = 1; i <= n; i++)
r *= x;
return r;
}
int main()
{
double x, ret;
int n;
printf("input:");
scanf("%lf%d", &x, &n);
ret = power(x, n);
printf("%lf %d = %lf\n", x, n, ret);
return 0;
}
问:使用函数库时,为什么引入头文件,有没有别的替代办法?
答:头文件中有函数的说明,引入头文件后才能让编译器看到文件中的函数说明和调用。将头文件中需要的函数说明引入到程序中,可以代替直接引入头文件。
例:引用scanf和printf函数可以引入<stdio.h>头文件,也可以直接引用<stdio.h>中的scanf和printf
函数说明。
//#include <stdio.h>
int scanf(const char *format, ...);
int printf(const char *format, ...);
double power(double, int);
int main()
{
double x, ret;
int n;
printf("input:");
scanf("%lf%d", &x, &n);
ret = power(x, n);
printf("%lf %d = %lf\n", x, n, ret);
return 0;
}
double power(double x, int n)
{
double r = 1;
int i;
for (i = 1; i <= n; i++)
r *= x;
return r;
}
函数的参数传递
函数之间的参数传递方式:全局变量、复制传递方式、地址传递方式
1.全局变量
(1)全局变量就是在函数体外说明的变量,它在程序中的每个函数都是可见的。
(2)全局变量一经定义后就会在程序中的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响,不建议使用。
例:
#include <stdio.h>
double x = 2;
int n = 3;
double power();
int main()
{
// double x = 2;
// int n = 3;
double ret;
ret = power();
printf("%lf %d = %lf\n", x, n, ret);
return 0;
}
double power()
{
double r = 1;
int i;
for (i = 1; i <= n; i++)
r *= x;
return r;
}
2.复制传递方式(值传递)
(1)调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化。
(2)形参是新开辟的存储空间,因此在函数中改变形参的值,不会影响到实参。形参、实参是一对一关系。
例:
#include <stdio.h>
double power(double, int);
int main()
{
double x = 2;
int n = 3;
double ret;
printf("&x=%p &n=%p\n", &x, &n);
ret = power(x, n);
printf("%lf %d = %lf\n", x, n, ret);
return 0;
}
double power(double a, int b) //double a = x; int b = n;将x的值传给a,将n的值传给b
{
double r = 1;
int i;
printf("&a=%p &b=%p\n", &a, &b);
for (i = 1; i <= b; i++)
r *= a;
return r;
}
3.地址传递方式(指针传递)
按地址传递,实参为变量的地址,而形参为同类型的指针。被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作相当于对实参本身的操作)。
例:
#include <stdio.h>
void swap(int * x, int * y);
int main()
{
int a = 10;
int b = 20;
printf("before:%d %d\n", a, b);
swap(&a, &b);//引入a,b地址
printf("after:%d %d\n", a, b);
return 0;
}
//int * x = &a; int *y = &b;
void swap(int * x, int * y)
{
int t;
t = *x;//a
*x = *y;
*y = t;
}
4.程序举例
写一个函数,统计字符串中小写字母的个数,并把字符串中的小写字母转化为大写字母。
#include <stdio.h>
int str_fun(char * p);
int main(int argc, char *argv[])
{
char s[] = "welcome2021Beijing";
int n;
n = str_fun(s);
printf("n=%d %s\n", n, s);
return 0;
}
int str_fun(char * p) //char * p = s;
{
int num = 0;
while (*p != '\0') {//while (*p)
if (*p <= 'z' && *p >= 'a') {
num++;//统计小写字母数量
*p -= ' ';//将小写字母转化为大写
}
p++;
}
return num;
}
问:如何编程可以实现地址传递方式且不能改变实参?
答:使用const修饰符
函数中传递数组参数
1.函数的传参——数组
(1)全局数组传递方式
(2)复制传递方式
实参为数组的指针,形参为数组名(本质是一个指针变量)
(3)地址传递方式
实参为数组的指针,形参为同类型的指针变量
2.程序举例
(1)编写函数,计算一个一维数组的所有元素的和
复制传递方式
#include <stdio.h>
int array_sum(int data[], int n);
int main(int argc, char *argv[])
{
int a[] = {5, 9, 10, 3, 10};
int sum = 0;
sum = array_sum(a, sizeof(a)/sizeof(int));
printf("sum=%d\n", sum);
return 0;
}
int array_sum(int data[], int n) // int data[] = a;error int * data = a;
{
//int n = sizeof(a)/sizeof(int);
int ret = 0;
int i;
for (i = 0; i < n;i++) {
printf("%d\n", data[i]);
ret += data[i];
}
return ret;
}
地址传递方式
#include <stdio.h>
int array_sum(int * data, int n);
int main(int argc, char *argv[])
{
int a[] = {5, 9, 10, 3, 10};
int sum = 0;
sum = array_sum(a, sizeof(a)/sizeof(int));
printf("sum=%d\n", sum);
return 0;
}
int array_sum(int * data, int n) //int * data = a;
{
//int n = sizeof(a)/sizeof(int);
int ret = 0;
int i;
for (i = 0; i < n;i++) {
printf("%d\n", data[i]);
ret += data[i];
}
return ret;
}
(2)编写函数,删除字符串中的空格
#include <stdio.h>
void del_space(char * s1);
int main(int argc, char *argv[])
{
char s[] = " h a sdf g ";
puts(s);
del_space(s);
puts(s);
return 0;
}
void del_space(char * s1)
{
char * s2;
s2 = s1;
while (*s1) {
if (*s1 == ' '){
s1++;//遇到空格指针前移
}
else {
*s2 = *s1;//遇到字符时将s1内容赋值给s2
s1++;
s2++;//s1,s2前移循环继续
}
}
*s2 = '\0';
}
指针函数
指针函数是指一个函数的返回值为地址量的函数。
指针函数的定义的一般形式如下:
<数据类型> *<函数名称>(<参数说明>){
语句序列;
return [(<表达式>)];
}
返回值:全局变量的地址/static变量的地址/字符串常量的地址/堆地址
例:
下面程序是否有问题,若有问题如何修改?(存疑)
#include <stdio.h>
#include <string.h>
char * getstring();
int main(int argc, char *argv[])
{
char * r;
r = getstring();
printf("---%s---\n", getstring());
(*r)++;
puts(r);
return 0;
}
char * getstring()
{
char str[20];
strcpy(str, "hello");//字符串str为局部变量
return str;
}
修改:
#include <stdio.h>
#include <string.h>
//char str[20];
char * getstring();
int main(int argc, char *argv[])
{
char * r;
r = getstring();
printf("---%s---\n", getstring());
//(*r)++;
puts(r);
return 0;
}
char * getstring()
{
//char str[20];//error
//static char str[20];
char * str = "hello";
// strcpy(str, "hello");
return str;
}
例:
(1)编写一个指针函数,删除一个字符串中的空格
#include <stdio.h>
#include <string.h>
char * del_space(char * s);
int main(int argc, char *argv[])
{
char * r;
char str[]= " how are you ";
char s[50], s2[50];
r = del_space(str);
printf("---%s---\n", r);
//strcpy(s, del_space(str));
strcpy(s2, strcpy(s, del_space(str)));//m=n=k
puts(str);
puts(s);
puts(s2);
return 0;
}
char * del_space(char * s) //char * s = str;
{
char * r = s;
char * p = s;
while (*s) {
if (*s == ' ')
s++;
else {
*p = *s;
s++;
p++;
}
}
*p = '\0';
return r;//r指针为字符串首地址,p指针进行字符串操作
}
(2)编写一个指针函数,实现字符串函数连接
#include <stdio.h>
char * mstrcat(char * dest, const char * src);
int main(int argc, char *argv[])
{
//char * r;
char dest[50] = "welcome";
char src[] = "everyone";
puts(mstrcat(dest, src));
puts(dest);
return 0;
}
char * mstrcat(char * dest, const char * src)
{
char * r = dest;
while (*dest++);//当*dest不为\0时,dest指针加1
dest--;//此时dest指针指向\0,需要回退一位,进行字符串连接
while (*dest++ = *src++);//循环将src字符串连接在dest字符串之后
return r;
/*方式二
char * r = dest;
while (*dest++);
dest--;
while (*src) {
*dest++ = *src++;
}
*dest = '\0';
return r;
*/
/*方式三
char * r = dest;
while (*dest) {
dest++;
}
while (*src) {
*dest = *src;
dest++;
src++;
}
*dest = '\0';
return r;
*/
}
(3)编写一个指针函数,把整数转化为字符串
#include <stdio.h>
char * itoa(char *p, int n);
int main(int argc, char *argv[])
{
int n;
char s[50], * r;
printf("input:");
scanf("%d", &n);
r = itoa(s, n);
puts(r);
puts(s);
return 0;
}
char * itoa(char *p, int n)
{
int r, i = 0, j;
//static char p[50];
while (n) {
r = n % 10;
n /= 10;
p[i] = r + '0';
i++;//循环输出数字字符串,此时输出字符转为数字的逆序
}
p[i] = '\0';
j = i-1;
i = 0;
while (i < j) {
r = p[i];
p[i] = p[j];
p[j] = r;
i++;
j--;//将输出字符串转化为正序
}
return p;
}
递归函数和函数指针
1.递归函数
递归函数是指一个函数的函数体中直接或间接调用了该函数。
递归函数调用的执行过程分为两个阶段:
① 递推函数:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件。
② 回归阶段:按递归终止条件求出结果,逆向逐步带入递归公式,回归到原问题求解。
例:
(1)编写一个递归函数计算n!
#include <stdio.h>
int fac(int n);
int main(int argc, char *argv[])
{
int n;
printf("input:");
scanf("%d", &n);
printf("%d\n", fac(n));
return 0;
}
int fac(int n)
{
if (n == 0 || n == 1)
return 1;
return n * fac(n-1);
}
(2)编写一个递归函数,计算斐波那契数列:一般而言,兔子出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来,如果所有兔子不死,那么一年后可以繁殖多少对兔子?
#include <stdio.h>
int fib(int n);
int main(int argc, char *argv[])
{
int n = 1;
while (n <= 10) {
printf("%d ", fib(n));
n++;
}
printf("\n");
return 0;
}
int fib(int n)
{
if (n == 1 || n == 2)
return 1;
return fib(n-1)+fib(n-2);
}
2.函数指针
(1)函数指针用来存放函数的地址,这个地址是一个函数的地址,函数名代表了函数的入口地址,函数指针变量说明的一般形式如下:
<数据类型> (*<函数指针名称>) (<参数说明列表>) ;
<数据类型>是函数指针所指向的函数的返回值类型。
<参数说明列表>应该与函数指针所指向的函数的形参说明保持一致,参数名可省略。
(*<函数指针名称>)中,*说明为指针类型。 ()不可缺省,表明为函数的指针。
例:
#include <stdio.h>
int add(int a, int b) {
return a+b;
}
int sub(int a, int b) {
return a-b;
}
int mul(int a, int b) {
return a*b;
}
int main(int argc, char *argv[])
{
int m = 10, n = 20;
int (* p)(int, int);
p = add;
//printf("%d\n", add(m, n));
printf("%d\n", (*p)(m, n));
p = sub;
printf("%d\n", (*p)(m, n));
return 0;
}
(2)函数指针数组是一个保存若干个函数名的数组。一般形式如下:
<数据类型> (*<函数指针数组名称>) [<大小>] (<参数说明列表>) ;
其中,<大小>是指函数指针数组元素的个数,其它同普通函数指针。
例:
int add(int a, int b) {
return a+b;
}
int sub(int a, int b) {
return a-b;
}
int mul(int a, int b) {
return a*b;
}
int main(int argc, char *argv[])
{
int m = 10, n = 20;
int (* p[2])(int, int);//int * p[3]
p[0] = add;
//printf("%d\n", add(m, n));
printf("%d\n", (*p[0])(m, n));
p[1] = sub;
printf("%d\n", (*p[1])(m, n));
return 0;
}
问:如何调用C库中的qsort函数来实现整型数组排序?
答:
#include <stdio.h>
#include <stdlib.h>
int compare(const void *, const void *);
int main(int argc, char *argv[])
{
int s[] = {89, 23, 10, 8, 7, 61}, n, i;
n = sizeof(s)/sizeof(int);
qsort(s, n, sizeof(int), compare);//学会通过查找函数手册使用函数
for (i = 0; i < n; i++)
printf("%d ", s[i]);
puts("");
return 0;
}
int compare(const void * p, const void * q)
{
return (*(int *)p - *(int *)q);
}
398



被折叠的 条评论
为什么被折叠?



