input为number类型时,限制长度

场景:手机号只能输入11位,且为 number 类型

vue 使用 element-ui 的 el-input 时,不设置 type 属性,maxlength 是可以正常限制字数。 但如果让 type="number" ,maxlength就不起作用了

应给 input 添加 oninput 方法监听

<el-form-item label="手机号">
    <el-input v-model="searchForm.phone" type="number" oninput="if(value.length>11)value=value.slice(0,11)"></el-input>
</el-form-item>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HTML + JavaScript + Ajax + CSS 赵旭 [email protected] HTML5 1、WEB基础知识(了解) 1、Internet - 网 由若干台电脑、手机、平板 通过 网线(WIFI) 连接起来的结构 2、基于Internet上的程序 1、C / S 结构 Client / Server 特点:必须通过指定的客户端软件才能访问服务器端的一种程序 :如 :桌面版QQ,... ... 2、B / S 结构 Browser / Server Browser :浏览器 特点:通过 浏览器 就能访问服务器端的一种程序 如 :网页版百度,网页版 QQ,网页版 京东,... ... 3、WEB 1、什么是WEB WEB,是基于Internet上的一种应用程序(网页应用程序) WEB页面,简称WEB页(网页),就是保存在服务器端上的一个具体的页面( **.html / **.htm 作为结尾的文件) 2、WEB浏览器 1、功能 1、提交用户请求 (UA : User Agent) 2、作为HTML 以及 脚本执行的 解释器 3、以图形化的方式显示web页面 2、主要浏览器产品 1、Microsoft Internet Explorer (IE) 2、Mozilla Firefox 3、Google Chrome 4、Apple Safari 5、Opera Opera(欧朋) 3、主要技术 1、HTML 2、CSS 3、Javascript 2、HTML入门(重点) 1、什么是HTML Hyper Text Markup Language 超级 文本 标记 语言 超文本 :也是文本,但会具备特殊功能 普通文本 a : 普通字符 a 超文本 a : 表示超链接 普通文本 b : 普通字符 b 超文本 b : 加粗显示文本 标记 :超文本的表现形式 普通文本 a : a 超文本 a : 语言 :具备一定的语法规范 HTML 也具备自己的语法规范 WEB页面(HTML页面)就是由 HTML 语言来进行开发的 ,以 .html 或 .htm 进行结尾的文件 开发 & 运行网页的工具: 1、记事本 2、浏览器 2、HTML 的基础语法 1、标记语法 HTML中用于描述功能的符号称为"标记" 标记在使用,用尖括号 "",标记的分类 1、封闭类型的标记 也称为 "双标记" , 必须成对出现 语法:内容 Demo : 1、创建 p 标记 --... 2、创建 div 标记 -- 3、创建 header 标记 -- <header></header> 2、非封闭类型的标记 也称为 "单标记" 语法: 或 Demo : 1、创建 br 标记 -- 2、创建 hr 标记 -- 3、创建 img 标记 -- 4、创建 input 标记 -- <input/> 2、标记(元素)的嵌套 在一对标记中,允许出现另外一对(一个)标记 注意:嵌套标记的书写格式 -- 被嵌套的标记要通过一个 缩进键(Tab) 来表示层级关系 Demo : 1、编写一对 body 标记,在body标记中,嵌套一对 div标记,在 div 标记中 ,嵌套一对 a 标记,在 a标记中,嵌套一对 b 标记,b标记中,随意编写一些文本 <body> 这是一段测试文本 </body> 3、标记的属性 1、什么是属性 用来修饰标记的效果的内容,就是属性 2、语法 1、属性必须声明在开始标记中 2、属性与标记名称之间,用 空格 隔开 3、属性的值 与 属性之间 使用 "=" 连接 4、一个元素允许有多属性,多属性间,排名不分先后,中间用 空格 隔开即可 Demo : 1、创建一个 div 标记 ,增加属性 ,设置 align 属性的值为 center ,设置 id 属性的值为 container,设置 title 属性的值为 这是一个div 四大标准属性: 1、id :定义元素在页面中独一无二的名称 2、title :鼠标悬停在元素上,体现的文字 3、class :引用 类选择器使用(CSS中) 4、style :定义 内联方式方式使用(CSS中) 4、注释 语法:<!-- --> 注意: 1、注释不能嵌套 2、注释不能出现在标记中 3、HTML 文档结构 W3C :Word Wide Web Consortium (万维网联盟) 1、HTML文档的组成 1、文档类型声明 告知浏览器当前的HTML文档用的是哪个版本 在网页的最顶端 2、由一对 html 根标记,来表示页面的开始与结束 Demo : 1、在 htdoc 中,创建一个网页 01-first.html 文件 创建一个 记事本,将 .txt 重命名为 .html 2、搭建HTML网页结构 1、添加 文档类型声明 2、添加 html 根标记 2、<html> 标记 包含两对子元素 1、<head></head> 包含的子元素(2对) 1、<title>网页的标题</title> 2、<meta charset="utf-8"/> -- 能正常显示中文 2、<body></body> 属性: 1、text ,取值是一个颜色值(red,green,blue...) 2、bgcolor ,取值也是一个颜色值 Demo : 在刚才的 Demo 基础上 1、在 <html>中 增加 <head> 和 <body> 2、为网页指定标题 - 我的第一个HTML文档 3、指定网页的字符编码格式为 utf-8 4、在 body 中 输出一句话 "我的第一HTML页面" 5、设置 body text为red,bgcolor为yellow 3、文本 1、特殊文本的实现 页面的空格以及一些特殊字符需要通过转义字符的方式体现 1、  表示一个空格 2、< 表示 4、© 表示© Demo : 1、创建一个页面 02-text.html 2、在页面中 输出以下内容 The element. ©2017 By Tarena The <p> element.     © By Tarena 2、文本样式相关标记 : 加粗 : 斜体 : 下划线 : 删除线 : 上标 : 下标 Demo : 这是一段包含 ,粗体,斜体,下划线,删除线,上标,下标的文本内容 将以上文本内,对应文字的特殊效果,用标记体现出来 3、标题元素 以 醒目 的方式表现出文本 语法: 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 属性: 1、align 文本的水平对齐方式 取值:left / center / right 4、段落元素 语法: 属性:align : left / center / right Demo : 1、在 02-text.html 中,增加以下内容 1、用 段落标记表示 :The first paragraph 2、用 段落标记表示 :The second paragraph ,文本表现为 右对齐 5、换行元素 6、分割线元素 作用:在页面中表现为一条直线 语法: 属性: 1、size 尺寸,以 px 为单位的数值 2、width 宽度,以 px 或 % 为单位的数值 3、align 水平对其方式 4、color 颜色 Demo : 02-text.html 中 增加一根水平线,size为5px,宽度为50%,居中对齐,颜色为红色(red) 7、预格式化 作用:保留源文档中的回车 和 空格 的作用 8、分区元素 1、块分区元素 语法: 作用:布局 2、行内分区元素 语法: 作用:设置同一行文字内的不同样式 9、行内元素 与 块级元素 按照元素们的表现形式来分类,分为 行内元素 和 块级元素1、行内元素 在一行内允许显示多个元素的,称为 "行内元素" span,i,b,s,u,sup,sub 作用:包裹文本,并处理文本的表现形式 2、块级元素 每个元素独占一行显示的,称为 "块级元素" div,p,h1,h2,h3,h4,h5,h6 作用:布局 注意: 1、p标记不能嵌套其它的块级元素 2、行内元素中 最好 不要嵌套块级元素 4、图像 和 链接 1、URL 目录结构 : 目录 ,保存文件的文件夹名称 多个文件夹之间的关系,就是目录结构 1、什么是 URL URL (Uniform Resource Locator),统一资源定位器。用来标识某资源文件的位置 2、URL 在 WEB 中的表现形式 共三种表现形式: 1、绝对路径 特点:从文件所在的最高级目录处开始查找资源文件所经过的路径,就是绝对路径 使用场合:当想访问互联网上的资源,只能用绝对路径 完整的绝对路径分四部分: 1、协议名 http 2、域名(主机名,IP地址) www.codeboy.com 3、目录路径 img/header 4、文件名 logo.png http://www.codeboy.com/img/header/logo.png img/header/logo.png 2、相对路径(重点) 场合:使用本机文件使用 什么是相对路径: 从当前文件位置处开始查找资源文件所经过的路径,就是相对路径 1、同目录,直接用 2、子目录,先进入 3、父目录,先返回 3、根相对路径 路径形式是以 / 作为开始的。 / : 表示的是服务器的根路径 2、图像 1、图像格式 WEB中支持的图像格式 1、*.jpg 2、*.gif(动图) 3、*.png(透明) 2、图像的语法 标记: 属性: 1、src :指定要显示图像的 URL 2、width :图像的宽度 3、height :图像的高度 4、title :鼠标悬停,要显示的文本 Demo : 1、将 学子网的 logo 下载下来 2、显示在自己的网页中 3、超链接 1、语法 标记:内容 属性: 1、href : 要链接的HTML页面URL 2、target : 目标,指定新网页的打开形式 取值: 1、_blank : 在新标签页中打开网页 2、_self : 在自身标签页中打开新网页(默认值) Demo 1、创建一个超链接,内容为 :学子商城,点击,在自身标签页中 打开 http://www.codeboy.com 2、创建一个超链接,内容为 :学子商城的LOGO,点击,在新标签页中打开 http://www.codeboy.com 2、链接的四种表现形式 1、点击操作,完成资源下载的操作 链接的资源为 zip / rar 则为下载操作 下载 2、电子邮件链接 联系我们 前提 : 必须在计算机中安装并配置好至少一个邮件客户端的信息 3、返回页面顶部的空连接 返回顶部 4、执行Javascript代码片段 执行JS 3、锚点 1、作用 锚点用于在网页中的某个位置处做个记号,允许从其他位置处跳转到记号位置处 2、使用方式 1、定义锚点 1、使用 a 标记的 name 属性允许定义锚点 xxxx 2、任何标记的 id 属性定义锚点 2、链接到锚点 链接到锚点 链接到锚点 5、表格 1、表格 1、表格的作用 按照一定的格式来显示数据的 表格是由 单元格(列),行 来组成的 2、表格的语法 1、标记 1、 : 表示一个表格 2、 : 表示表格中的一行 3、 : 表行中的一列(单元格) : 行/列 标题 (加粗,水平居中) 4、 : 表格的标题 该元素必须添加在 之下,所有的tr之上 2、常用属性 1、table 1、width 2、height 3、align 设置表格的对齐方式 , left / center / right 4、border 边框宽度,取值以 px 为单位的数值 5、bgcolor 6、cellspacing :单元格外边距(单元格与单元格 或 单元格与表格之间的距离) 7、cellpadding : 单元格内边距(单元格与内容之间的距离) 2、tr 1、align 控制当前行内容的水平对齐方式 2、valign 控制当前行内容的垂直对齐方式 top / middle / bottom 3、bgcolor 3、td / th 1、align 2、valign 3、width 4、height 5、colspan :跨列 6、rowspan :跨行 Demo : 在 刚才的表格基础上,增加以下内容 1、为表格增加边框 1px 2、为表格增加尺寸 400 * 400 3、为表格增加单元格内边距 5px 4、为第四行 增加属性 ,内容水平居中对齐 3、表格的复杂应用 1、行分组 允许将若干行划分到一个组中,以便实现统一管理 1、表头行分组 允许将第一行的内容单独分到表头行分组中 2、表尾行分组 允许将最后一行的内容单独分到表尾行分组中 3、表主体行分组 2、不规则表格创建 通过 td 的跨行 和 跨列来实现不规则的表格 1、跨列 从指定单元格位置处开始,横向向右,合并掉几个单元格(包含自己),被合并掉的单元格,要删除 2、跨行 从指定单元格位置处开始,纵向向下,合并掉几个单元格(包含自己),被合并掉的单元格,要删除 3、表格的嵌套 被嵌套的所有的内容,只能放在 td 中 2、列表 1、作用 按照一定的格式显示数据 2、列表的分类 & 语法 1、列表的组成 由列表类型 以及 列表项 来组成 2、分类 & 语法 1、有序列表 --> Order List 允许包含若干列表项: --> List Item 2、无序列表 --> Unorder List 允许包含若干列表项: 3、属性 1、ol 1、type 1 、按数字方式排列,默认值 a 、按小写英文方式排列 A 、按大写英文方式排列 i 、按小写罗马数字排列 I 、按大写罗马数字排列 2、start 指定标识从 几 开始显示 2、ul 1、type 1、disc,实心圆,默认值 2、circle,空心圆 3、square,实心矩形 4、none 3、列表的嵌套 被嵌套的内容只能放在 li 中 Demo : 1、声明一个列表在 html 中(有序),包含两个列表项,显示 两部小说的名字 2、在两个列表项中,再各嵌套一个无序列表,各写3-4个小说中的主角 3、定义列表 1、作用 以一种特殊的结构来排列数据 通常用语对一类事物/名词 的解释上面 2、语法 :声明一个定义列表 :声明要解释的事物名称 或 名词 :对上述名词或事物解释的内容 3、常用场合 图文混排 3、结构标记 1、结构标记的作用 搭建网页的结构(布局),用于替代 div,最大的优点是提升了标记的"语义性" 2、结构标记详解 1、<header></header> 作用:表示网页 或 某块内容的头部 2、 作用:表示网页中的 导航内容 3、 作用:表示网页主体内容中的某一部分 4、 作用:出现在文字描述性比较强的场合:一则新闻,论坛中的帖子,微博信息,博客的信息 5、 作用:表示网页中,或某部分内容的 边栏信息 6、 作用:表示网页中尾部的信息 4、表单(难点) 1、表单的作用 用于收集用户的信息并提交给服务器 表单主体是由两部分组成的 1、表单元素 收集信息,并提交给服务器 2、表单控件 用于与用户交互的一些元素:文本框,密码框 2、表单元素(难点) 标记:<form></form> 注意:只有放在 <form></form> 里面的表单控件的值,才能提交给服务器 属性: 1、action 提交给服务器处理程序的地址 (动作) 2、method 提交方式,以什么样的方式把数据交给服务器 1、get 特点: 1、以明文的方式将数据传递给服务器(提交的数据会显示在地址栏上) 2、安全性很低 3、提交数据最大限制为 2KB 4、向服务器要数据可以使用 2、post 特点: 1、以隐式的方式将数据传递给服务器 2、安全性很高 3、不限制提交数据的大小 4、要提交数据给服务器处理使用 3、... ... 3、enctype 作用:指定表单数据进行编码的方式 取值: 1、application/x-www-form-urlencoded 默认值,允许将任意类型的文本提交给服务器 2、multipart/form-data 允许将文件提交给服务器 3、text/plain 不对任何数据进行编码和传输 Demo : 1、创建 05-form.html 2、声明一个表单元素 form 3、指定提交地址为 login.php,提交方式为 get,编码方式为默认 3、表单控件 表单元素分类: 1、input元素 2、textarea 多行文本域 3、select 和 option 选项框 4、其它元素 5、新input元素 详解 : 1、input元素 里面会包含若干个不同的表单控件 标记 :<input> 属性 : 1、type 根据不同的type属性值,可以创建不同的input元素 2、name 定义表单控件的名称,主要提交给服务器使用的 注意:如果不声明name属性的话,元素则无法提交给服务器 3、value 定义当前控件的值,主要提交给服务器使用的 4、disabled 禁用控件,无值的属性 <input disabled> 2、文本框 与 密码框 文本框:<input type="text"> 密码框:<input type="password"> 具备除以上四个属性外,还具备以下几个独立属性: 1、maxlength 限制输入的字符数,取值为数字 2、readonly 只读,无值属性 Q : readonly 和 disabled 的区别 1、readonly ,是只读的意思,是允许提交给服务器,不让用户改数据而已 2、disabled , 是禁用的意思,不仅不让用户改数据,而且还不能提交给服务器 3、placeholder 占位符,即默认显示在文本框用于给用户提示的文字 3、按钮 1、提交按钮 作用:将表单提交给服务器 标记:<input type="submit" value="显示的文本"> 2、重置按钮 作用:将表单恢复到初始化的状态 标记:<input type="reset" value="显示的文本"> 3、普通按钮 作用:通过 JS 自定义功能 标记:<input type="button" value="显示的文本"> 4、显示的内容 属性: 1、type :submit / reset / button 4、单选按钮 和 复选框 单选按钮:<input type="radio"> 复选框:<input type="checkbox"> 属性: 1、name 设置名称,并用于分组,一组单选按钮 或 复选框的名称必须相同 2、value 必须设置 3、checked 默认被选中,无值属性 5、隐藏域 和 文件选择框 1、隐藏域 <input type="hidden" name="" value=""> 想提交给服务器,但不想给用户看的数据,可以放在隐藏域中 2、文件选择框 <input type="file" name=""> 注意: 1、form的 method 属性值必须为 post 2、form的 enctype属性值必须为 multipart/form-data http://localhost/02-HTML/Day02/login.php?uname=wenhua.li&upwd=bingbing http://localhost/02-HTML/Day02/login.php?uname=wenhua.li&upwd=fengjie&gender=1&question=0&question=1&question=2&question=3 http://localhost/02-HTML/Day02/login.php?uname=wenhua.li&upwd=fdsafdsafdsa&gender=0&question=0&question=1&question=2&question=3&uid=10000567789 1、表单 1、form ... 2、input ... 3、textarea 控件 标记:<textarea></textarea> 属性: 1、name 定义名称,提交给服务器使用 2、cols 指定文本域的列数,变相指定宽 3、rows 指定文本域的行数,变相指定高 4、readonly 只读 Demo : 创建 01-form.html 文件 创建一个 多行文本域,列数为 50,行数为 5,名称为 intro 4、选项框 1、标记 表示 下拉列表 或 滚动列表 列表中的内容,允许出现多个 2、属性 1、 1、name :控件的名称 2、size :默认显示选项的数量,默认为1,为下拉列表,如果取值>1的话,则为滚动列表 3、multiple 设置多选,无值的属性 2、 1、value :选项的值,提交给服务器用 2、selected :默认被选中 5、其它标记 1、label 元素 作用:关联文本与表单控件的,点击文本就如同点击表单控件一样 标记:文本 属性: 1、for 指定要关联的表单控件的ID值 2、为控件分组 分组标题 分组中的内容 3、浮动框架 1、作用 将其他页面导入到当前页面中来 2、语法 标记:<iframe></iframe> 属性: 1、src 要引入的页面的url 2、frameborder 指定浮动框架的边框,默认为1,则显示边框 不需要边框则设置为 0 3、width 4、height 练习: 创建一个 02-iframe 的网页,将 01-form.html 页面引入进来 ,适当调整宽度,高度,边框 6、新input元素 (HTML5) 1、电子邮件类型 作用:提交会验证数据是否符合Email的规范 标记:<input type="email"> 2、搜索类型 作用:在文本框的基础上,提供了快速清除操作 标记:<input type="search"> 3、URL类型 作用:提交会验证数据是否符合Web站点的URL规范(绝对路径) 标记:<input type="url"> 4、电话号码类型 作用:在移动端设备中,能展开 拨号键盘,在PC中无效 标记:<input type="tel"> 5、数字类型 作用:只能让用户输入 或 选择数字 标记:<input type="number"> 属性: 1、min :当前控件接受的最小值 2、max :当前控件接受的最大值 3、step :微调数字每次变化的长度,默认为1 6、范围类型 作用:提供一个滑块,让用户选择数字 标记:<input type="range"> 属性: 1、min :当前控件的最小值 2、max :当前控件的最大值 3、value :设定初始值 7、颜色类型 作用:提供一个颜色拾取控件 语法:<input type="color"> 8、日期类型 作用:提供一个日期输入控件 标记:<input type="date"> 9、周类型 作用:提供一个日期控件,用于选取周 标记:<input type="week"> 10、月份类型 作用:选取月份控件 标记:<input type="month"> =============================
适于初学者 第五章:函数 概述   在第一章中已经介绍过,C源程序是由函数组成的。 虽然在前面各章的程序中都只有一个主函数main(), 但实用程序往往由多个函数组成。函数是C源程序的基本模块, 通过对函数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。 C语言不仅提供了极为丰富的库函数(如Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。   可以说C程序的全部工作都是由各式各样的函数完成的, 所以也把C语言称为函数式语言。 由于采用了函数模块式的结构, C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。   在C语言中可从不同的角度对函数分类。 1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。 (1)库函数   由C系统提供,用户无须定义, 也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf 、 scanf 、 getchar 、putchar、gets、puts、strcat等函数均属此类。 (2)用户定义函数   由用户按需要写的函数。对于用户自定义函数, 不仅要在程序中定义函数本身, 而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。 2. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。 (1)有返回值函数   此类函数被调用执行完后将向调用者返回一个执行结果, 称为函数返回值。如数学函数即属于此类函数。 由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。 (2)无返回值函数   此类函数用于完成某项特定的处理任务, 执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。 由于函数无须返回值,用户在定义此类函数可指定它的返回为“空类型”, 空类型的说明符为“void”。 3. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。 (1)无参函数   函数定义、函数说明及函数调用中均不带参数。 主调函数和被调函数之间不进行参数传送。 此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。 (2)有参函数   也称为带参函数。在函数定义及函数说明都有参数, 称为形式参数(简称为形参)。在函数调用也必须给出参数, 称为实际参数(简称为实参)。 进行函数调用,主调函数将把实参的值传送给形参,供被调函数使用。 4. 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。    函数定义的一般形式 1.无参函数的一般形式 类型说明符 函数名() { 类型说明 语句 }   其中类型说明符和函数名称为函数头。 类型说明符指明了本函数的类型,函数的类型实际上是函数返回值的类型。 该类型说明符与第二章介绍的各种说明符相同。 函数名是由用户定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。{} 中的内容称为函数体。在函数体中也有类型说明, 这是对函数体内部所用到的变量的类型说明。在很多情况下都不要求无参函数有返回值, 此函数类型符可以写为void。 我们可以改为一个函数定义: void Hello() { printf ("Hello,world \n"); }  这里,只把main改为Hello作为函数名,其余不变。Hello 函数是一个无参函数,当被其它函数调用,输出Hello world字符串。 2.有参函数的一般形式 类型说明符 函数名(形式参数表) 型式参数类型说明 { 类型说明 语句 }   有参函数比无参函数多了两个内容,其一是形式参数表, 其二是形式参数类型说明。在形参表中给出的参数称为形式参数, 它们可以是各种类型的变量, 各参数之间用逗号间隔。在进行函数调用,主调函数将赋予这些形式参数实际的值。 形参既然是变量,当然必须给以类型说明。例如,定义一个函数, 用于求两个数中的大数,可写为: int max(a,b) int a,b; { if (a>b) return a; else return b; }   第一行说明max函数是一个整型函数,其返回的函数值是一个整数。形参为a,b。第二行说明a,b均为整型量。 a,b 的具体值是由主调函数在调用传送过来的。在{}中的函数体内, 除形参外没有使用其它变量,因此只有语句而没有变量类型说明。 上边这种定义方法称为“传统格式”。 这种格式不易于编译系统检查,从而会引起一些非常细微而且难于跟踪的错误。ANSI C 的新标准中把对形参的类型说明合并到形参表中,称为“现代格式”。   例如max函数用现代格式可定义为: int max(int a,int b) { if(a>b) return a; else return b; }   现代格式在函数定义和函数说明(后面将要介绍), 给出了形式参数及其类型,在编译易于对它们进行查错, 从而保证了函数说明和定义的一致性。例1.3即采用了这种现代格式。 在max函数体中的return语句是把a(或b)的值作为函数的值返回给主调函数。有返回值函数中至少应有一个return语句。 在C程序中,一个函数的定义可以放在任意位置, 既可放在主函数main之前,也可放在main之后。例如例1.3中定义了一个max 函数,其位置在main之后, 也可以把它放在main之前。 修改后的程序如下所示。 int max(int a,int b) { if(a>b)return a; else return b; } void 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); }   现在我们可以从函数定义、 函数说明及函数调用的角度来分析整个程序,从中进一步了解函数的各种特点。程序的第1行至第5行为max函数定义。进入主函数后,因为准备调用max函数,故先对max函数进行说明(程序第8行)。函数定义和函数说明并不是一回事,在后面还要专门讨论。 可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程序第12 行为调用max函数,并把x,y中的值传送给max的形参a,b。max函数执行的 结果 (a或b)将返回给变量z。最后由主函数输出z的值。   函数调用的一般形式前面已经说过,在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。C语言中, 函数调用的一般形式为:   函数名(实际参数表) 对无参函数调用则无实际参数表。 实际参数表中的参数可以是常数,变量或其它构造类型数据及表达式。 各实参之间用逗号分隔。'Next of Page在C语言中,可以用以下几种方式调用函数: 1.函数表达式   函数作表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如: z=max(x,y)是一个赋值表达式,把max的返回值赋予变量z。'Next of Page 2.函数语句   函数调用的一般形式加上分号即构成函数语句。例如: printf ("%D",a);scanf ("%d",&b);都是以函数语句的方式调用函数。 3.函数实参   函数作为另一个函数调用的实际参数出现。 这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。例如: printf("%d",max(x,y)); 即是把max调用的返回值又作为printf函数的实参来使用的。在函数调用中还应该注意的一个问题是求值顺序的问题。 所谓求值顺序是指对实参表中各量是自左至右使用呢,还是自右至左使用。 对此, 各系统的规定不一定相同。在3.1.3节介绍printf 函数已提 到过,这里从函数调用的角度再强调一下。 看例5.2程序。 void main() { int i=8; printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--); } 如按照从右至左的顺序求值。例5.2的运行结果应为: 8 7 7 8 如对printf语句中的++i,--i,i++,i--从左至右求值,结果应为: 9 8 8 9   应特别注意的是,无论是从左至右求值, 还是自右至左求值,其输出顺序都是不变的, 即输出顺序总是和实参表中实参的顺序相同。由于Turbo C现定是自右至左求值,所以结果为8,7,7,8。上述问题如还不理解,上机一试就明白了。函数的参数和函数的值 一、函数的参数   前面已经介绍过,函数的参数分为形参和实参两种。 在本小节中,进一步介绍形参、实参的特点和两者的关系。 形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。 形参和实参的功能是作数据传送。发生函数调用, 主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。   函数的形参和实参具有以下特点: 1.形参变量只有在被调用才分配内存单元,在调用结束, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。 2.实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。 3.实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。 4.函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。例5.3可以说明这个问题。 void 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); } 本程序中定义了一个函数s,该函数的功能是求∑ni=1i 的值。在主函数中输入n值,并作为实参,在调用传送给s 函数的形参量n( 注意,本例的形参变量和实参变量的标识符都为n, 但这是两个不同的量,各自的作用域不同)。 在主函数中用printf 语句输出一次n值,这个n值是实参n的值。在函数s中也用printf 语句输出了一次n值,这个n值是形参最后取得的n值0。从运行情况看,输入n值为100。即实参n的值为100。把此值传给函数s,形参 n 的初值也为100,在执行函数过程中,形参n的值变为5050。 返回主函数之后,输出实参n的值仍为100。可见实参的值不随形参的变化而变化。 二、函数的值   函数的值是指函数被调用之后, 执行函数体中的程序段所取得的并返回给主调函数的值。如调用正弦函数取得正弦值,调用例5.1的max函数取得的最大数等。对函数的值(或称函数返回值)有以下一些说明: 1. 函数的值只能通过return语句返回主调函数。return 语句的一般形式为: return 表达式; 或者为: return (表达式); 该语句的功能是计算表达式的值,并返回给主调函数。 在函数中允许有多个return语句,但每次调用只能有一个return 语句被执行, 因此只能返回一个函数值。 2. 函数值的类型和函数定义中函数的类型应保持一致。 如果两者不一致,则以函数类型为准,自动进行类型转换。 3. 如函数值为整型,在函数定义可以省去类型说明。 4. 不返回函数值的函数,可以明确定义为“空类型”, 类型说明符为“void”。如例5.3中函数s并不向主函数返函数值,因此可定义为: void s(int n) { &hellip;&hellip; }   一旦函数被定义为空类型后, 就不能在主调函数中使用被调函数的函数值了。例如,在定义s为空类型后,在主函数中写下述语句 sum=s(n); 就是错误的。为了使程序有良好的可读性并减少出错, 凡不要求返回值的函数都应定义为空类型。函数说明在主调函数中调用某函数之前应对该被调函数进行说明, 这与使用变量之前要先进行变量说明是一样的。 在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型, 以便在主调函数中按此种类型对返回值作相应的处理。 对被调函数的说明也有两种格式,一种为传统格式,其一般格式为: 类型说明符 被调函数名(); 这种格式只给出函数返回值的类型,被调函数名及一个空括号。   这种格式由于在括号中没有任何参数信息, 因此不便于编译系统进行错误检查,易于发生错误。另一种为现代格式,其一般形式为: 类型说明符 被调函数名(类型 形参,类型 形参&hellip;); 或为: 类型说明符 被调函数名(类型类型&hellip;);   现代格式的括号内给出了形参的类型和形参名, 或只给出形参类型。这便于编译系统进行检错,以防止可能出现的错误。例5.1 main函数中对max函数的说明若 用传统格式可写为: int max(); 用现代格式可写为: int max(int a,int b); 或写为: int max(int,int);   C语言中又规定在以下几种情况可以省去主调函数中对被调函数的函数说明。 1. 如果被调函数的返回值是整型或字符型, 可以不对被调函数作说明,而直接调用。这系统将自动对被调函数返回值按整型处理。例5.3的主函数中未对函数s作说明而直接调用即属此种情形。 2. 当被调函数的函数定义出现在主调函数之前, 在主调函数中也可以不对被调函数再作说明而直接调用。例如例5.1中, 函数max的定义放在main 函数之前,因此可在main函数中省去对 max函数的函数说明int max(int a,int b)。 3. 如在所有函数定义之前, 在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明。例如: char str(int a); float f(float b); main() { &hellip;&hellip; } char str(int a) { &hellip;&hellip; } float f(float b) { &hellip;&hellip; } 其中第一,二行对str函数和f函数预先作了说明。 因此在以后各函数中无须对str和f函数再作说明就可直接调用。 4. 对库函数的调用不需要再作说明, 但必须把该函数的头文件用include命令包含在源文件前部。数组作为函数参数数组可以作为函数的参数使用,进行数据传送。 数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用; 另一种是把数组名作为函数的形参和实参使用。一、数组元素作函数实参数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用, 把作为实参的数组元素的值传送给形参,实现单向的值传送。例5.4说明了这种情况。[例5.4]判别一个整数数组中各元素的值,若大于0 则输出该值,若小于等于0则输出0值。编程如下: 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]); } }void nzp(int v) { &hellip;&hellip; } main() { int a[5],i; printf("input 5 numbers\n"); for(i=0;i<5;i++) { scanf("%d",&a[i]); nzp(a[i]); } }   本程序中首先定义一个无返回值函数nzp,并说明其形参v 为整型变量。在函数体中根据v值输出相应的结果。在main函数中用一个for 语句输入数组各元素, 每输入一个就以该元素作实参调用一次nzp函数,即把a[i]的值传送给形参v,供nzp函数使用。 二、数组名作为函数参数   用数组名作函数参数与用数组元素作实参有几点不同: 1. 用数组元素作实参,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此, 并不要求函数的形参也是下标变量。 换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数, 则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致,即会发生错误。 2. 在普通变量或下标变量作函数参数,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢? 在第四章中我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数所进行的传送只是地址的传送, 也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。图5.1说明了这种情形。图中设a为实参数组,类型为整型。a占有以2000 为首地址的一块内存区。b为形参数组名。当发生函数调用,进行地址传送, 把实参数 组a的首地址传送给形参数组名b,于是b也取得该地址2000。 于是a,b两数组共同占有以2000 为首地址的一段连续内存单元。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内 存单元(整型数组每个元素占二字节)。例如a[0]和b[0]都占用2000和2001单元,当然a[0]等于b[0]。类推则有a[i]等于b[i]。 [例5.5]数组a中存放了一个学生5门课程的成绩,求平均成绩。 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); } float aver(float a[5]) { &hellip;&hellip; } void main() { &hellip;&hellip; for(i=0;i<5;i++) scanf("%f",&sco[i]); av=aver(sco); &hellip;&hellip; }   本程序首先定义了一个实型函数aver,有一个形参为实型数组a,长度为5。在函数aver中,把各元素值相加求出平均值,返回给主函数。主函数main 中首先完成数组sco的输入,然后以sco作为实参调用aver函数,函数返回值送av,最后输出av值。 从运行情况可以看出,程序实现了所要求的功能 3. 前面已经讨论过,在变量作函数参数,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同, 而形参的值发生改变后,实参并不变化, 两者的终值是不同的。例5.3证实了这个结论。 而当用数组名作函数参数,情况则不同。 由于实际上形参和实参为同一数组, 因此当形参数组发生变化,实参数组也随之变化。 当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化。为了说明这种情况,把例5.4改为例5.6的形式。[例5.6]题目同5.4例。改用数组名作函数参数。 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]); } void nzp(int a[5]) { &hellip;&hellip; } main() { int b[5],i; &hellip;&hellip; nzp(b); &hellip;&hellip; }   本程序中函数nzp的形参为整数组a,长度为 5。 主函数中实参数组b也为整型,长度也为5。在主函数中首先输入数组b的值,然后输出数组b的初始值。 然后以数组名b为实参调用nzp函数。在nzp中,按要求把负值单元清0,并输出形参数组a的值。 返回主函数之后,再次输出数组b的值。从运行结果可以看出,数组b 的初值和终值是不同的,数组b 的终值和数组a是相同的。这说明实参形参为同一数组,它们的值同得以改变。 用数组名作为函数参数还应注意以下几点: a. 形参数组和实参数组的类型必须一致,否则将引起错误。 b. 形参数组和实参数组的长度可以不相同,因为在调用,只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。如把例5.6修改如下: 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]); }   本程序与例5.6程序比,nzp函数的形参数组长度改为8,函数体中,for语句的循环条件也改为i<8。因此,形参数组 a和实参数组b的长度不一致。编译能够通过,但从结果看,数组a的元素a[5],a[6],a[7]显然是无意义的。c. 在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的个数。 例如:可以写为: void nzp(int a[]) 或写为 void nzp(int a[],int n)   其中形参数组a没有给出长度,而由n值动态地表示数组的长度。n的值由主调函数的实参进行传送。 由此,例5.6又可改为例5.7的形式。 [例5.7] 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]); } void nzp(int a[],int n) { &hellip;&hellip; } main() { &hellip;&hellip; nzp(b,5); &hellip;&hellip; }   本程序nzp函数形参数组a没有给出长度,由n 动态确定该长度。在main函数中,函数调用语句为nzp(b,5),其中实参5将赋予形参n作为形参数组的长度。 d. 多维数组也可以作为函数的参数。 在函数定义对形参数组可以指定每一维的长度,也可省去第一维的长度。因此,以下写法都是合法的。 int MA(int a[3][10]) 或 int MA(int a[][10]) 函数的嵌套调用   C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。 但是C语言允许在一个函数的定义中出现对另一个函数的调用。 这样就出现了函数的嵌套调用。即在被调函数中又调用其它函数。 这与其它语言的子程序嵌套的情形是类似的。其关系可表示如图5.2。   图5.2表示了两层嵌套的情形。其执行过程是:执行main函数中调用a函数的语句,即转去执行a函数,在a函数中调用b 函数,又转去执行b函数,b函数执行完毕返回a函数的断点继续执行,a 函数执行完毕返回main函数的断点继续执行。 [例5.8]计算s=22!+32! 本题可编写两个函数,一个是用来计算平方值的函数f1, 另一个是用来计算阶乘值的函数f2。主函数先调f1计算出平方值, 再在f1中以平方值为实参,调用 f2计算其阶乘值,然后返回f1,再返回主函数,在循环程序中计算累加和。 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); } long f1(int p) { &hellip;&hellip; long f2(int); r=f2(k); &hellip;&hellip; } long f2(int q) { &hellip;&hellip; } main() { &hellip;&hellip; s=s+f1(i); &hellip;&hellip; }   在程序中,函数f1和f2均为长整型,都在主函数之前定义, 故不必再在主函数中对f1和f2加以说明。在主程序中, 执行循环程序依次把i值作为实参调用函数f1求i2值。在f1中又发生对函数f2的调用,这是把i2的值作为实参去调f2,在f2 中完成求i2! 的计算。f2执行完毕把C值(即i2!)返回给f1,再由f1 返回主函数实现累加。至此,由函数的嵌套调用实现了题目的要求。 由于数值很大, 所以函数和一些变量的类型都说明为长整型,否则会造成计算错误。 函数的递归调用   一个函数在它的函数体内调用它自身称为递归调用。 这种函数称为递归函数。C语言允许函数的递归调用。在递归调用中, 主调函数又是被调函数。执行递归函数将反复调用其自身。 每调用一次就进入新的一层。例如有函数f如下: int f (int x) { int y; z=f(y); return z; }   这个函数是一个递归函数。 但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段。常用的办法是加条件判断, 满足某种条件后就不再作递归调用,然后逐层返回。 下面举例说明递归调用的执行过程。 [例5.9]用递归法计算n!用递归法计算n!可用下述公式表示: n!=1 (n=0,1) n×(n-1)! (n>1) 按公式可编程如下: 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); } long ff(int n) { &hellip;&hellip; else f=ff(n-1)*n; &hellip;&hellip; } main() { &hellip;&hellip; y=ff(n); &hellip;&hellip; }   程序中给出的函数ff是一个递归函数。主函数调用ff 后即进入函数ff执行,如果n<0,n==0或n=1都将结束函数的执行,否则就递归调用ff函数自身。由于每次递归调用的实参为n-1,即把n-1 的值赋予形参n,最后当n-1的值为1再作递归调用,形参n的值也为1,将使递归终止。然后可逐层退回。下面我们再举例说明该过程。 设执行本程序输入为5, 即求 5!。在主函数中的调用语句即为y=ff(5),进入ff函数后,由于n=5,不等于0或1,故应执行f=ff(n-1)*n,即f=ff(5-1)*5。该语句对ff作递归调用即ff(4)。 逐次递归展开如图5.3所示。进行四次递归调用后,ff函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。ff(1)的函数返回值为1,ff(2)的返回值为1*2=2,ff(3)的返回值为2*3=6,ff(4) 的返 回值为6*4=24,最后返回值ff(5)为24*5=120。   例5. 9也可以不用递归的方法来完成。如可以用递推法,即从1开始乘以2,再乘以3&hellip;直到n。递推法比递归法更容易理解和实现。但是有些问题则只能用递归算法才能实现。典型的问题是Hanoi塔问题。      [例5.10]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上,见图5.5(b)。 (2)将A上的一个圆盘移到B,见图5.5(c) (3)将C上的n`-1(等于1)个圆盘移到B,见图5.5(d) B. 将A上的一个圆盘移到C,见图5.5(e) C. 将B上的n-1(等于2,令其为n`)个圆盘移到C(借助A), 步骤如下: (1)将B上的n`-1(等于1)个圆盘移到A,见图5.5(f) (2)将B上的一个盘子移到C,见图5.5(g) (3)将A上的n`-1(等于1)个圆盘移到C,见图5.5(h)。 到此,完成了三个圆盘的移动过程。 从上面分析可以看出,当n大于等于2, 移动的过程可分解为 三个步骤: 第一步 把A上的n-1个圆盘移到B上; 第二步 把A上的一个圆盘移到C上; 第三步 把B上的n-1个圆盘移到C上;其中第一步和第三步是类同的。 当n=3,第一步和第三步又分解为类同的三步,即把n`-1个圆盘从一个针移到另一个针上,这里的n`=n-1。 显然这是一个递归过 程,据此算法可编程如下: 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'); } move(int n,int x,int y,int z) { if(n==1) printf("%-->%c\n",x,z); else { move(n-1,x,z,y); printf("%c-->%c\n",x,z); move(n-1,y,x,z); } } main() { &hellip;&hellip; move(h,'a','b','c'); }   从程序中可以看出,move函数是一个递归函数,它有四个形参n,x,y,z。n表示圆盘数,x,y,z分别表示三根针。move 函数的功能是把x上的n个圆盘移动到z 上。当n==1,直接把x上的圆盘移至z上,输出x→z。如n!=1则分为三步:递归调用move函数,把n-1个圆盘从x移到y;输出x→z;递归调用move函数,把n-1个圆盘从y移到z。在递归调用过程中n=n-1,故n的值逐次递减,最后n=1,终止递归,逐层返回。当n=4 程序运行的结果为 input number: 4 the step to moving 4 diskes: a→b a→c b→c a→b c→a c→b a→b a→c b→c b→a c→a b→c a→b a→c b→c 变量的作用域   在讨论函数的形参变量曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的, 离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。 一、局部变量   局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。 例如: int f1(int a) /*函数f1*/ { int b,c; &hellip;&hellip; }a,b,c作用域 int f2(int x) /*函数f2*/ { int y,z; }x,y,z作用域 main() { int m,n; } m,n作用域 在函数f1内定义了三个变量,a为形参,b,c为一般变量。在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。同理,x,y,z的作用域限于f2内。 m,n的作用域限于main函数内。关于局部变量的作用域还要说明以下几点: 1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。 2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。 3. 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在例5.3 中,形参和实参的变量名都为n,是完全允许的。4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。例如: main() { int s,a; &hellip;&hellip; { int b; s=a+b; &hellip;&hellip;b作用域 } &hellip;&hellip;s,a作用域 }[例5.11]main() { int i=2,j=3,k; k=i+j; { int k=8; if(i==3) printf("%d\n",k); } printf("%d\n%d\n",i,k); } main() { int i=2,j=3,k; k=i+j; { int k=8; if(i=3) printf("%d\n",k); } printf("%d\n%d\n",i,k); }   本程序在main中定义了i,j,k三个变量,其中k未赋初值。 而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第7行对i赋值为3,故以输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第4 行已获得为5,故输出也为5。 二、全局变量 全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。 例如: int a,b; /*外部变量*/ void f1() /*函数f1*/ { &hellip;&hellip; } float x,y; /*外部变量*/ int fz() /*函数fz*/ { &hellip;&hellip; } main() /*主函数*/ { &hellip;&hellip; }/*全局变量x,y作用域 全局变量a,b作用域*/   从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。 [例5.12]输入正方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z的面积。 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("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3); }   本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积,其作用域为整个程序。函数vs用来求正方体体积和三个面积, 函数的返回值为体积v。由主函数完成长宽高的输入及结果输出。由于C语言规定函数返回值只有一个, 当需要增加函数的返回数据,用外部变量是一种很好的方式。本例中,如不使用外部变量, 在主函数中就不可能取得v,s1,s2,s3四个值。而采用了外部变量, 在函数vs中求得的s1,s2,s3值在main 中仍然有效。因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明: 1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函数之外,且只能定义一次。其一般形式为: [extern] 类型说明符 变量名,变量名&hellip; 其中方括号内的extern可以省去不写。 例如: int a,b; 等效于: extern int a,b;   而外部变量说明出现在要使用该外部变量的各个函数内, 在整个程序内,可能出现多次,外部变量说明的一般形式为: extern 类型说明符 变量名,变量名,&hellip;; 外部变量在定义就已分配了内存单元, 外部变量定义可作初始赋值,外部变量说明不能再赋初始值, 只是表明在函数内要使用某外部变量。 2. 外部变量可加强函数模块之间的数据联系, 但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的, 因此在不必要尽量不要使用全局变量。 3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。 [例5.13]int vs(int l,int w) { extern int h; int v; v=l*w*h; return v; } main() { extern int w,h; int l=5; printf("v=%d",vs(l,w)); } int l=3,w=4,h=5;   本例程序中,外部变量在最后定义, 因此在前面函数中对要用的外部变量必须进行说明。外部变量l,w和vs函数的形参l,w同名。外部变量都作了初始赋值,mian函数中也对l作了初始化赋值。执行程序,在printf语句中调用vs函数,实参l的值应为main中定义的l值,等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量,其值为5,因此v的计算结果为100,返回主函数后输出。变量的存储类型各种变量的作用域不同, 就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式, 也称为存储方式。 变量的存储方式可分为“静态存储”和“动态存储”两种。   静态存储变量通常是在变量定义就分定存储单元并一直保持不变, 直至整个程序结束。5.5.1节中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中,使用它才分配存储单元, 使用完毕立即释放。 典型的例子是函数的形式参数,在函数定义并不给形参分配存储单元,只是在函数被调用,才予以分配, 调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、 释放形参变量的存储单元。从以上分析可知, 静态存储变量是一直存在的, 而动态存储变量则而存在而消失。我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的间。 生存期和作用域是从间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。 一个变量究竟属于哪一种存储方式, 并不能仅从其作用域来判断,还应有明确的存储类型说明。   在C语言中,对变量的存储类型说明有以下四种: auto     自动变量 register   寄存器变量 extern    外部变量 static    静态变量   自动变量和寄存器变量属于动态存储方式, 外部变量和静态变量属于静态存储方式。在介绍了变量的存储类型之后, 可以知道对一个变量的说明不仅应说明其数据类型,还应说明其存储类型。 因此变量说明的完整形式应为: 存储类型说明符 数据类型说明符 变量名,变量名&hellip;; 例如: static int a,b;           说明a,b为静态类型变量 auto char c1,c2;          说明c1,c2为自动字符变量 static int a[5]={1,2,3,4,5};    说明a为静整型数组 extern int x,y;           说明x,y为外部整型变量 下面分别介绍以上四种存储类型: 一、自动变量的类型说明符为auto。   这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定, 函数内凡未加存储类型说明的变量均视为自动变量, 也就是说自动变量可省去说明符auto。 在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。例如: { int i,j,k; char c; &hellip;&hellip; }等价于: { auto int i,j,k; auto char c; &hellip;&hellip; }   自动变量具有以下特点: 1. 自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效。在复合语句中定义的自动变量只在该复合语句中有效。 例如: int kv(int a) { auto int x,y; { auto char c; } /*c的作用域*/ &hellip;&hellip; } /*a,x,y的作用域*/ 2. 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数被调用才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此函数调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。例如以下程序: main() { auto int a,s,p; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0){ s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); } s,p是在复合语句内定义的自动变量,只能在该复合语句内有效。而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误。 3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复合语句内), 因此不同的个体中允许使用同名的变量而不会混淆。 即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。例5.14表明了这种情况。 [例5.14] main() { auto int a,s=100,p=100; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0) { auto int s,p; s=a+a; p=a*a; printf("s=%d p=%d\n",s,p); } printf("s=%d p=%d\n",s,p); }   本程序在main函数中和复合语句内两次定义了变量s,p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s,p起作用,故s的值应为a+ a,p的值为a*a。退出复合语句后的s,p 应为main所定义的s,p,其值在初始化给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同, 但却是两个不同的变量。 4. 对构造类型的自动变量如数组等,不可作初始化赋值。 二、外部变量外部变量的类型说明符为extern。 在前面介绍全局变量已介绍过外部变量。这里再补充说明外部变量的几个特点: 1. 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变是是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。 2. 当一个源程序由若干个源文件组成, 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C组成: F1.C int a,b; /*外部变量定义*/ char c; /*外部变量定义*/ main() { &hellip;&hellip; } F2.C extern int a,b; /*外部变量说明*/ extern char c; /*外部变量说明*/ func (int x,y) { &hellip;&hellip; } 在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。 对构造类型的外部变量, 如数组等可以在说明作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。 三、静态变量   静态变量的类型说明符是static。 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量, 例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。 对于自动变量,前面已经介绍它属于动态存储方式。 但是也可以用static定义它为静态自动变量,或称静态局部变量,从而成为静态存储方式。 由此看来, 一个变量可由static进行再说明,并改变其原有的存储方式。 1. 静态局部变量   在局部变量的说明前再加上static说明符就构成静态局部变量。 例如: static int a,b; static float array[5]={1,2,3,4,5};      静态局部变量属于静态存储方式,它具有以下特点: (1)静态局部变量在函数内定义,但不象自动变量那样,当调用就存在,退出函数就消失。静态局部变量始终存在着,也就是说它的生存期为整个源程序。 (2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。 (3)允许对构造类静态局部量赋初值。在数组一章中,介绍数组初始化已作过说明。若未赋以初值,则由系统自动赋以0值。 (4)对基本类型的静态局部变量若在说明未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有会造成意外的副作用,因此仍以采用局部静态变量为宜。 [例5.15]main() { int i; void f(); /*函数说明*/ for(i=1;i<=5;i++) f(); /*函数调用*/ } void f() /*函数定义*/ { auto int j=0; ++j; printf("%d\n",j); }   程序中定义了函数f,其中的变量j 说明为自动变量并赋予初始值为0。当main中多次调用f,j均赋初值为0,故每次输出值均为1。现在把j改为静态局部变量,程序如下: main() { int i; void f(); for (i=1;i<=5;i++) f(); } void f() { static int j=0; ++j; printf("%d\n",j); } void f() { static int j=0; ++j; printf("%d/n",j); } 由于j为静态变量,能在每次调用后保留其值并在下一次调用继续使用,所以输出值成为累加的结果。读者可自行分析其执行过程。 2.静态全局变量   全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它 的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。 四、寄存器变量   上述各类变量都存放在存储器内, 因此当对一个变量频繁读写,必须要反复访问内存储器,从而花费大量的存取间。 为此,C语言提供了另一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用,不需要访问内存,而直接从寄存器中读写, 这样可提高效率。寄存器变量的说明符是register。 对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量。 [例5.16]求∑200i=1imain() { register i,s=0; for(i=1;i<=200;i++) s=s+i; printf("s=%d\n",s); } 本程序循环200次,i和s都将频繁使用,因此可定义为寄存器变量。 对寄存器变量还要说明以下几点: 1. 只有局部自动变量和形式参数才可以定义为寄存器变量。因为寄存器变量属于动态存储方式。凡需要采用静态存储方式的量不能定义为寄存器变量。 2. 在Turbo C,MS C等微机上使用的C语言中, 实际上是把寄存器变量当成自动变量处理的。因此速度并不能提高。 而在程序中允许使用寄存器变量只是为了与标准C保持一致。3. 即使能真正使用寄存器变量的机器,由于CPU 中寄存器的个数是有限的,因此使用寄存器变量的个数也是有限的。 内部函数和外部函数   函数一旦定义后就可被其它函数调用。 但当一个源程序由多个源文件组成, 在一个源文件中定义的函数能否被其它源文件中的函数调用呢?为此,C语言又把函数分为两类: 一、内部函数   如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被同一源程序其它文件中的函数调用, 这种函数称为内部函 数。定义内部函数的一般形式是: static 类型说明符 函数名(形参表) 例如: static int f(int a,int b) 内部函数也称为静态函数。但此处静态static 的含义已不是指存储方式,而是指对函数的调用范围只局限于本文件。 因此在不同的源文件中定义同名的静态函数不会引起混淆。 二、外部函数   外部函数在整个源程序中都有效,其定义的一般形式为: extern 类型说明符 函数名(形参表) 例如: extern int f(int a,int b)如在函数定义中没有说明extern或static则隐含为extern。在一个源文件的函数中调用其它源文件中定义的外部函数,应 用extern说明被调函数为外部函数。例如: F1.C (源文件一) main() { extern int f1(int i); /*外部函数说明,表示f1函 数在其它源文件中*/ &hellip;&hellip; } F2.C (源文件二) extern int f1(int i); /*外部函数定义*/ { &hellip;&hellip; } 本章小结 1. 函数的分类 (1)库函数:由C系统提供的函数; (2)用户定义函数:由用户自己定义的函数; (3)有返回值的函数向调用者返回函数值,应说明函数类型( 即返回值的类型 ); (4)无返回值的函数:不返回函数值,说明为空(void)类型; (5)有参函数:主调函数向被调函数传送数据; (6)无参函数:主调函数与被调函数间无数据传送; (7)内部函数:只能在本源文件中使用的函数; (8)外部函数:可在整个源程序中使用的函数。 2. 函数定义的一般形式 [extern/static] 类型说明符 函数名([形参表]) 方括号内为可选项。 3. 函数说明的一般形式 [extern] 类型说明符 函数名([形参表]); 4. 函数调用的一般形式 函数名([实参表]) 5. 函数的参数分为形参和实参两种,形参出现在函数定义中,实参出现在函数调用中,发生函数调用,将把实参的值传送给形参。 6. 函数的值是指函数的返回值,它是在函数中由return语句返回的。 7. 数组名作为函数参数不进行值传送而进行地址传送。形参和实参实际上为同一数组的两个名称。因此形参数组的值发生变化,实参数组的值当然也变化。 8. C语言中,允许函数的嵌套调用和函数的递归调用。 9. 可从三个方面对变量分类,即变量的数据类型,变量作用域和变量的存储类型。在第二章中主要介绍变量的数据类型,本章中介绍了变量的作用域和变量的存储类型。 10.变量的作用域是指变量在程序中的有效范围, 分为局部变量和全局变量。 11.变量的存储类型是指变量在内存中的存储方式,分为静态存储和动态存储,表示了变量的生存期。 12.变量分类特性表存储方式存储类型说明符何处定义生存期作用域赋值前的值可赋初值类型动态存储自动变量 auto 寄存器变量 register 函数或复合语句内被调用在定义它的函数或复合语句内不定基本类型int或char外部变量extern函数之外整个源程序整个源程序静态局部变量static 函数或复合语句内静态全局变量static 函数之外整个源程序在定义它的函数或复合语句内在定义它的源文件内0任何类型 资料收集:beck Copyright 2002 www.vcok.com, All Rights Reserved

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值