首页 > 编程语言 >C语言程序设计-8 函 数

C语言程序设计-8 函 数

时间:2024-06-19 22:29:32浏览次数:15  
标签:include 函数 int C语言 ++ printf 程序设计 main

8.1 概述

在前面已经介绍过,C源程序是由函数组成的。虽然在前面各章的程序中大都只有一个
主函数 main(),但实用程序往往由多个函数组成。函数是C源程序的基本模块,通过对函
数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。C语言不仅
提供了极为丰富的库函数(如 Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自
己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来
使用函数。可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函
数式语言。
由于采用了函数模块式的结构,C语言易于实现结构化程序设计。使程序的层次结构清
晰,便于程序的编写、阅读、调试。
在C语言中可从不同的角度对函数分类。

  1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。
  1. 库函数:由C系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序
    前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用
    到 printf、scanf、getchar、putchar、gets、puts、strcat 等函数均属此类。
  2. 用户定义函数:由用户按需要写的函数。对于用户自定义函数,不仅要在程序中定
    义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能
    使用。
  1. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为
    有返回值函数和无返回值函数两种。
  1. 有返回值函数:此类函数被调用执行完后将向调用者返回一个执行结果,称为函数
    返回值。如数学函数即属于此类函数。由用户定义的这种要返回函数值的函数,必
    须在函数定义和函数说明中明确返回值的类型。
  2. 无返回值函数:此类函数用于完成某项特定的处理任务,执行完成后不向调用者返
    回函数值。这类函数类似于其它语言的过程。由于函数无须返回值,用户在定义此
    类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。
  1. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。
  1. 无参函数:函数定义、函数说明及函数调用中均不带参数。主调函数和被调函数之
    间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函
    数值。
  2. 有参函数:也称为带参函数。在函数定义及函数说明时都有参数,称为形式参数(简
    称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函
    数调用时,主调函数将把实参的值传送给形参,供被调函数使用。
  1. C语言提供了极为丰富的库函数,这些库函数又可从功能角度作以下分类。
  1. 字符类型分类函数:用于对字符按 ASCII 码分类:字母,数字,控制字符,分隔符,
    大小写字母等。
  2. 转换函数:用于字符或字符串的转换;在字符量和各类数字量(整型,实型等)之间进行转换;在大、小写之间进行转换。
  3. 目录路径函数:用于文件目录和路径操作。
  4. 诊断函数:用于内部错误检测。
  5. 图形函数:用于屏幕管理和各种图形功能。
  6. 输入输出函数:用于完成输入输出功能。
  7. 接口函数:用于与 DOS,BIOS 和硬件的接口。
  8. 字符串函数:用于字符串操作和处理。
  9. 内存管理函数:用于内存管理。
  10. 数学函数:用于数学函数计算。
  11. 日期和时间函数:用于日期,时间转换操作。
  12. 进程控制函数:用于进程管理和控制。
  13. 其它函数:用于其它各种功能。
    以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需
    要一个较长的学习过程。应首先掌握一些最基本、最常用的函数,再逐步深入。由于课时关
    系,我们只介绍了很少一部分库函数,其余部分读者可根据需要查阅有关手册。
    还应该指出的是,在C语言中,所有的函数定义,包括主函数 main 在内,都是平行的。
    也就是说,在一个函数的函数体内,不能再定义另一个函数,即不能嵌套定义。但是函数之
    间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数还可以自己调用自
    己,称为递归调用。
    main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。因此,C程序
    的执行总是从 main 函数开始,完成对其它函数的调用后再返回到 main 函数,最后由 main
    函数结束整个程序。一个C源程序必须有,也只能有一个主函数 main。

8.2 函数定义的一般形式

【例 8.1】

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

int max(int a, int b)
{
    if (a > b)
        return a;
    else
        return b;
}
main()
{
    int max(int a, int b);
    int x, y, z;
    printf("input two numbers:\n");
    scanf("%d%d", &x, &y);
    z = max(x, y);
    printf("maxmum=%d", z);
}

8.3 函数的参数和函数的值

8.3.1 形式参数和实际参数

【例 8.2】可以说明这个问题。

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

main()
{
    int n;
    printf("input number\n");
    scanf("%d", &n);
    s(n);
    printf("n=%d\n", n);
}
int s(int n)
{
    int i;
    for (i = n - 1; i >= 1; i--)
        n = n + i;
    printf("n=%d\n", n);
}

8.3.2 函数的返回值

8.4 函数的调用

8.4.1 函数调用的一般形式

8.4.2 函数调用的方式

【例 8.3】

#include <stdio.h>

main()
{
    int i = 8;
    printf("%d\n%d\n%d\n%d\n", ++i, --i, i++, i--);
}

8.4.3 被调用函数的声明和函数原型

8.5 函数的嵌套调用

【例 8.4】计算s=(2^2)! + (3^2)!;

#include <stdio.h>

long f1(int p)
{
    int k;
    long r;
    long f2(int);
    k = p * p;
    r = f2(k);
    return r;
}
long f2(int q)
{
    long c = 1;
    int i;
    for (i = 1; i <= q; i++)
        c = c * i;
    return c;
}
main()
{
    int i;
    long s = 0;
    for (i = 2; i <= 3; i++)
        s = s + f1(i);
    printf("\ns=%ld\n", s);
}

8.6 函数的递归调用

【例 8.5】用递归法计算 n!
用递归法计算 n!可用下述公式表示:
n!=1 (n=0,1)
n×(n-1)! (n>1)
按公式可编程如下:

#include <stdio.h>

long ff(int n)
{
    long f;
    if (n < 0)
        printf("n<0,input error");
    else if (n == 0 || n == 1)
        f = 1;
    else
        f = ff(n - 1) * n;
    return (f);
}
main()
{
    int n;
    long y;
    printf("\ninput a inteager number:\n");
    scanf("%d", &n);
    y = ff(n);
    printf("%d!=%ld", n, y);
}

【例 8.6】Hanoi 塔问题

一块板上有三根针,A,B,C。A 针上套有 64 个大小不等的圆盘,大的在下,小的在上。
如图 5.4 所示。要把这 64 个圆盘从 A 针移动 C 针上,每次只能移动一个圆盘,移动可以借
助 B 针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求移动的步
骤。
本题算法分析如下,设 A 上有 n 个盘子。
如果 n=1,则将圆盘从 A 直接移动到 C。
如果 n=2,则:
1.将 A 上的 n-1(等于 1)个圆盘移到 B 上;
2.再将 A 上的一个圆盘移到 C 上;
3.最后将 B 上的 n-1(等于 1)个圆盘移到 C 上。
如果 n=3,则:
A. 将 A 上的 n-1(等于 2,令其为 n)个圆盘移到 B(借助于 C),步骤如下: (1)将 A 上的 n-1(等于 1)个圆盘移到 C 上。
(2)将 A 上的一个圆盘移到 B。
(3)将 C 上的 n-1(等于 1)个圆盘移到 B。 B. 将 A 上的一个圆盘移到 C。 C. 将 B 上的 n-1(等于 2,令其为 n)个圆盘移到 C(借助 A),步骤如下:
(1)将 B 上的 n-1(等于 1)个圆盘移到 A。 (2)将 B 上的一个盘子移到 C。 (3)将 A 上的 n-1(等于 1)个圆盘移到 C。
到此,完成了三个圆盘的移动过程。
从上面分析可以看出,当 n 大于等于 2 时,移动的过程可分解为三个步骤:
第一步 把 A 上的 n-1 个圆盘移到 B 上;
第二步 把 A 上的一个圆盘移到 C 上;
第三步 把 B 上的 n-1 个圆盘移到 C 上;其中第一步和第三步是类同的。
当 n=3 时,第一步和第三步又分解为类同的三步,即把 n-1 个圆盘从一个针移到另一 个针上,这里的 n=n-1。 显然这是一个递归过程,据此算法可编程如下:

#include <stdio.h>

move(int n, int x, int y, int z)
{
    if (n == 1)
        printf("%c-->%c\n", x, z);
    else
    {
        move(n - 1, x, z, y);
        printf("%c-->%c\n", x, z);
        move(n - 1, y, x, z);
    }
}
main()
{
    int h;
    printf("\ninput number:\n");
    scanf("%d", &h);
    printf("the step to moving %2d diskes:\n", h);
    move(h, 'a', 'b', 'c');
}

8.7 数组作为函数参数

【例 8.7】判别一个整数数组中各元素的值,若大于 0 则输出该值,若小于等于 0 则输出 0
值。编程如下:

#include <stdio.h>

void nzp(int v)
{
    if (v > 0)
        printf("%d ", v);
    else
        printf("%d ", 0);
}
main()
{
    int a[5], i;
    printf("input 5 numbers\n");
    for (i = 0; i < 5; i++)
    {
        scanf("%d", &a[i]);
        nzp(a[i]);
    }
}

【例 8.8】数组 a 中存放了一个学生 5 门课程的成绩,求平均成绩。

#include <stdio.h>

float aver(float a[5])
{
    int i;
    float av, s = a[0];
    for (i = 1; i < 5; i++)
        s = s + a[i];
    av = s / 5;
    return av;
}
void main()
{
    float sco[5], av;
    int i;
    printf("\ninput 5 scores:\n");
    for (i = 0; i < 5; i++)
        scanf("%f", &sco[i]);
    av = aver(sco);
    printf("average score is %5.2f", av);
}

【例 8.9】题目同 8.7 例。改用数组名作函数参数。

#include <stdio.h>

void nzp(int a[5])
{
    int i;
    printf("\nvalues of array a are:\n");
    for (i = 0; i < 5; i++)
    {
        if (a[i] < 0)
            a[i] = 0;
        printf("%d ", a[i]);
    }
}
main()
{
    int b[5], i;
    printf("\ninput 5 numbers:\n");
    for (i = 0; i < 5; i++)
        scanf("%d", &b[i]);
    printf("initial values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
    nzp(b);
    printf("\nlast values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
}

【例 8.10】如把例 8.9 修改如下:

#include <stdio.h>

void nzp(int a[8])
{
    int i;
    printf("\nvalues of array aare:\n");
    for (i = 0; i < 8; i++)
    {
        if (a[i] < 0)
            a[i] = 0;
        printf("%d ", a[i]);
    }
}
main()
{
    int b[5], i;
    printf("\ninput 5 numbers:\n");
    for (i = 0; i < 5; i++)
        scanf("%d", &b[i]);
    printf("initial values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
    nzp(b);
    printf("\nlast values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
}

【例 8.11】

#include <stdio.h>

void nzp(int a[], int n)
{
    int i;
    printf("\nvalues of array a are:\n");
    for (i = 0; i < n; i++)
    {
        if (a[i] < 0)
            a[i] = 0;
        printf("%d ", a[i]);
    }
}
main()
{
    int b[5], i;
    printf("\ninput 5 numbers:\n");
    for (i = 0; i < 5; i++)
        scanf("%d", &b[i]);
    printf("initial values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
    nzp(b, 5);
    printf("\nlast values of array b are:\n");
    for (i = 0; i < 5; i++)
        printf("%d ", b[i]);
}

本程序 nzp 函数形参数组 a 没有给出长度,由 n 动态确定该长度。在 main 函数中,函
数调用语句为 nzp(b,5),其中实参 5 将赋予形参 n 作为形参数组的长度。
d. 多维数组也可以作为函数的参数。在函数定义时对形参数组可以指定每一维的
长度,也可省去第一维的长度。因此,以下写法都是合法的。
int MA(int a[3][10])

int MA(int a[][10])。

8.8 局部变量和全局变量

8.8.1 局部变量

【例 8.12】

#include <stdio.h>

main()
{
    int i = 2, j = 3, k;
    k = i + j;
    {
        int k = 8;
        printf("%d\n", k);
    }
    printf("%d\n", k);
}

8.8.2 全局变量

【例 8.13】输入正方体的长宽高 l,w,h。求体积及三个面 xy,xz,y*z 的面积。

#include <stdio.h>

int s1, s2, s3;
int vs(int a, int b, int c)
{
    int v;
    v = a * b * c;
    s1 = a * b;
    s2 = b * c;
    s3 = a * c;
    return v;
}
main()
{
    int v, l, w, h;
    printf("\ninput length,width and height\n");
    scanf("%d%d%d", &l, &w, &h);
    v = vs(l, w, h);
    printf("\nv=%d,s1=%d,s2=%d,s3=%d\n", v, s1, s2, s3);
}

【例 8.14】外部变量与局部变量同名。

#include <stdio.h>

int a = 3, b = 5; /*a,b 为外部变量*/
max(int a, int b) /*a,b 为外部变量*/
{
    int c;
    c = a > b ? a : b;
    return (c);
}
main()
{
    int a = 8;
    printf("%d\n", max(a, b));
}

8.9 变量的存储类别

8.9.1 动态存储方式与静态动态存储方式

8.9.2 auto 变量

8.9.3 用 static 声明局部变量

【例 8.15】考察静态局部变量的值。

#include <stdio.h>

f(int a)
{
    auto b = 0;
    static c = 3;
    b = b + 1;
    c = c + 1;
    return (a + b + c);
}
main()
{
    int a = 2, i;
    for (i = 0; i < 3; i++)
        printf("%d", f(a));
}

对静态局部变量的说明:

  1. 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间
    都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数
    调用结束后即释放。
  2. 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时
    进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
  3. 如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值 0(对
    数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是
    一个不确定的值。

【例 8.16】打印 1 到 5 的阶乘值。

#include <stdio.h>

int fac(int n)
{
    static int f = 1;
    f = f * n;
    return (f);
}
main()
{
    int i;
    for (i = 1; i <= 5; i++)
        printf("%d!=%d\n", i, fac(i));
}

输出结果:

1!=1
2!=2
3!=6
4!=24
5!=120

8.9.4 register 变量

【例 8.17】使用寄存器变量。

#include <stdio.h>

int fac(int n)
{
    register int i, f = 1;
    for (i = 1; i <= n; i++)
        f = f * i return (f);
}
main()
{
    int i;
    for (i = 0; i <= 5; i++)
        printf("%d!=%d\n", i, fac(i));
}

输出结果

1!=1
2!=2
3!=6
4!=24
5!=120

说明:

  1. 只有局部自动变量和形式参数可以作为寄存器变量;
  2. 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
  3. 局部静态变量不能定义为寄存器变量。

8.9.5 用 extern 声明外部变量

【例 8.18】用 extern 声明外部变量,扩展程序文件中的作用域。
函数声明

// header.h
extern void printMessage(void); // 声明函数

// file1.c
#include "header.h"
void printMessage() {
    printf("Hello, World!\n");
}

// main.c
#include "header.h"
int main() {
    printMessage(); // 使用声明的函数
    return 0;
}

变量声明

// file1.c
int globalVar = 10; // 定义并初始化全局变量

// file2.c
extern int globalVar; // 声明globalVar是在别处定义的

标签:include,函数,int,C语言,++,printf,程序设计,main
From: https://blog.csdn.net/qq_43071699/article/details/139726493

相关文章

  • linux下C语言如何操作文件(四)
    structstring_segment*parse_path(constchar*file_path)函数,解析路径,将一个linux的路径分成两部分路径和文件名。返回一个字符串分段接头体指针,里边有三个字符串。第0个是原始的file_path,第二个是已经拆分好的路径,第三个是文件名部分。代码如下:/***解析linux下的路径......
  • C语言犯错集锦
    .h和.c文件全局变量定义和声明报错:error#10056:symbol"_bChannel"redefined:firstdefinedin"./src/Source/schedule.obj";redefinedin"./src/Source/main.obj"//错误:仅在.h中定义并初始化//正确://全局变量:需要在.h中用extern声明,在.c中定义并初始化,如果是......
  • 汇编语言程序设计 - 显示EXE文件的头信息
    一、问题描述本程序旨在读取并显示一个DOS可执行文件(.EXE文件)的头信息。具体来说,该程序需要打开一个名为“xxx.exe”的文件,读取其头部信息并将其逐项打印出来,并在每个信息前面加上相应的标注,使输出的信息清晰易读。二、数据结构程序使用的主要数据结构包括:文件名字符串:存......
  • 汇编语言程序设计 - 统计字符串中非数字字符的数
    80x86汇编习题题目描述:从键盘输入一系列以$为结束符的字符串,然后对其中的非数字字符计数,并显示出计数结果。思路:有两种思路,一种是先输出,后计数;另一种是边输入,边计数。本文是用前者。1,逐个字符输入到BUFF,指针后移,判断是否为'$',是则结束输入2,逐个判断数组内容是否为数字,是则......
  • 汇编语言程序设计 - 将当前目录下文件 FIE10.TXT 的所有小写字母改为大写字母,然后拷贝
    80x86汇编题目题目描述:编写一个程序,将当前目录下文件FIE10.TXT 的所有小写字母改为大写字母,然后拷贝到当前目录文件FILE20.TXT。思路:1,分别打开两个文件,保存文件句柄2,读取FILE10文件的一个字节到BUF内存中。3,判断是否为小写。非小写字母直接写入到FILE20文件中,小写字母......
  • 【操作系统】实验九 设备驱动程序设计
    一、实验目的分析一个典型的块设备RAM-DISK的驱动程序,学习编写设备驱动程序的一般过程。将这个RAM-DISK的程序改造成为U盘的驱动程序,并通过它来使用你的U盘。二、实验内容仔细阅读上一节中radimo的源代码,将radimo驱动程序改写成为一个U盘驱动程序。在检查时,你需要当场独......
  • C语言 计算平闰年,某个月有多少天
    #include<stdio.h>intisLeapYear(intyear){   if((year%4==0&&year%100!=0)||(year%400==0))       return1; //闰年返回1   return0; //平年返回0}intgetDaysInMonth(intyear,intmonth){   intdays;   sw......
  • C语言开发日志,问题记录(长期更新版本)
    一、程序存储与占用1.编译后内存分配编译后的Code(代码),RO-data(只读,譬如const),RW-data(读写,初始化非0的全局变量),存储在ROM(flash)中,ZI-data(初始化为0或者未初始化的变量),运行时ROM占用是Code+RO-data+RW-data运行时RAM占用是RO-data+RW-data+ZI-data;RW和ZI会被......
  • C语言实现三子棋游戏
    三子棋我们再熟悉不过了,但是如何用C语言制作三子棋游戏呢?首先我们用*号代表玩家下棋;我们用#号代表电脑下棋;接着我们可以通过输入坐标来控制棋子的输入,而电脑通过随机数的来下棋。用模块化编程,将一个游戏分成3个文件来制作,分别为主文件test.c来执行主函数main、其次是注册函数......
  • 【C语言】信号
    【C语言】信号信号1.信号状态2.信号处理方式3.信号注册相关函数4.信号集相关函数最后信号1.信号状态信号有三种状态:产生、未决和递达信号产生方式:按键产生,ctrl+c产生中断信号SIGINT,ctrl+\产生退出信号SIGQUIT并生成core文件,ctrl+z产生停止信号SIGSTO......