C语言编程中函数的基本学习教程

C 语言中的函数等价于 Fortran 语言中的子程序或函数,也等价于 Pascal 语言中的过程或函数。函数为计算的封装提供了一种简便的方法,此后使用函数时不需要考虑它是如何实现的。使用设计正确的函数,程序员无需考虑功能是如何实现的,而只需知道它具有哪些功能就够了。在 C 语言中可以简单、方便、高效地使用函数。我们经常会看到在定义后仅调用了一次的短函数,这样做可以使代码段更清晰易读。

到目前为止,我们所使用的函数(如 printf、getchar 和 putchar 等)都是函数库中提供的函数。现在,让我们自己动手来编写一些函数。C 语言没有像 Fortran 语言一样提供类似于**的求幂运算符,我们现在通过编写一个求幂的函数 power(m, n)来说明函数定义的方法。power(m, n)函数用于计算整数 m 的 n 次幂,其中 n 是正整数。对函数调用 power(2,5)来说,其结果值为 32。该函数并非一个实用的求幂函数,它只能处理较小的整数的正整数次幂,但这对于说明问题已足够了。(标准库中提供了一个计算 xy 的函数 pow(x, y)。)

下面是函数 power(m, n)的定义及调用它的主程序,这样我们可以看到一个完整的程序结构。

#include <stdio.h>
int power(int m, int n);

/* test power function */
main()
{
 int i;
 for (i = 0; i < 10; ++i)
 printf("%d %d %d\n", i, power(2,i), power(-3,i));
 return 0;
}

/* power: raise base to n-th power; n >= 0 */
int power(int base, int n)
{
 int i, p;
 p = 1;
 for (i = 1; i <= n; ++i)
 p = p * base;
 return p;
}

函数定义的一般形式为:

返回值类型 函数名(0 个或多个参数声明)
{
 声明部分
 语句序列
}

函数定义可以以任意次序出现在一个源文件或多个源文件中,但同一函数不能分割存放在多个文件中。如果源程序分散在多个文件中,那么,在编译和加载时,就需要做更多的工作,但这是操作系统的原因,并不是语言的属性决定的。我们暂且假定将 main 和 power 这两个函数放在同一文件中,这样前面所学的有关运行 C 语言程序的知识仍然有效。

main 函数在下列语句中调用了两次 power 函数:printf("%d %d %d\n", i, power(2, i), power(-i, 3)); 每次调用时,main 函数向 power 函数传递两个参数;在调用执行完成时,power 函数向 main 函数返回一个格式化的整数并打印。在表达式中,power(2, i)同 2 和 i 一样都是整数

power 函数的第一行语句 int power(int base, int n) 声明参数的类型、名字以及该函数返回结果的类型。power 函数的参数使用的名字只在 power 函数内部有效,对其它任何函数都是不可见的:其它函数可以使用与之相同的参数名字而不会引起冲突。变量 i 与 p 也是这样:power 函数中的 i 与 main 函数中的 i 无关。

我们通常把函数定义中圆括号内列表中出现的变量称为形式参数,而把函数调用中与形式参数对应的值称为实际参数。

power 函数计算所得的结果通过 return 语句返回给 main 函数。关键字 return 的后面可以跟任何表达式,形式为: return 表达式;

函数不一定都有返回值。不带表达式的 return 语句将把控制权返回给调用者,但不返回有用的值。这等同于在到达函数的右终结花括号时,函数就“到达了尽头”。主调函数也可以忽略函数返回的值。

读者可能已经注意到,main 函数的末尾有一个 return 语句。由于 main 本身也是函数,因此也可以向其调用者返回一个值,该调用者实际上就是程序的执行环境。一般来说,返回值为 0 表示正常终止,返回值为非 0 表示出现异常情况或出错结束条件。为简洁起见,前面的 main 函数都省略了 return 语句,但我们将在以后的 main 函数中包含 return 语句,以提醒大家注意,程序还要向其执行环境返回状态。

出现在 main 函数之前的声明语句 int power(int m, int n); 表明 power 函数有两个 int 类型的参数,并返回一个 int 类型的值。这种声明称为函数原型,它必须与 power 函数的定义和用法一致。如果函数的定义、用法与函数原型不一致,将出现错误。

函数原型与函数声明中参数名不要求相同。事实上,函数原型中的参数名是可选的,这样上面的函数原型也可以写成以下形式: int power(int, int);

但是,合适的参数名能够起到很好的说明性作用,因此我们在函数原型中总是指明参数名。

回顾一下,ANSI C 同较早版本 C 语言之间的最大区别在于函数的声明与定义方式的不同。按照 C 语言的最初定义,power 函数应该写成下列形式:

/* power: raise base to n-th power; n >= 0 */
/* (old-style version) */
power(base, n)
int base, n;
{
 int i, p;
 p = 1;
 for (i = 1; i <= n; ++i)
 p = p * base;
 return p;
}

其中,参数名在圆括号内指定,参数类型在左花括号之前声明。如果没有声明某个参数的类型,则默认为 int 类型。函数体与 ANSI C 中形式相同。

在 C 语言的最初定义中,可以在程序的开头按照下面这种形式声明 power 函数:int power();

函数声明中不允许包含参数列表,这样编译器就无法在此时检查 power 函数调用的合法性。事实上,power 函数在默认情况下将被假定返回 int 类型的值,因此整个函数的声明可以全部省略。

在 ANSI C 中定义的函数原型语法中,编译器可以很容易检测出函数调用中参数数目和类型方面的错误。ANSI C 仍然支持旧式的函数声明与定义,这样至少可以有一个过渡阶段。但我们还是强烈建议读者:在使用新式的编译器时,最好使用新式的函数原型声明方式。

下面给出MFC上的实现:

void CNowaMagic_MFCDlg::OnBnClickedOk()
{
 // TODO: 在此添加控件通知处理程序代码
 //CDialogEx::OnOK();
 //获得EDIT
 CEdit* base;
 CEdit* n;
 base = (CEdit*) GetDlgItem(IDC_EDIT1);
 n = (CEdit*) GetDlgItem(IDC_EDIT2);

 CString str1;
 CString str2;
 CString showStr;

 char tmp[10] = "";
 base -> GetWindowText(str1);
 n -> GetWindowText(str2);

 //char* pstr = (LPTSTR)LPCTSTR(str1);
 int my_base = _ttoi(str1);
 int my_n = _ttoi(str2);

 int result = power(my_base, my_n);

 showStr = itoa(result,tmp,10);

 CString str = _T("乘方运算结果为:");

 MessageBox(str + showStr,_T("程序运行结果"),MB_OK);
 str.ReleaseBuffer();
}

int power(int base, int n)
{
 int i, p;
 p = 1;
 for (i = 1; i <= n; ++i)
 p = p * base;
 return p;
}

程序运行结果:

CString转int可以使用

 int my_base = _ttoi(str1);

函数声明注意要写到头函数中。

传值调用与参数
习惯其它语言(特别是 Fortran 语言)的程序员可能会对 C 语言的函数参数传递方式感到陌生。在 C 语言中,所有函数参数都是“通过值”传递的。也就是说,传递给被调用函数的参数值存放在临时变量中,而不是存放在原来的变量中。这与其它某些语言是不同的,比如,Fortran 等语言是“通过引用调用”,Pascal 则采用 var 参数的方式,在这些语言中,被调用的函数必须访问原始参数,而不是访问参数的本地副本。

最主要的区别在于,在 C 语言中,被调用函数不能直接修改主调函数中变量的值,而只能修改其私有的临时副本的值。

传值调用的利大于弊。在被调用函数中,参数可以看作是便于初始化的局部变量,因此额外使用的变量更少。这样程序可以更紧凑简洁。侧如,下面的这个 power 函数利用了这一性质:

/* power: raise base to n-th power; n >= 0; version 2 */
int power(int base, int n)
{
 int p;
 for (p = 1; n > 0; --n)
 p = p * base;
 return p;
}

其中,参数 n 用作临时变量,并通过随后执行的 for 循环语句递减,直到其值为 0,这样就不需要额外引入变量 i;power 函数内部对 n 的任何操作不会影响到调用函数中 n 的原始参数值。

必要时,也可以让函数能够修改主调函数中的变量。这种情况下,调用者需要向被调用函数提供待设置值的变量的地址(从技术角度看,地址就是指向变量的指针),而被调用函数则需要将对应的参数声明为指针类型,并通过它间接访问变量。

如果是数组参数,情况就有所不同了。当把数组名用作参数时,传递给函数的值是数组起始元素的位置或地址——它并不复制数组元素本身。在被调用函数中,可以通过数组下标访问或修改数组元索的值。

(0)

相关推荐

  • 快速入门的一些C\C++书籍

    人们常常问我有什么C++和编程的书籍推荐,今天就为大家分享了几本 第一个注意项:如果你打算学习C++,请务必学习最新版的C++ 2011.这个版本的C++移除了许多由C++强大带来的一些痛苦之处.另外,也不用担心C++ 2014的书籍,大多数编译器已经开始支持它了. 学习编程 学习编程包含以下几个重要方面: 了解语言的语法 知道那些特性可以使用和何时使用 写出可读性好的代码:编译器可以理解,但是下一个人是否可以阅读呢? 在一个更高层次设计结构良好的程序 为了学习一门语言,通常我们可以找到叫<X语

  • C语言指针学习经验总结浅谈

    这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较费解的东西做一下讲解,希望能达到以下三个目的 1.通过写这些东西,把我脑袋中关于C的模糊的知识清晰化.2.给初转C的同事们一点提示和帮助.3.也希望各位前辈检查一下文档中是否有理解偏差的地方.1 指针的概念分解      指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容: 1.指针的类型2.指

  • JavaScript入门学习书籍推荐

    在 JavaScript 方面,自己不是什么专家,也不是什么高手,但自己一路走来,JavaScript 从迷茫到认识,对于 JavaScript 书籍的认识或许还有些借鉴价值. 入门推荐首选书籍:<JavaScript DOM 编程艺术 > 当初读了不下 4 遍,书内容简单,易学,上手快,编程思想严谨.好的入门书,对你未来的编程都会有着深远的影响.此本书不辜负这个历史使命. 更详细的评论可以看 Realazy 在豆瓣上的评论在 豆瓣 上的评论<通往终点的过程与终点本身同样重要> 基

  • 为什么要学习C语言 C语言优势分析

    不止一个学生问到我:"老师,为什么我们的应用程序设计要学C语言而不是别的?C语言不是已经过时了吗?如果现在要写一个Windows程序,用VB或Dephi开发多快呀,用C行吗?退一万步,为什么选择C而不是C++呢?" 这个问题三言两语还真说不全.简单来说,C语言是计算机程序语言的基础,是实用的程序设计工具,学好C语言对你今后学习JAVA.C++.VB等可以打下良好的基础,因为这些语言大部分都是由C语言扩充或衍生而来的.C可以用于开发比较底层的东西,比如驱动.通信协议之类,在Unix和Li

  • C语言入门之指针用法教程

    本文针对C语言初学者详细讲述了指针的用法,并配以实例进行说明.具体分析如下: 对于C语言初学者来说,需要明白指针是啥?重点就在一个"指"上.指啥?指的地址.啥地址?内存的地址. 上面说明就是指针的本质了. 这里再详细解释下.数据存起来是要存在内存里面的,就是在内存里圈出一块地,在这块地里放想放的东西.变量关心的是这块地里放的东西,并不关心它在内存的哪里圈的地:而指针则关心这块地在内存的哪个地方,并不关心这块地多大,里面存了什么东西. 指针怎么用呢?下面就是基本用法: int a, b,

  • C语言WinSock学习笔记第1/2页

    作者:肖进 Socket(套接字) ◆先看定义: typedef unsigned int u_int; typedef u_int SOCKET; ◆Socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了.其定义类似于文件句柄的定义. ◆Socket有五种不同的类型: 1.流式套接字(stream socket) 定义: #define SOCK_STREAM 1  流式套接字提供了双向.有序的.无重复的以及无记录边界的数据流

  • C语言编程中的联合体union入门学习教程

    联合体(union)在C语言中是一个特殊的数据类型,能够存储不同类型的数据在同一个内存位置.可以定义一个联合体使用许多成员,但只有一个部件可以包含在任何时候给定的值.联合体会提供使用相同的存储器位置供多用途的有效方式. 定义联合体 要定义联合体,必须使用union语句很相似于定义结构.联合体声明中定义了一个新的数据类型,程序不止一个成员.联合体声明的格式如下: union [union tag] { member definition; member definition; ... member

  • C语言编程中函数的基本学习教程

    C 语言中的函数等价于 Fortran 语言中的子程序或函数,也等价于 Pascal 语言中的过程或函数.函数为计算的封装提供了一种简便的方法,此后使用函数时不需要考虑它是如何实现的.使用设计正确的函数,程序员无需考虑功能是如何实现的,而只需知道它具有哪些功能就够了.在 C 语言中可以简单.方便.高效地使用函数.我们经常会看到在定义后仅调用了一次的短函数,这样做可以使代码段更清晰易读. 到目前为止,我们所使用的函数(如 printf.getchar 和 putchar 等)都是函数库中提供的函数

  • C语言编程中生成随机数的入门教程

    语言产生随机数是一个常见的编程功能任务,当然这个也不难,调用两三个函数就出来了,但是你知道这些函数具体是起到怎样的作用,并且是它们是如何产生随机数的吗? 几个概念 随机数:数学上产生的都是伪随机数,真正的随机数使用物理方法产生的. 随机数种子:随机数的产生是由算术规则产生的,srand(seed)的随机数种子不同,rand()的随机数值就不同,倘若每次的随机数种子一样,则rand()的值就一样.所以要产生随机数,则srand(seed)的随机数种子必须也要随机的. 用srand()产生随机数种子

  • 在C语言编程中使用变量的基础教程

    C语言在明面上将数的变量分为两类,整型变量以及浮点数,对应着现实世界的整数和小数. 首先是整数,使用了这么多的C语言之后,每当在使用整数之时都会将其想象成二进制的存在,而不是十进制.原因在于,这是程序的本质所在,稍有研究编译器工作原理的都会发现,在编译器处理乘法乃至除法的时候,优秀的编译器总会想方设法的加快程序的速度,毫无疑问在所有运算中移位运算是最快速的"乘法"以及"除法": 1<<2 == 4 ,8>>2 == 2 而正常一个乘法相当于十

  • Java编程中void方法的学习教程

    void 关键字 本节说明如何声明和调用一个void方法. 下面的例子声明了一个名为printGrade的方法,并且调用它来打印给定的分数. 示例 public class TestVoidMethod { public static void main(String[] args) { printGrade(78.5); } public static void printGrade(double score) { if (score >= 90.0) { System.out.println

  • C语言编程中常见的五种错误及对应解决方案

    目录 1. 未初始化的变量 2. 数组越界 3. 字符串溢出 4. 重复释放内存 5. 使用无效的文件指针 前言: C 语言有时名声不太好,因为它不像近期的编程语言(比如 Rust)那样具有内存安全性.但是通过额外的代码,一些最常见和严重的 C 语言错误是可以避免的. 即使是最好的程序员也无法完全避免错误.这些错误可能会引入安全漏洞.导致程序崩溃或产生意外操作,具体影响要取决于程序的运行逻辑. 下文讲解了可能影响应用程序的五个错误以及避免它们的方法: 1. 未初始化的变量 程序启动时,系统会为其

  • C语言编程计算信噪比SNR理解学习

    目录 概念 计算方法 相关认知 Taprint中的信噪比 实例 概念 这里面的信号指的是来自设备外部需要通过这台设备进行处理的电子信号,噪声是指经过该设备后产生的原信号中并不存在的无规则的额外信号(或信息),并且该种信号并不随原信号的变化而变化. 计算方法 信噪比的计量单位是dB,其计算方法是10lg(Ps/Pn),其中Ps和Pn分别代表信号与噪声的有效功率,也可以换算成电压幅值的比率关系:20Lg(Vs/Vn),Vs和Vn分别代表信号和噪声电压的"有效值". 在音频放大器中,我们希望

  • Go语言结构体Go range的学习教程

    目录 正文 Go Range 正文 在前一篇博客我们学习了 Go 数组,其要求所有元素为同一数据类型,如果希望存储不同类型的数据,就要用到结构体相关知识. 结构体的定义:存储相同或不同类型的数据集合. 有 C 相关经验,结构体还是比较容易理解的,语法格式如下所示: type struct_variable_type struct { member definition member definition ... member definition } 上述语法格式的关键字是 struct 和 t

  • Go 语言 IDE 中的 VSCode 配置使用教程

    Gogland 是 JetBrains 公司推出的Go语言集成开发环境.Gogland 同样基于 IntelliJ 平台开发,支持 JetBrains 的插件体系.官方:https://www.jetbrains.com/go/.关于 Goland 相关配置参考该链接即可.Goland 用的好好的,为啥突然想用到 VSCode 呢 ?VSCode 是目前比较流行的 IDE 工具,在功能方面也相对齐全,使用方面也比较友好.不过对于 Golang 来说,配置起来不算太麻烦,只能说其中有一些比较坑的地

  • 深入解析golang编程中函数的用法

    函数是一组一起执行任务的语句.每Go程序具有至少一个函数,它一般是main(),以及所有的最琐碎程序可以定义附加函数. 你可以将代码放到独立的功能.如何划分代码之间的不同功能,但逻辑上的划分通常是让每个函数执行特定的任务. 函数声明告诉编译器有关的函数的名称,返回类型和参数.一个函数定义提供了函数的实际主体. Go语言标准库提供了大量的内置函数,在程序可以调用.例如,函数len()需要不同类型的参数和返回值的类型的长度.例如,如果一个字符串传递给它,它会返回字符串的长度以字节为单位,如果一个数组

  • 详解C语言编程中预处理器的用法

    预处理最大的标志便是大写,虽然这不是标准,但请你在使用的时候大写,为了自己,也为了后人. 预处理器在一般看来,用得最多的还是宏,这里总结一下预处理器的用法. #include <stdio.h> #define MACRO_OF_MINE #ifdef MACRO_OF_MINE #else #endif 上述五个预处理是最常看见的,第一个代表着包含一个头文件,可以理解为没有它很多功能都无法使用,例如C语言并没有把输入输入纳入标准当中,而是使用库函数来提供,所以只有包含了stdio.h这个头文

随机推荐