C语言基础⑧——函数

一、函数的概述

1、函数:实现一定功能的,独立的代码模块。我们的函数一定是先定义后使用

2、使用函数的优势:

  1. 我们可以通过函数提供功能给别人使用,也可以使用别人提供的函数,减少代码量;
  2. 借助函数可以减少重复性的代码;
  3. 实现结构化(模块化)程序设计思想;

结构化程序设计思想:将大型的任务功能划分为相互独立的小型的任务模块来设计。

3、函数是C语言程序的基本组成单元:

      C语言程序是由一个(必然是main函数)或多个函数组成。

二、函数的分类

1、从函数实现的角度:

  • 库函数:C语言标准库实现并提供使用的函数,比如常见的有printf(),scanf();
  • 用户自定义函数:需要程序员自行实现,开发中大部分都是这样的函数;

2、从函数形式的角度:

  • 无参函数:函数调用时,无需传参,可配可不配返回值;

  • 有参函数:函数调用时,需要参数传递数据,经常需要配套返回值使用。

  • 相关概念:

  1. 主调函数:主动去调用其他函数的函数。
  2. 被调函数:被调用的函数。
//此时,main是主调函数,注意:main只能作为主调函数。
int main()
{
    //此时printf()是被调函数
    printf("hello world!");

    return 0;
}

很多时候,一个函数既可以是主调函数,也可以是被调函数,这种情况一般出自自定义函数。

int fun_b()
{
    printf("执行B\n");
}
int fun_a()
{
    printf("执行A\n");
}
int main()
{
    fun_a();
}

三、函数的定义

1、语法

返回类型 函数名(形参列表)             --> 函数头 | 函数首部
{
    函数体语句;(简称为函数体)        --> 函数体,整个{}包裹的内容包括返回值都属于函数体
}

函数首部:

  • 返回类型:函数返回值的类型;
  • 函数名:函数的名称,遵循标识符命名(是用英文字母、数字、_、$,不能以数字开头,建议小写加 '_' 命名法);
  • 形参列表:用于接收主调函数传递的数据,如果有多个用","分隔,且每个形参都需要指定类型。
    void fun1(int a,float b)//多个传参一定要指定类型
    {
        
    }
    面试题:
main() //①问main的返回类型? int 如果省略类型标识符,则默认返回int
{
    
}

void main()  //②无类型(void类型)不返回函数值
{

}

①与②不同,①返回函数值(int型),②不返回函数值
注意:
  • 函数类型标识符、变量类型说明符相同,它表示返回的函数值的类型;
  • 在C语言中还可以定义无类型(即void类型)的函数,这种函数不返回函数值,只是完成某种功能;
  • 如果省略函数的类型标识符,则默认为是int型;
  • 函数中返回语句的形式为return(表达式);或return 表达式;其作用是将表达式的值作为函数值返回给调用函数。其中表达式的类型应与函数类型一致。
  • 如果形参表列中有多个形式参数,则它们之间要用","分隔;
  • 如果形参列表中有多个形参,即使它们的类型是相同的,在形参表中也只能逐个进行说明;
fun1(int a,int b)
{

}
  • 一个完整C程序中的所有函数可以放在一个文件中,也可放在多个文件中。
demo01.c  -->  int main(){    printf();    }

stdio.c  -->  printf(char *p){    }

举例:计算1到5之间个自然数的阶乘值

/*
 * 需求:计算1到5之间个自然数的阶乘值
 */
#include <stdio.h>
// 定义一个函数,用来实现阶乘
int p(int n)
{
    int k,s;// k:循环变量,s:阶乘结果
    s = 1;
    for(k = 1;k <= n;k++) s *= k;
    
    return s;    
}
int main()
{
    int m;// 来管理参与计算的自然数
    int n = 5;// 定义范围
    int s = p(n);
    printf("1~5之间自然数的阶乘值是%d\n",s);
}

举例:计算并输出一个圆台两底面积之和

/**
 * 需求:计算并输出一个圆台两底面积之和。 
 */
#include <stdio.h>
// 定义PI
#define PI 3.1415926
/**
 * 定义一个函数,实现圆的面积的计算
 * @param r 圆的半径
 * @return 圆的面积
 */
double circleArea(double r) 
{
    // 圆的面积 = PI * r * r
    return PI * r * r;
}
/**
 * main函数以后只做输入输出
 */
int main()
{
    // 定义两个半径,两个面积
    double r1,r2,area1,area2;
    printf("请输入两个圆的半径:\n");
    scanf("%lf,%lf",&r1,&r2);
    // 调用函数计算两个圆的面积
    area1 = circleArea(r1);
    area2 = circleArea(r2);
    printf("一个圆台两底面积之和是:%lf\n",area1 + area2);
    return 0;
}

四、形参和实参

1、概念

  • 形参(形式参数):

函数定义时指定的参数,形参是用来接收数据的。函数定义时,系统不会为形参申请内存,只有当函数调用时,系统才会为形参申请内存,用于存储实际参数,并且当函数返回,系统会自动回收为形参申请的内存资源;(本质上所有函数都有return,只不过当我们的函数返回类型是void的时候,return关键字被省略了)

  • 实参(实际参数):

  1. 函数调用时主调函数传递的数据参数(常量、变量、表达式...),实参是传递的数据;
  2. 实参和形参必须类型相同。若不同,按赋值规定自动进行类型转换;
  3. 在C语言中,参数传递遵循单项值传递实参只是将自身的传递给形参,而不是实参本身。形参的值的改变不会影响实参;
  4. 实参与形参在内存中占据不同的内存空间,尽管可能实参和形参名称相同。
    double fun(double a,double b)
    {
        return a+b;
    }
    
    int main()
    {
        int x = 12,y = 13;
        int c = (int)fun(x,y);
        // 通过案例,将int类型赋值给double类型,此时程序不报错,因为此时会发生自动类型转换(隐式转换);
        //通过案例,返回时,将double类型赋值给int类型,此时将满足强制转换需求,需要我们手动转换
    }

举例:输入两个整数,要求用一个函数求出其中的最大数,并在主函数输出此数

代码:

/*
*输入两个整数,要求用一个函数求出其中的最大数,并在主函数输出此数
*/
#include <stdio.h>

/*
*求最大值
*@param x,y都是形式参数,形式参数本身没有意义,需要赋值实际参数
*/
int max(int x,int y)
{
    return (x > y ? x : y);
}

int main()
{
    int a,b,c;
    printf("请输入两个整数:\n");
    scanf("%d,%d",&a,&b);
    c = max(a,b);//这里的a,b就是实际参数
    printf("%d,%d中的最大数是:%d\n",a,b,c);

    return 0;
}

实际效果:

2、函数的返回值

  • 被调函数返回给主调函数的结果数据(可以是变量、常量、表达式,只要有确定值即可)。
  • 返回值类型一般情况下吧需要和函数中return语句返回的数据类型保持一致,如果不一致,以函数定义时指定的返回类型为标准。也就是返回值类型和实际返回值可以存在自动类型转换或者强制类型转换的关系。

举例:输入两个整数,要求用一个函数求出其中的最大数,并在主函数输出此数

/*
*输入两个整数,要求用一个函数求出其中的最大数,并在主函数输出此数
*/
#include <stdio.h>

/*
*求最大值
*@param x,y都是形式参数,形式参数本身没有意义,需要赋值实际参数
*/
double max(int x,int y)
{
    if(x > y)
    {
        return x;
    }
    return y; //当 x > y 不满足时,执行 return y;(两个return只能一次执行一个)
}

int main()
{
    int a,b,c;
    printf("请输入两个整数:\n");
    scanf("%d,%d",&a,&b);
    c = (int)max(a,b);//这里的a,b就是实际参数
    printf("%d,%d中的最大数是:%d\n",a,b,c);

    return 0;
}

举例:返回值和返回值类型不一致时,转换问题

/*
   理解:返回值和返回值类型不一致时,转换问题
 */
#include <stdio.h>
/**
 * 求最大值
 * @param x,y都是形式参数,形式参数本身没有意义,需要赋值实际参数
 */
double max(int x,int y)
{
    return (x > y ? x : y);
}
int main()
{
    int a,b,c;
    printf("请输入两个整数:\n");
    
    scanf("%d,%d",&a,&b);
    
    c = (int)max(a,b);// 这里的a,b就是实际参数
   
    printf("%d,%d中的最大数是:%d\n",a,b,c);
   
    return 0;
    
}

举例:返回值和返回值类型不一致时,转换问题

/**
 * 理解:返回值和返回值类型不一致时,转换问题。
 */
#include <stdio.h>
/**
 * 求最大值
 * @param x,y都是形式参数,形式参数本身没有意义,需要赋值实际参数
 */
int max(int x,int y)
{
    double z;
    z = x > y ? x : y;
    return (int)z;// 将double类型转换为int类型,此时会执行强制转换,如果为了增加代码的可读性,我们可以
手动强转
}
int main()
{
    int a,b,c;
    printf("请输入两个整数:\n");
    
    scanf("%d,%d",&a,&b);
    
    c = (int)max(a,b);// 这里的a,b就是实际参数
   
    printf("%d,%d中的最大数是:%d\n",a,b,c);
   
    return 0;
    
}

五、函数的调用

1、调用方式:

  1. 采用函数调用语句: test() , int result = max(2,4);这时不要求函数值,只要求函数完成一定操作;
  2. 表达式语句: 4 + max(2,4)  ;
  3. 函数参数: printf("%d",max(2,4))  ;

2、在一个函数中调用另一个函数需具备以下条件:

  1. 被调用的函数必须是已经定义的函数;
  2. 若使用库函数,应在本文件开头用#include包含;
  3. 若使用用户定义的函数,而用户函数又在主调函数的后面,则应在主调函数中对被调用的函数进 行声明。声明的作用是把函数名、函数参数的个数和类型等信息通知编译系统,以便在遇到函数时, 编译系统能正确识别函数,并检查函数调用的合法性。

六、函数声明

函数调用时,往往要遵循 先定义后调用 ,但如果我们对函数的调用操作出现在函数的定义之前,则需要对函数进行声明。

函数声明的作用:

是把函数名、函数参数的个数和返回类型等信息通知给编译系统,以便于在遇到函数时,编译系统能正确识别函数,并检查函数调用的合法性。

举例:函数声明的三种情况

①错误
//函数调用错误演示
int main()
{
    int c = add(12,13);//此时会报编译错误,因为函数没有经过声明,所以编译系统无法正确识别函数
    printf("%d\n",c);
}
int add(int x,int y)
{
    return x + y;
}
②正确
//函数调用正确演示
//函数声明和实现放在一起
int add(int x,int y)
{
    return x + y;
}
int main()
{
    int c = add(12,13);
    printf("%d\n",c);
}
③正确
//函数调用正确演示

//函数声明和实现放在一起
int add(int x,int y);

int main()
{
    int c = add(12,13);
    printf("%d\n",c);
}

int add(int x,int y)
{
    return x + y;
}

声明的方式:

  • 函数首部后加上分号 ";" ;
void fun(int a);
  • 函数首部后加上分号,可省略形参名,但不能省略参数类型;
void fun(int);

七、函数的嵌套调用

  • 函数不允许嵌套定义,但允许嵌套使用;
  • 嵌套调用:在被调函数内有去主动去调用其他函数,这样的函数调用方式,称之为嵌套调用。
// 嵌套函数的错误写法,不能嵌套定义函数
int a()
{
    int b()
   {
       ..
   }
   ..
}

举例:编写一个函数,判断给定的3~100正整数是否是素数,若是返回1,否则返回0

/**
 * 需求:编写一个函数,判断给定的3~100正整数是否是素数,若是返回1,否则返回0 
 */
#include <stdio.h>
// 定义一个函数,求素数
int sushu(int n)
{
    int k,i,flag = 1;
    // 素数:只能被1和自身整除的数,需要校验的是2~n-1
    for(i = 2;  i < n-1;i++)
   {
        if(n % i == 0)
       {
            flag = 0;
       }
   }
    
    return flag;
}
// 主函数
int main()
{
    for(int i = 3; i <= 100;i++)
   {
        if(sushu(i)==1)
       {
           printf("%d是素数\n",i);
       }
   }
    
    printf("\n");
    return 0;
    
}

举例:输入四个整数,找出其中最大的数,用函数嵌套来处理,要求每次只能两个数来比较

 /**
*输入四个整数,找出其中最大的数,用函数嵌套来处理,要求每次只能两个数来比较
*/
#include <stdio.h>

//函数声明
int max_2(int,int);
int max_4(int,int,int,int);

//主函数
int main()
{
    int a=12,b=44,c=33,d=16,result;
    result = max_4(12,44,33,16);
    printf("%d,%d,%d,%d中的最大数为:%d\n",a,b,c,d,result);
    
    return 0;
}

int max_2(int a,int b)
{
    return a > b ? a : b;
}

int max_4(int a,int b,int c,int d)
{
    int max;//存储比较的最大数
    max = max_2(a,b);//第一次比较
    max = max_2(max,c);//第二次比较
    max = max_2(max,d);//第三次比较

    return max;
}

八、函数的递归调用

  • 递归调用的含义:在一个函数中,直接或者间接调用的函数本身称之为函数的递归调用。

//直接调用
a() -> a();

//间接调用
a() -> b() -> a();
a() -> b() -> .. -> a();
  • 递归调用的本质:

是一种循环结构,它不同于之前所学的while, do-while,for这样的循环结构,这些结构是借助循环变量,而递归是利用函数自身实现循环结构,如果不加以利用,很容易产生死循环。

  • 递归调用的注意事项:

  1. 递归调用必须要有出口,一定要终止递归(否则会产生死循环);
  2. 对终止条件的判断一定要放在函数递归之前;
  3. 进行函数的递归调用;
  4. 函数递归的同时一定要将函数调用向出口逼近。

举例:年龄的递归调用

 /**
 * 需求:递归案例-有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。
         问第4个人岁数,他说比第3个人大2岁。
         问第3个人,又说比第2个人大2岁。
         问第2个人,说比第1个人大2岁。
         最后问第1个人,他说是10岁。请问第5个人多大。
 */
#include <stdio.h>
/* 求年龄的递归函数 */
int age(int n)
{
    // 存放函数的返回值,也就是年龄
    int c; 
    if(n == 1)
   {
        c = 10;// 第一个人的年龄是10岁
   }
    else if(n > 1)
   {
        c = age(n-1)+2;      // 当前这个人的年龄 = 上一个人的年龄+2
   }
    return c;
}
int main()
{
    printf("%d\n",age(5));
    return 0;
}

举例:用递归函数求n!

 /**
 * 需求:递归案例-求阶乘(n!)
 */
#include <stdio.h>
/* 编写一个函数,用来求阶乘 */
long fac(int n)
{
    // 因为int型表示的数据范围小,所以乘法操作我们使用long来接收计算结果
    long f;
    
    if(n < 0)
   {
        printf("n的范围不能是0以下的数!\n");
   }
    else if(n == 0 || n==1) // 此时不满足阶乘条件
   {
        f = 1; 
   }
    else
   {
        f = fac(n-1)*n;  
   }
    
    return f;
}
int main()
{
    int n;
    printf("请输入一个整数:\n");
    scanf("%d",&n);
    printf("%d!=%ld\n",n,fac(n));
    return 0;
}

九、数组做函数参数

注意:

        当用数组做函数的实际参数时,则形参应该也要用数组/指针变量来接收,但请注意,此次并不 代表传递了数组中所有的元素数据,而是传递了第一个元素的内存地址(数组首地址),形参接收这 个地址后,则形参和实参就代表了同一块内存空间,则形参的数据修改会改变实参的。这种数据传递方式我们可以称之为“引用传递”

        如果用数组做函数形式参数,那么我们提供另一个形参表示数组的元素个数。原因是数组形参代 表的仅仅是实际数组的首地址。也就是说形参只获取到了实际数组元素的开始,并未获取元素的结 束。所以提供另一个形参表示数组的元素个数,可以防止在被调函数对实际数组元素访问的越界。

        但有一个例外,如果是用字符数组做形参,且实际数组中存放的是字符串数据(形参是字符数组,实参是字符串)。则不用表示数组元素的个数的形参,原因是字符串本身会自动结束符\0。

举例:数组元素做函数实参

/**
 * 需求:数组为参数案例-有两个数组a和b,各有10个元素,将它们对应元素逐个地相比(即a[0]与b[0]比,a[1]与b[1]比……)。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素大于a数组中相应元素的数目(例如,a[i]>b]i]6次,b[i]>a[i] 3次,其中i每次为不同的值),则认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的个数。
 */
#include <stdio.h>
/* 定义一个函数,实现两个数的比较 */
int large(int x,int y)
{
    int flag;// 用来存放比较结果
    
    if(x > y) flag = 1;
    else if(x < y) flag = -1;
    else flag = 0;
    return flag;
}
int main()
{   
    // 比较用的两个数组,循环变量,最大,最小,相等
    int a[10],b[10],i,max=0,min=0,k=0;
    
    printf("请给数组a添加十个整型数据:\n");
    for(i = 0;i < sizeof(a)/sizeof(int);i++)
   {
        scanf("%d",&a[i]);
   }    
    printf("\n");
    printf("请给数组b添加十个整型数据:\n");
    for(i = 0;i < sizeof(b)/sizeof(int);i++)
        scanf("%d",&b[i]);
    printf("\n");
    
    // 遍历
    for(i = 0;i < sizeof(a)/sizeof(int);i++)
   {
        if(large(a[i],b[i])==1)
       {
            max++;
       }
        else if(large(a[i],b[i])==0)
       {
            k++;
       }
        else
       {
            min++;
       }
   }
    
    printf("max=%d,min=%d,k=%d\n",max,min,k);
    
    return 0;
}

举例:编写一个函数,用来分别求数组score_1(有5个元素)和数组score_2(有10个元素) 各元素的平均值

/**
 * 需求:数组函数的参数案例-编写一个函数,用来分别求数组score_1(有5个元素)和数组score_2(有10个元素)各元素的平均值 。
 */
#include <stdio.h>
/* 定义一个函数,用来求平均分 */
float avg(float scores[],int len)
{
    int i;// 循环变量
    float aver,sum = scores[0];// 保存平均分和总成绩
    // 遍历集合
    for(i = 1;i < len;i++)
   {
        sum += scores[i];
   }
    
    aver = sum / len;
    return aver;
}
int main()
{
    //准备俩测试数组
    float score_1[5] = {66,34,46,37,97};
    float score_2[10] = {77,88,66,55,65,76,87,98,75,34};
    printf("这个班的平均分是:%6.2f\n",avg(score_1,sizeof(score_1)/sizeof(float)));
    printf("这个班的平均分是:%6.2f\n",avg(score_2,sizeof(score_2)/sizeof(float)));
    return 0;
}

十、变量的作用域

1、引入问题

        我们在函数设计过程中,经常要考虑对参数的设计,换句话说,我们需要考虑函数需要几个参数,需 要什么类型的参数,但我并没有考虑函数是否需要提供参数,如果说函数可以访问到已定义的数据, 则就不需要提供函数形参,那么我么到底要不要提供函数参数,取决于什么?答案就是变量的作用域 (如果函数在变量的作用域范围内,则函数可以直接访问数据)

2、变量的作用域

概念:变量的作用范围,也就是说变量在什么范围是有效的。

3、变量的分类

根据变量的作用域不同,变量可分为全局变量和局部变量:

局部变量
序号局部变量作用域
1形式参数(形参)函数作用域
2函数内定义的变量函数作用域
3复合语句中定义的变量块作用域
4for循环表达式1定义的变量块作用域
全局变量
序号全局变量作用域
1定义在函数之外的变量,也称为外部变量或全程变量从全局变量定义到本源文件的结束

建议在全局变量定义时初始化。如果不初始化,系统会将全局变量初始化为0(0 | \0 |0.0)。

  • 使用全局变量定义时初始化:

        优点:
  1. 利用全局变量可以实现一个函数对外输出的多个结果数据;
  2. 利用全局变量可以减少函数形参个数,从而降低内存消耗,以及因形参传递带来的时间消耗。
        缺点:
  1. 全局变量在程序的整个运行期间,始终占据内存空间,会引起资源消耗;
  2. 过多的全局变量会引起程序的混乱,造成程序结果错误;
  3. 降低程序通用性,特别是当我们进行函数移植时,不仅仅要移植函数,还要考虑全局变量;
  4. 违反了“高内聚,低耦合”的程序设计原则。
总结:我们发现弊大于利,建议尽量减少对全局变量的使用,函数之间要产生联系,仅通过实参形参的方式产生联系。

作用域举例:

举例:

int p=1,q=5;             /*外部变量p,q*/
float f1(int a)          /*定义函数f1*/
{ int b,c;
   …
}
char c1,c2;              /*外部变量c1,c2*/
char f2 (int x, int y)   /*定义函数f2*/
{ int i,j;                                                                 
   …
}
void main ( )            /*主函数*/
{ int m,n;
   …
}  

注意: 如果全局变量(外部变量)和局部变量同名,程序执行的时候, 就近原则 

int a = 10;
int main()
{
    int i = 20;
    printf("%d\n",a); // 20 就近原则
    for(int i = 0;i < 5; i++)
   {
        printf("i=%d ",i); // 0 1 2 3 4 就近原则
   }
}

十一、变量的生命周期

  • 概念:变量在程序运行中的存在时间。
  • 根据变量存在的时间不同,变量可分为静态存储方式动态存储方式

  • 变量的存储类型

变量的完整定义格式:[存储类型] 数据类型 变量列表; 
  • 存储类型

auto

auto存储类型只能修饰局部变量,被auto修饰的局部变量是存储在动态存储区的。auto也是局部变 量默认的存储类型。

int a = 10; 等价于 auto int a = 10;

static

修饰局部变量:局部变量会被存储在静态存储区。局部变量的生命周期被延长,但是作用域不发 生改变。

修改全局变量:全局变量的生命周期不变,但作用域被衰减。一般限制全局变量只能在本文件 内。

demo01.c
#include "demo01.h"
// 全局变量
static int fun_a = 10;
int fun1()
demo02.c
#include "demo01.h"
main()
{
    // 此时fun_a就不能被其他文件访问
    fun_a = 20;
}

extern

外部存储类型:只能修饰全局变量,次全局变量可以被其他文件访问。相当于扩展了全局变量的 作用域。

extern修饰外部变量,往往是外部变量进行声明,声明该变量是在外部文件中定义的;不是变量定 义。

demo01.c
#include "demo01.h"
int fun_a = 10;
int fun1(){..}
demo02.c
#include "demo01.h"
// 声明外部文件的变量
extern int fun_a;
// 声明外部文件的函数
extern int fun1();
main()
{  
    fun_a = 20;
    fun1();
}

register

寄存器存储类型:只能修饰局部变量,用register修饰的局部变量会直接存储到CPU的寄存器中, 往往将循环变量设置为寄存器存储类型。

面试题:

static关键字的作用

  1. . static修饰局部变量,延长其生命周期,但不影响局部变量的作用域;
  2. static修饰全局变量,不影响全局变量的生命周期,会限制全局变量的作用域仅限本文件内使 用;
  3. static修饰函数:此函数就称为内部函数,仅限本文件内调用。 static int funa(){..}

十二、值传递与引用传递

  • 值传递:发生在整型、浮点型、字符型,数据传递,传递的是数值,也就是内存空间只能被当前变量独享。

  • 引用传递:发生在数组、指针、结构体..,数据传递,传递的是地址值,也就是内存空间可以被多个变量共享。

// 值传递(整型、浮点型、字符型..)
fun(int x)
{
    printf("%d\n",x); // x = 10
 x = 20;           // x = 20
}
main()
{
    int a = 10;  // a = 10
 fun(a);
 printf("%d\n",a);// a = 10
 
}
-------------------------------------------------------------------------------------
// 引用传递(数组、指针、结构体..)
fun(int x[10])
{
   printf("%d\n",x[9]);// x[9] = 0
   x[9] = 20;          // x[9] = 20
}
main()
{
   int a[10] = {1,2,3};
   fun(a);
   printf("%d\n",a[9]);// a[9] = 20
}

十二、内部函数与外部函数

  • 内部函数:使用static修饰的函数,称作内部函数,内部函数只能在当前文件中调用;
  • 外部函数:使用extern修饰的函数,称作外部函数,extern是默认的,可以不写,也就是说本质上 我们所写的函数都是外部函数,建议外部函数在被其他文件调用的时候,在其他文件中声明的时 候,加上extern关键字。

十三、作业

1、编程题

1. 编写一个函数,通过输入球的半径,返回球的体积;

代码:

/*
*编写一个函数,通过输入球的半径,返回球的体积;
*/
#include <stdio.h>

#define PI 3.1415926//定义PI

double ballVolume(double r)
{
    return PI * r * r * r;//球的体积
}

int main()
{
    double r,volume;
    printf("请输入球的半径:\n");
    scanf("%lf",&r);//录入球的半径

    volume = ballVolume(r);
    printf("半径为%.4lf的球的体积为%.4lf\n",r,volume);
    return 0;
}

运行效果:

2. 编写一个函数,通过输入一个数字字符,返回该数字;

 代码:

/*
*输入一个数字字符,返回该数字
*/
#include <stdio.h>

//转换函数,使其接受一个字符并返回对应的整数值(如果字符是数字的>话)
char conv(char a)
{
    if(a >= '0' && a <= '9')
    {
        return a - '0';//将字符转换为整数
    }
    else
    {
        return -1;//如果不是数字字符,返回一个错误值
    }
}

//主函数
int main()
{
    char a;//声明变量a来存储输入的数字字符
    int z;//声明变量z来存储转换后的整数值

    printf("请输入一个数字字符:\n");
    scanf("%c",&a);

    z = conv(a);//调用函数并存储结果

    if(z != -1)
    {
        printf("根据您所属入的数字字符,返回该数字:%d\n",z);
    }
    else
    {
        printf("输入的不是数字字符。\n");
    }

    return 0;
}

运行效果:

3. 编写一个函数,输入四个数据分别表示2个点的x,y坐标,返回两点之间的距离;

  代码:

 /*
*输入四个数据分别表示2个点的x,y坐标,返回两点之间的距离
*/
#include <stdio.h>
#include <math.h>

//求两坐标点之间举例
double dist(double x1,double y1,double x2,double y2)
{
    double dx = x2 - x1;
    double dy = y2 - y1;
    if(dx != 0 && dy != 0)
    {
        double result = sqrt(dx*dx + dy*dy);
        return result;
        //return dist;
    }
    //double result = sqrt(dx*dx + dy*dy);
    else
    {
        return 1;
    }
}

double main()
{
    double a1,b1,a2,b2,result;
    printf("请输入两个点的坐标:(%lf,%lf),(%lf,%lf)\n",a1,b1,a2,b2);
    scanf("%lf,%lf,%lf,%lf",&a1,&b1,&a2,&b2);

    result = dist(a1,b1,a2,b2);

    printf("您所输入的两个点的坐标间的距离为:%lf\n",result);

    //return 0;
}

运行效果:

4. 编写一个函数,通过参数输入一个整型数,返回该数各位上数字的平方和;

  代码:

/*
*通过参数输入一个整型数,返回该数各位上数字的平方和
*/
#include <stdio.h>

int squareSum(int a)
{
    int sum = 0;

    while(a != 0)
    {
        int b = a % 10;//最高位
        sum += b * b;
        a /= 10;//去掉最低位数字
    }
    return sum;
}

int main(int a)
{
    int result;
    printf("请输入一个整数:\n");
    scanf("%d",&a);

    result = squareSum(a);

    printf("您所输入的整数各位上的平方和为:%d\n",result);

    return 0;
}

运行效果:

5. 编写一个函数,通过参数输入x的值,计算如下的数学函数值,当 x>5时, f(x) = 4x+7;否则 f(x) = -2x+3,返回结果值;

  代码:

 /*
*通过参数输入x的值,计算如下的数学函数值,当x>5时,f(x)=4x+7;否则f(x)=-2x+3,返回
结果值;
*/
#include <stdio.h>
#include <math.h>

double fun(double a)
{
    if(a > 5)
    {
        double sum = 4 * a + 7;
        return sum;
    }
    else
    {
        double sum = -2 * a + 3;
        return sum;
    }
}

double main(double a)
{
    double result;
    printf("请您输入一个整数:\n");
    scanf("%lf",&a);

    result = fun(a);

    printf("您所输入的值的计算结果为:%.2lf\n",result);

    return 0;
}

运行效果:

6. 设计一个函数,用来求出多个数据的平均值;

  代码:

/*
*输入数据,求平均值
*/

#include <stdio.h>

double aver(int arr[], int length);//求均值函数的声明  

int main()//主函数 
{
    int arr[] = {43,52,76,45,65,23,98};//创建数组  
    int length = sizeof(arr) / sizeof(arr[0]); // 计算数组所占空间的大小  

    double ave = aver(arr, length);//引用定义的函数  
    printf("您所输入的数据的平均值为: %.2f\n", ave);

    return 0;
}

double aver(int arr[], int length)//求均值函数的定义 
{
    double sum = 0.0; // 用来给数组中的元素求和
    for (int i = 0; i < length; i++)
    {
        sum += arr[i]; // 累加数组中的每个元素  
    }
    return sum / length; // 返回平均值  
}

运行效果:

7. 设计一个函数,用来查找一个字符串中某个字符的位置;

  代码:

/*
*设计一个函数,来查找一个字符串中某个字符的位置
*/
#include <stdio.h>

int find(const char *arr,char let);//const用于修饰常量静态字符串

int main()
{
    char arr[]="hello,xian!";//定义数组,初始化
    char let = 'x';
    int pos = find(arr,let);

    if(pos != -1)
    {
        printf("您所输入的字符%c的位置为:%d\n",let,pos);
    }
    else
    {
        printf("没有找到该字符!\n");
    }

    return 0;

}

int find(const char *arr,char let)
{
    int i=0;
    while(arr[i] != '\0')//遍历字符串直到结束符'\0'
    {
        if(arr[i] == let)
        {
            return i;
        }
        i++;
    }
    return -1;
}

运行效果:

8. 设计一个函数,把一个整型数字转成对应的字符串格式;

  代码:

 /*
*设计一个函数,把一个整型数字转成对应的字符串格式
*/
#include <stdio.h>

int conv(int a)
{
    if(a >= 0 && a <= 9)
    {
        return a - 0;
    }
    else
    {
        return -1;
    }
}

char main()
{
    int a;
    char z;

    printf("请输入一个整型数字:\n");
    scanf("%d",&a);

    z = conv(a);

    if(z != -1)
    {
        printf("根据您所输入的数字,返回该字符:%c\n",z);
    }
    else
    {
        printf("输入不是整型数字!\n");
    }
    return 0;
}

运行效果:

9. 设计一个函数,统计字符串中大写字母的个数;

  代码:

 /*
*设计一个函数,统计字符串中大写字母的个数
*/
#include <stdio.h>
#include <stdbool.h>//引入stdbool.h以使用bool类型

int find(const char *arr)//函数定义
{
    int num = 0;//初始化计数的个数

    while(*arr != '\0')//遍历到字符串结束符'\0'停止
    {
        //printf("%c,%d\n",arr,arr);
        if(*arr <= 'Z' && *arr >= 'A')//比较字符串中字母是否为大写字母
        {
            num++;//大写字母计数+1
        }
        arr++;
    }
    return num;
}

int main()
{
    char arr[]="Hello,Xian!";
    int num = find(arr);
    printf("您所输入的字符串中的大写字母的数量为:%d\n",num);
    return 0;
}

运行效果:

10. 设计函数,实现strcmp 的功能;

  代码:

/*
*设计函数,实现strcmp的功能,即:字符串比较  strcmp(字符串1,字符串2)
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

//const 数据类型 常量名;
// 使用const修饰的变量是常量,表示一旦赋值就不能改变,主要是防止变量被恶意篡改
int compare(int i,const char username[i],const char stip[i])//定义函数
{
    i =0;
    //判断数组中是否存在字符,如果不存在就跳出循环
    while(username[i] && stip[i])
    {
        if(username[i] != stip[i])
        {
            return username[i] - stip[i];
            //此时会产生两个结果,一个正数,一个负数
        }
        i++;
    }
    return username[i] - stip[i];//此时会产生一个结果,0
}

int main()
{
    const char username[] = "shenshenshen";
    const char stip[] = "shenshenshen";

    int i;
    int result = compare(i,username,stip);

    if(result < 0)
    {
        printf("上次登录的账户名'%s' 小于 您输入的账户名'%s',不匹配!\n",username,stip);
    }
    else if(result > 0)
    {
        printf("上次登录的账户名'%s' 大于 您输入的账户名'%s',不匹配!\n",username,stip);
    }
    else
    {
        printf("上次登录的账户名'%s' 和 您输入的账户名'%s' 相同,匹配成功!\n",username,stip);
    }
    return 0;
}

运行效果:

11. 编写函数,用于判断输入的字符是不是个数字。是返回1,不是返回0.

  代码:

/*
* 编写函数,用于判断输入的字符是不是个数字。是返回1,不是返回0.
*/
#include <stdio.h>
#include <stdbool.h>

int change()
{
    printf("请您输入一个字符:\n");
    char b;
    scanf("%c",&b);
    //int r = judge(b);
    //printf("%d\n",n);
    printf("您输入的字符%c对应的ASCII码值为:%d\n",b,b);
    return b;
}

int judge(char a)//定义判断函数
{
    int n;

    while(1)
    {
        if(a >= 48 && a <= 57)
        {
            n = 1;
            break;
            //printf("%d\n",n);
        }
        else
        {
            n = 0;
            break;
            //printf("%d\n",n);
        }
    }
    printf("%d\n",n);
//    return n;
}

int main()
{
    char x,a1,b,a;
    a1 = change(b);
    int n = judge(a1);
    //printf("%d\n",n);
    return 0;
}

运行效果:

12. 设计一程序,实现一个简单的计算器。

要求:有菜单12函数 和加、减、乘、除的函数 主函数调用这些函数实现程序的功能.要求菜单函数能够输出如下的界面

1、加法 2、减法 3、乘法 4、除法 0.退出

  代码:

 /*
* 实现简易的计算器:有菜单函数和加减乘除的函数,主函数调用这些函>数实现程序的功能*要求菜单函数能够输出如下的界面:1、加法 2、减法 3、乘法 4、除法 0、退出;
*/
#include <stdio.h>

void menu()//菜单
{
    printf("\n1.加法 2.减法 3.乘法 4.除法 0.退出\n");
}

int choice()//选项
{
    int n;
    printf("请输入选项(0-4):\n");
    scanf("%d",&n);
    return n;
}

void add()//加
{
    double a;
    double b;
    double re = a+b;
//    return a+b;
}

void subt()//减
{
    double a;
    double b;
    double re = a - b;
//    return re;
}
void mult()//乘
{
    double a;
    double b;
    double re = a * b;
//    return re;
}
void div()//除
{
    double a;
    double b;
    double re = a / b;
//    return re;
}
//double calculator(double a,double b)
int main(double x,double y)//主函数
{
    printf("*********简易计算器 v1.0***********\n");

    int n;//定义运算符的变量
    double re;//定义结果

    while(1)
    {
        menu();
        n = choice();

        if(n == 0)
        {
            printf("退出程序!\n");
            break;
        }
        printf("请输入两个整数:\n");
        scanf("%lf,%lf",&x,&y);

        switch(n)
        {
            case 1:
                add(x,y);
                printf("计算结果为:%.2lf\n",x+y);
                break;
            case 2:
            //printf("%.2lf - %.2lf = %.2lf\n",a,b,a-b);
                subt(x,y);
                printf("计算结果为:%.2lf\n",x-y);
                break;
            case 3:
            //printf("%.2lf * %.2lf = %.2lf\n"a,b,a*b);
                mult(x,y);
                printf("计算结果为:%.2lf\n",x*y);
                break;
            case 4:
                if(y == 0)
                {
                    printf("除数不能为0!\n");
                    break;
                }
                else
                {
                //printf("%.2lf / %.2lf = %.2lf\n",a,b,a/b);
                    div(x,y);
                    printf("计算结果为:%.2lf\n",x/y);
                    break;
                }
            //case 0:
                //printf("退出!\n");
                //break;
            default:
                printf("输入错误!\n");
        }
    }
        return 0;
}


运行效果:

13. 设计函数实现冒泡排序;

  代码:

 /*
* 设计函数实现冒泡排序
*/
#include <stdio.h>

int bubblingSort(int arr[],int l)//定义冒泡排序函数
{
    int i,j,temp;
//    int length = sizeof(arr) / sizeof(arr[0]); 
    for(i = 0;i < l-1;i++)//外层循环,比较轮数
    {
        for(j = 0;j < l-1-i;j++)//内层循环,每轮中比较的次数
        {
            if(arr[j] > arr[j+1])
            {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }

    printf("冒泡排序后遍历数组:\n");

    for(i = 0;i < l;i++)
    {
        printf("%6d",arr[i]);
    }

//    return arr[i];
}


int main()
{
    int i;
    int arr[] = {21,432,54,763,44,66,87,56,89,96};
//    printf("您输入的10个整数为:%d\n",arr[i]);

    int length = sizeof(arr) / sizeof(int);

//    for(i1 = 0;i1 < length ;i1++)
//    {
//        scanf("%d",&arr[i1]);
//    }
//    printf("\n");

    bubblingSort(arr,length);

    printf("\n");

    return 0;
}

运行效果:

2、思考题【选做】

14. 编写一个函数,将数组中的数据首尾互换。

  代码:

 

运行效果:

  • 26
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值