C语言快速入门


C 语言快速入门 C 语言快速入门 --以 C 语言为例,学一门计算机语言 BY DieHeart, Dec 30th, 2007 前言 入门,是一种思考方法的转变。 这本小册子最初是在上学时完成初稿,曾经命名《C 语言 4 小时通》、《C 语言速成》等,主要供学 弟学妹参考,侧重点与现在有所不同。经过仔细筛选和整理,现为其命名《C 语言快速入门》,主要介 绍和分析入门阶段经常遇到的问题,从开始就认清写计算机书骗钱的人的本质。 阅读本文的朋友也许已经阅读过很多 C 语言的书,那么也许你会发现这本与其他的书有很大的不 同。这是一本笔记:简明扼要,提纲挈领,读起来也许会口干舌燥,却不至于失去方向;本书的叙述顺 序可能更象一本手册,纯粹为增强层次感和阅读方便而编辑。 语言是一种交流的工具。 和中文、英语不同的是,计算机编程语言是人与计算机之间的交流,不是人与人之间,也不是计算 机与计算机之间。其实,交流也谈不上,计算机只是按照人的意思机械地执行。 计算机基础知识 本节及下节中涉及的词语比较适合放在引擎中搜索。 组成: CPU 主频 指令 MEM 内存 计算机运行程序之处 DISK 磁盘 计算机存储数据之处 IO 接口 串行 单车道 并行 多车道 USB 网卡 VGA 显示 DEVICE 设备 键盘 鼠标 打印机 计算机语言 函数 名字 C 语言快速入门 数据 结果 数据 控制函数细节 取得函数结果 运算 函数 直观 结构 顺序 条件 循环 0 C 语言概览 在讲授编程语言时,经常把编程与日常的其他工作相类比,这里摘录两个典型的类比的例子。 编程的过程如同炒一盘菜。原料构成数据,加工过程是函数,先后次序和逻辑关系构成程序结构, 最终,得到了一盘大菜。 编程就象写文章。字母构成词汇,词汇和标点构成句子,句子和结构形成文章。 对于某种特定的语言,把它当成特定的菜系或文章的体例好了。 程序=算法+语言+工具+编程技术 这 4 个方面是一个程序设计人员所应具备的知识。 本文的目的是使初学知道怎样编写一个 C 程序,进行编写程序的初步训练,因此,只介绍语言的 初步知识,以及针对简单要求的具体算法,并把编程技术的内容融入各章节。 编程者需要做的是,设计解决一个具体问题的算法,选择一种计算机语言,使用已知的编程技术, 对该算法进行表达,应用该语言的一个编译&连接工具/环境的翻译,最终得到一个可在计算机上直接 运行的程序,调试该程序,确认可完成问题的计算机解决,提交一份报告。 概念: 源程序,是用某语言表达的解决方案的格式文本。 编译器,把源程序翻译成可供计算机 CPU 执行的目标代码的翻译工具。 OS,操作系统,提供计算机基本硬件管理的用户接口,通常把一些功能已经做成模块,以 API 形 式提供给用户使用。最常用的功能之一是用户程序的加载。 程序,以计算机 CPU 机器指令形式表达的某问题的解决方案。程序按其状态分为两种,一种是存 储备用状态,程序只存在于计算机的存储介质上,如光盘、软盘、硬盘、U 盘等;一种是正在执行状态, 此时程序处于计算机内存条中,并按流水线方式送入 CPU 寄存器执行。显然必须执行程序才能实现某 问题的解决。 0.1 字符集 字符是组成语言的最基本的元素。C语言字符集由字母,数字,空白,标点和特殊字符组成。 0.1.1 字母 character 英文字母的大小写。对于 C 语言,是大小写敏感的语言。不象有些语言,大小写不作区分。 C 语言快速入门 C 语言中,下划线可以认为是一个特殊的字母。 注意:拉丁语字母、俄语字母、其他扩展 ASC-Ⅱ的语言字符等在 C 语言中不要把它当成字母。 字母是构成 C 语言词汇中关键字、标志符的基础。有时,字母也表示十六进制数的一部分。 0.1.2 数字 number 阿拉伯数字。 少罗索,上小班时就知道了。 数字构成了主要的常数;和字母组合时,也可构成标志符。 0.1.3 空白符 blank 空格符、制表符、换行符等统称为空白符。 空白符只在字符常量和字符串常量中起作用。在其它地方出现时,只起间隔作用,编译程序对它们忽略 不计。因此在程序中使用连续的空白符,对程序的编译不发生影响,但在程序中适当的地方使用空白符 将增加程序的清晰性和可读性。 0.1.4 标点 point 键盘上所能看到的所有标点。 0.1.5 特殊字符 special-character 在字符常量、字符串和注释中还可以使用汉字或其它可表示的图形符号,其值属于扩展 ASC-Ⅱ。 通常用的最多的是中国的汉字和全角符号。 一种典型的中文习惯错误是,把中、英文的逗号和分号误用了。 0.2 词汇 正是为数不多的词汇,描述了 C 语言的简单性。 词汇是各种语言表达语义的基础。 0.2.1 关键字 keyword 关键字是由C语言规定的具有特定意义的字符串,通常也称为保留字。C语言的关键字分为以下几类: 类型说明 用于定义、说明变量、函数或其它数据结构的类型。如 int,double。 结构控制 用于表示一个语句的功能。如 if else 就是条件语句的语句定义符。 后面 1 关键字分类一节对每个关键字有更详细的说明。 0.2.2 标识字 identifier 在程序中使用的变量名、函数名、标号统称为标识字。 标识字只能是字母(A~Z,a~z)、数字(0~9)、下划线(_)组成的字符串,并且其第一个字符必须是字母 或下划线。 用户定义的标识字不应与关键字、库函数名相同。 标准 C 不限制标识符的长度,但它受各种版本的 C 语言编译系统限制。 C 语言快速入门 0.2.3 常数 constant 按引用方式分类: 值常数:详见 2.1.1、2.1.2、2.1.3 各节。 字符串指针常数:详见 2.1.4 字符串指针常数一节。 符号常数:有两种定义方法:定义成枚举型符号整数或任意类型符号替代型常数。 0.2.4 注释 comment 注释是以“/*”开头并以“*/”结尾的串。在“/*”和“*/”之间的即为注释。程序编译时,不对注释 作任何处理。注释可出现在程序中的任何位置。注释用来向用户提示或解释源程序的意义。 0.2.5 运算子 operator 构成运算的符号。 常见的+-*/等。 几乎每种编译器都提供了关于 C 语言运算子符号、操作数格式、运算优先级的帮助说明,使用现成的吧。 如果找不到,试试这个联接,运算。 运算符的意义在于直观表达函数的功能。 使用运算符就一定直观吗?不一定。要特别注意存在一些多义运算符和不常用的运算优先级和结合律。 0.3 句子、段落、章节 好文章要有华丽的词藻,缜密的逻辑,漂亮的书法。 好程序也是如此。 变量和函数名称要望文知义,逻辑层次分明,模块化结构,适当注释。 一个应用的程序,紧凑、精巧的编写有时不比可读性更重要。源程序多几个空格和注释不会影响程序执 行的效率,却能大大提高维护的效率。 0.4 小结 前面,分析的过程主要是词法、语法的分解。 不要被这些说明吓到。 要记住: 一盘菜是由原料和烹调环节构成的。 程序就是由数据和数据的处理过程构成的。 从下章开始,我们就着重从 C 语言的功能性上进行剖析(语义分析)。 1 关键字分类 假如说一种语言只有四五十个单词,那么我们有理由相信这种语言是简单的。 ANSI C 一共只有 32 个关键字,按字母顺序: auto break case char const continue default do double else enum extern float for goto if int long register return short signed static sizof struct switch typedef union unsigned void volatile while C 语言快速入门 Turbo C 扩充了 11 个关键字: _cs _ds _es _ss asm cdecl far huge interrupt near pascal 单词都比较简单,没有背诵的难度。如果再将他们按用途分类的话,就更加清晰了。 1.1 数据类型说明 用于声明变量的类型 int char float short long double signed unsigned enum 用于构造一个复杂的组合数据类型 struct union 用于给某类型取个别名 typedef 1.2 数据存取说明 const 说明不可显式更改的变量,帮助文件中的解释 make variable value unmodifiable 非常不错。但是, 通过指针仍可以修改 const 变量的值。 register 说明寄存器变量,但如果寄存器不够用的话,该请求会被转换成内存变量。 volatile 说明有可能被后台程序更改的变量,当引用该值时,重新读内存以获取值而不是可能从寄存器 得到值,编写中断处理相关程序的朋友要留意了。 1.3 数据作用域控制 extern 说明此标志符已经在其他文件中声明过了。 1.4 数据生存期控制 auto 说明局部生存期变量。如不显式声明,变量默认为局部生存期,所以 auto 并不常用。 static 说明全局生存期变量。初始化只有一次,再次初始化过程将被忽略。 1.5 无值传递函数说明 void 用于说明函数无返回值或无参数。 1.6 函数作用域控制 static 内部函数声明,说明函数仅限本文件内可引用。 extern 外部函数声明,说明函数可被其他文件引用。函数默认为外部函数。 引用外部函数前也需要用 extern 声明。 使用作用域控制可减少合作开发中的函数名冲突。 C 语言快速入门 1.7 取数据占用内存空间运算符 sizeof 非常特殊,运算符也是关键字。如果再广义一点,类型关键字也可作为类型强制转换运算符。 1.8 程序结构控制 if else switch case default goto for do while continue break return 就是这几个关键字,构成了源程序的骨架,错综复杂的程序结构。 2 数据 对于基本数据类型量,按其取值是否可改变又分为常量和变量两种。在程序执行过程中,其值不发 生改变的量称为常量,其值可变的量称为变量。 比如一元二次方程,x 为变量,各项的系数为常量。但是也要注意,C 语言中的变量和常量的含义 和使用方法与数学是有区别的。 不管常量的种类和内容如何,它们共同的特点是: 在程序执行过程中,值保持不变。 如果它们占用内存的话,那么占用的内存位置也不会改变。 普通变量的特点是: 在生存期内,值可以改变; 它占用的内存不变。 指针变量的特点是: 在生存期内,值可以改变; 它占用的内存一般均动态分配。 C 语言的数据有多种类型,数学运算只是其中一个方面,多种类型用于存储不同的数据。可分为字符型、 整型、枚举整型、浮点型、数组型、结构型。 在 C 语言中,直接常量数据是可以不经说明而直接引用的,而变量数据则必须先定义后使用。 2.1 直接数据-常数 程序中直接使用的一段文字、一个常量等,固定不变的量。 2.1.1 整数常数 整数是计算机及 C 语言中最基本、最简单、最灵活、最广泛的数据了,所以先要把它搞清楚。 24*7 也许某个业务软件属于全周、全天工作时间,那么也许会用到这个整数。 整数数制 在 C 程序中是根据前缀来区分各种进制数的。因此在书写常数时不要把前缀弄错造成结果不正确。 C 语言快速入门 无前缀时为十进制,即默认为十进制整数。 以 0 为八进制数的前缀,数码取值为 0~7。 以 0x 或 0X 为前缀是十六进制,后面接 0~f 或 0~F 表示的各位的值。 整数数长 在 16 位字长的机器上,基本整型的存储长度也为 16 位,因此表示的数的范围也是有限定的。如果 使用的数超过了上述范围,就必须用长整型数来表示。长整数的存储长度为 32 位。长整型数是用 后缀“L”或“l”来表示的。 无符号数也可用后缀表示,整型常数的无符号数的后缀为“U”或“u”。 编程时,可以综合前缀与后缀来表示特定的整数。 枚举整数 enum 关键字用于定义符号形式的一组整数常量。 感觉 enum 和 int 非常类似。 enum CHOICE { NO1, NO2, NO3 }; typedef CHOICE int; #define NO1 0 #define NO2 1 #define NO3 2 两种方式似乎都可达到相同的目的,有什么差别的话可能要看反汇编代码才知道。 尽管提出枚举类型的初衷是想对变量的取值范围限定在枚举的集合内,但实际上并不是所有的编译 环境都对此进行严格检查,所以上例中使用: enum CHOICE ch=4; 不一定会被编译器警告。 此处还用到了预处理。 #define 标识符 常量 其中#define 是一条预处理命令(预处理命令都以"#"开头),称为宏定义命令,详见 6 预处理一章。 „ 符号常量与变量不同,它的值在其作用域内不能改变,也不能再被赋值。 „ 使用符号常量的好处是:含义清楚;能做到“一改全改”。 2.1.2 浮点常数 在C语言中,浮点常数只采用十进制。它有二种形式:十进制小数形式,指数形式。 十进制数形式:由数码 0~ 9 和小数点组成。 例如:3.14159 圆周率,数学运算很常用。 指数形式:由十进制数,加阶码标志“e”或“E”以及阶码(只能为整数,可以带符号)组成。 其一般形式为: a E n(a 为十进制数,n 为十进制整数) 其值为 a*10n。 标准C允许浮点数使用后缀。后缀为“f”或“F”即表示该数为浮点数。如 356f 和 356.是等价的。 C 语言快速入门 浮点常数数长:8 字节。可能并不是我们想象的结果。但考虑到精度优先的运算法则,这也就不难 理解了。 2.1.3 字符常数 字符常数是通常由一对单引号括起的单个 ASCⅡ码字符,取值范围固定为 0~255。但对个别字符, 可能存在特殊的含义,以更人性化的表达人的意图,这种方式是改变原来字符的含义,专门表达特殊含 义的字符称为转义字符。 注意区别‘0’与‘\0’:以‘’标记的字符的值是它在 ASC-Ⅱ中的顺序号,如此例前值为 48 而 后值为 0。 转义字符 转义字符 转义字符的意义 ASCⅡ码序数值 \r 回车 13 \n 回车换行 10 \f 走纸换页 12 \t 横向跳到下一制表位置 9 \v 纵向制表 \b 退格 8 \a 鸣铃 7 \\ 反斜线符"\" 92 \' 单引号符(在 bc31 下不用\也可以) 39 \” 双引号符 34 \? 问号(在 bc31 下不用\也可以) \ddd 1~3 位八进制数所代表的字符 \xhh 1~2 位十六进制数所代表的字符 \后面不接以上规定的转义字符时,自动被编译系统过滤掉,不会连接进执行程序中。 既然,ASCⅡ码序数值是个 0~255 范围内的整数,可以想象整数与字符之间存在范围上的子集关系, 所以直接操作 0~255 的数值,也可以达到使用任意 ASCⅡ码字符的目的。 字符常数数长: 每个字符常量被分配 2 个字节的内存空间,字符值是以 ASCII 码的形式存放在内存单元之中。 所以这与我们平常的思考习惯并不一致,注意与字符变量的区别。 字符常数操作经常用于从键盘获得输入,向屏幕输出,以及文本文件操作等人机交互的场合。 2.1.4 字符串指针常数 字符串指针常数是由一对双引号括起的字符序列。如“人吃肉,狗吃?” 字符串指针常数的值是字符串在内存中占内存的起始地址。 所以容易分辨’a’和”a”的区别。比较’a’== ’a’返回真,”a”== ”a”返回假。 字符串指针常数占的内存数等于字符串中字符个数加 1。增加的一个字节中存放字符"\0",标志字 符串的结束。 即使是空字符串,它也要占用 1 字节的空间,专门存储字符串结束标志"\0"。 C 语言快速入门 2.2 间接数据-变量 变量命名规则:变量名是一种标志字,命名原则在不能使用 C 语言保留字,如类型名,运算符等; 第一个字符必须为字母;同一作用域不能有同名的变量。 变量声明规则:类型说明符与变量名之间至少用一个空白符间隔;可在一个类型说明符后,定义多 个相同类型的变量,各变量名之间用逗号间隔;最后一个变量名之后必须以“;”号结尾。 变量初始化规则:声明变量的同时可给变量初始化。但在声明中不允许连续初始化。 如 int a=b=c=5 是不合法的,但可以 int a=5,b=5,c=5;或 int a,b,c;a=b=c=5;。 变量使用规则:使用前必须先声明变量。 程序中使用的用来存放量的空间,可以认为它是一个内存空间的标识符号。这也是间接数据的间接 性的由来。 由上面叙述来看,变量的声明过程,其实质是为存储某种类型数据而声明一个内存空间的符号化表 达式。 引申: 内存中的位置可用连续的无符号整数来表达,所以整数与内存位置之间存在着可相互表达的关系。 普通变量声明的实质是给固定的内存地址、固定的存储类型规定了符号。但完全可以先规定符号的 存储类型和符号,在需要存储数据之前再分配存储空间,这种变量又称为指针变量,指针的意思也 就是内存位置。 声明指针变量时,只需要在类型和变量名之间增加指针符号 *。 说明: 普通变量的名字尽管表示的是一个内存空间的位置(即地址,是个无符号整数),但直接引用的是 该空间内存储的某类型的数据;要使用地址本身,需要用取地址运算 &变量名 来实现。 指针变量的名字表示的是一个内存空间的位置,直接引用的是该位置;要使用该内存空间中的某类 型的数据,需要用取数据运算 *变量名 来实现。 2.2.1 变量类型声明 字符 char 整数 int 无符号整数 unsigned int 长整数 long int 双精度长整数 double long int 枚举整数 enum 浮点数 float 双精度浮点数 double float 尽管类型好象较多,但其用法基本一样。下面以整数类型的变量为例分析。 int i=0; 将内存中的一块可存放 int 类型大小的固定空间做了标记 i,并把内容初始化成 0;&i 表示内存地址,i 表示其内容。 int ai[N]; 将内存中的一块可存放 N 个 int 类型大小的固定空间做了标记;起始地址为 ai,&ai[i]表示第 i 个数据的 C 语言快速入门 内存地址,ai[i]表示其内容。 数组的其他性质专门在下章叙述。 int *pi; 将内存中的一块可存放 int*类型大小的固定空间做了标记 pi;&pi 表示内存地址,pi 表示其内容,内容 是存放某个整数的内存位置。 int*类型:即某 int 类型变量的地址。 把声明更改为 int π更容易让我理解,可以知道 pi 存放的是地址,可实际上人家没这么干。 pi=&i; 将 i 在内存中的位置存放到 pi 中,引用 pi 即得到 i 的地址,引用*pi 即得到 i 的值。 pi=ai; 将数组 ai 的起始位置存放到 pi 中,引用 pi 即得到 ai 的起始地址,引用*pi 即得到 ai[0]的值。 int **ppi; 将内存中的一块可存放 int**类型大小的固定空间做了标记 pi;&pi 表示内存地址,pi 表示其内容,内 容是一个内存位置,该位置存放某个整数的内存位置。 ppi=π 将 pi 在内存中的位置存放到 ppi 中,引用 ppi 即得到 pi 的地址,引用*ppi 即得到 pi 的值即 i 的地址, 引用**ppi 即得到 i 的值。 简化叙述,数据与内存地址的对应关系一目了然。 2.2.2 初始化 在声明变量时可进行初始化,方法参考前节即可。 初始化与赋值均使用了=符号,但其意义是不同的。变量初始化,是程序的变量处存放的初始默认值; 赋值,是执行过程中将变量的值改变的过程。有些编程语言将此二者进行了符号化区分,以避免概念混 淆。 初始化不是赋值。 2.2.3 引用方式 直接引用,间接引用,参考前节即可。 2.2.4 存储结构 以上节为例: 由上表可以看出,改变一个变量的方式有很多种,有时是在不经意间悄悄进行。 内存位置 标记 内容 100 i 0 102 pi 100 104 ppi 102 C 语言快速入门 比如引用 ppi[0][0]可以改变 i 的值,而引用 ppi[0]可以改变 pi 与 i 之间的地址/数据对应关系。 一条狗装在一个笼子里,另有一个笼子存放别的笼子… 2.2.5 作用域 全局变量 声明在所有模块之外,作用在声明后。 局部变量 声明在模块之内,作用在模块内声明后。 静态变量 不改变变量作用域,只改变生存期。其初始化过程只执行一次,再次初始化将被忽略。 形参变量 声明在函数形参表,作用在函数内。 外部变量 声明在外部文件的全局变量,作用于包含该变量外部声明的所有文件。 尽量不要联系外星人。 2.2.6 生存期 全局变量 生存于程序执行全过程。 局部变量 生存于模块执行阶段。 形参变量 生存于该函数执行过程。 静态变量 生存于程序执行全过程。 动态内存 生存于模块执行阶段,申请之后,释放之前(如果没忘记释放的话)。 不要联系已死的人。 2.3 序列数据-数组 将某种类型的数据放在一起,用下标的方式来引用第几个数据,这种方法被定义成数组。 2.3.1 数组类型声明 类型说明符 数组名 [常量表达式]; 类型说明符 数组名[常量表达式 1][常量表达式 2]; C语言允许构造多维数组。 数组的类型实际上是指数组元素的取值类型。 下标中的常数表示构成数组的元素的数目。 如在声明同时给全部成员初始化,则不必写出元素个数。 例:一个学习小组有 4 个人,每个人有三门课的考试成绩,成绩表可以存放在数组中。 张 王 李 赵 C 语言快速入门 Math 80 61 59 85 C 75 65 63 87 Chinese 92 71 70 90 按行分段初始化可写为: int score[4][3]={ {80,75,92},{61,65,71},{59,63,70},{85,87,90}, }; 按行连续初始化可写为: int score[4][3]={ 80,75,92,61,65,71,59,63,70,85,87,90}; 若只对部分元素赋初值,未赋初值的元素自动取 0 值。 若对全部元素赋初值,则第一维的长度可以不给出。 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。 字符串常量总是以'\0'作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符'\0' 存入数组,并以此作为该字符串是否结束的标志。 可用字符串形式对数组作初始化: 例如: char c[]={'c', ' ','p','r','o','g','r','a','m'}; 可写为: char c[]={"C program"}; 或去掉{}写为: char c[]="C program"; 注意: 字符串结束判断的是'\0'标志,数组空间是编译编译器实际分配的内存空间,两者正好相差1 个字 符。 2.3.2 引用方式 数组的下标从 0 开始。 数组有变量和常量的两种性质: 它是一个名字、以下标区分的、多个存储空间的集合-象变量; 它的存储空间的大小、在内存中的位置固定-象常量。 当引用数组的元素时,把它当成变量使用,当引用数组名本身时,相当于引用常量指针。这也决定 了两个数组不能互相赋值。可以采取 for 循环为元素赋值的方法。 2.3.3 存储结构 数组占用连续的存储空间,空间排列按行主序连续排列。 C 语言快速入门 2.3.4 二维数组与一维数组嵌套的关系 内存位置 标记 内容 100 score,score[0],score[0][0] 80 104 score[0][1] 75 108 score[0][2] 92 112 score[1],score[1][0] 61 116 score[1][1] 65 120 score[1][2] 71 124 score[2],score[2][0] 69 128 score[2][1] 63 132 score[2][2] 70 136 score[3],score[3][0] 85 140 score[3][1] 87 144 score[3][2] 90 内存位置 标记 内容 100 c,c[0] ‘C’ 101 c[1] ‘ ’ 102 c[2] ‘p’ 103 c[3] ‘r’ 104 c[4] ‘o’ 105 c[5] ‘g’ 106 c[6] ‘r’ 107 c[7] ‘a’ 108 c[8] ‘m’ 109 c[9] 0 C 语言快速入门 2.4 组合数据-结构 有一本书,名字就叫《数据结构》,它就是以结构型的数据为基础的,主要研究建立在数据结构基础上 的一系列算法,本章简要对相关内容进行介绍。 结构化的数据是优、美程序的基础。 2.4.1 结构类型声明 结构是由基本类型组合构成的类型。 所以要产生一个结构类型的变量要经过两个基本步骤:构造结构类型和声明该结构的变量。 构造结构类型 定义一个结构的一般形式为: struct 结构名 {成员变量声明表}; 成员名的命名应符合标识符的书写规定。例如: struct stu { int num; char name[20]; char sex; float score; }; 在这个结构定义中,结构名为 stu,该结构由 4 个成员组成。第一个成员为 num,整型变量;第二 个成员为 name,字符数组;第三个成员为 sex,字符变量;第四个成员为 score,实型变量。 注意:构造结构类型后的分号是不可少的。 结构是一种数目固定、类型不同的有序变量构成的复杂数据类型。 包含位成员变量的结构类型 struct AX { unsigned AL:8; unsigned AH:8; }; 结构 AX 由两个 8 位的无符号成员 AL、AH 构成。 只构造结构类型并不产生实际数据的存储单元,只有在声明该结构 类型的变量后才真正实现该结构。 声明结构变量 结构类型定义之后,即可进行变量说明。凡说明为结构类型 stu 的变量都由上述 4 个成员组成。 struct stu student[60]; 声明了一个 60 元素的数组,数组的每个元素都包含 num、name、sex、score 四个成员属性。 C 语言快速入门 其他声明结构变量的方式 预定义方式 #define STU struct stu STU { int num; char name[20]; char sex; float score; }; STU student[60]; 构造结构类型与声明结构变量同时 struct stu { int num; char name[20]; char sex; float score; } student[60]; 省略结构类型名 struct { int num; char name[20]; char sex; float score; } student[60]; 2.4.2 初始化 在任何声明结构类型变量处均可对变量成员初始化。 一般形式: STU student[60]={{0,””,0,0},{1,”DieHeart”,1,80.0}}; 初始化严重不是赋值。 这是普通基本变量的原则,同样适用于结构类型变量。所以,除了在初始化时,妄图使用: student1={1,”DieHeart”,1,80.0}; 对 student1 进行赋值是不能实现的。 作为整体使用结构类型变量的方法可参考下节。 C 语言快速入门 2.4.3 引用方式 一般情况下,通过结构类型的变量引用其成员变量的属性。 (*结构指针变量).成员名 或为: 结构指针变量->成员名 student[1].num=1; strcpy(student[1].name,”DieHeart”); (&student[1])->sex=1; (&student[1])->score=80.0; 可以将结构变量做为一个整体操作。 student[2]=student[1]; 尽管内部含有数组变量成员,尽管数组变量成员不能直接赋值,但是这样对结构变量做赋值运算却是可 以的,具体原因在通常的 C 语言书籍中未做解释,我也不清楚,不理解。但是学过 C++的朋友们可能 对类对象的拷贝构造成员函数有印象,所以我大胆猜测,他们用的就是同一机制。 拷贝构造成员函数相关内容可参考本书娣妹篇《从 C 到 C++》一文。 2.4.4 存储结构 结构类型变量占用内存量是各成员变量占用内存量之和。 构造成员共用空间的结构类型 union ACC { struct AX ax; int acc; 内存位置 标记 内容 … 100 student[0].num 0 102 student[0].name 0 122 student[0].sex 0 123 student[0].score 0 127 student[1].num 1 129 student[1].name DieHeart 149 student[1].sex 1 150 student[1].score 80.0 … C 语言快速入门 }; 有的书(大多数)将此结构称为联合,也有些称为共用体。构造了一个共用结构类型 ACC,其结构类 型成员 ax 和整数类型成员 acc 将共用一块内存空间。 由于其共用空间,决定数据写入该类型的变量时,数据成员之间是相互覆盖的方式。 共用体结构类型的变量的成员变量具有相同的地址。 共用体结构类型的变量占用的内存空间与它的成员变量中占用内存空间最多的相同。 数据/代码覆盖的方式太多了,一不小心就会意外实现。 2.4.5 静态链表 用结构数组构造静态链表 struct stu { int num; struct stu *next; }boy[5]; for(int i=0;i<5;i++) boy[i].next=boy[(i+1)%5]; 构造了一个 5 个元素的单向循环链表,可通过 next 指针引用下一个节点。 2.4.6 动态链表 用动态内存分配替代静态数组 struct stu *girl=(struct stu *)malloc(sizeof(struct stu [n])); for(int i=0;i、小于<、等于==、 大于等于>=、小于等于<=和不等于!=六种。 3.1.3 逻辑运算符 用于逻辑运算。包括与&&、或||、非!三种。 3.1.4 位操作运算符 参与运算的量,按二进制位进行运算。包括位与&、位或|、位非~、位异或^、左移<<、右移>>六种。 3.1.5 赋值运算符 用于赋值运算,分为简单赋值=、复合算术赋值+=、-=、*=、/=、%=和复合位运算赋值&=、|=、^=、 >>=、<<=三类共十一种。 要避免将复合赋值运算神秘化。 变量 双目运算符=表达式 它等效于 变量=变量 运算符 表达式 复合赋值符这种写法,对初学者可能不习惯,据说有利于编译处理,能提高编译效率并产生质量较高的 目标代码。 3.1.6 条件运算符 这是一个三目运算符,用于条件求值? :。 3.1.7 逗号运算符 用于把若干表达式组合成一个表达式,。其求值过程是分别求两个表达式的值,并以最后一个表达 式的值作为整个逗号表达式的值。 并不是在所有出现逗号的地方都组成逗号表达式,如在变量说明、函数参数表中,逗号只是用作各 变量之间的间隔符。 3.1.8 指针运算符 用于取内容*和取地址&二种运算。 3.1.9 求字节数运算符 用于计算数据类型所占的字节数 sizeof。 3.1.10 特殊运算符 有括号( )、下标[ ]、成员->、.等几种。 C 语言快速入门 3.2 运算优先级与结合性 3.2.1 优先级 运算符的运算优先级共分为 15 级。1 级最高,15 级最低。在表达式中,优先级较高的先于优先级较低 的进行运算。 3.2.2 结合性 而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。 优先级 运算符 说明 结合性 {} 描述这里面是个功能模块 () 括号 [] 下标运算 -> 取结构成员 1 . 取结构成员 左结合 ! 逻辑运算:非 ~ 位取反 ++ 自增 -- 自减 - 负号 () 类型转换 * 取地址处的值 & 取标志字的内存位置 2 sizeof 取数据长度 右结合 * 算术运算:乘法 / 除法 3 % 求余 左结合 + 加法 4 - 减法 左结合 << 移位运算:左移 5 >> 移位运算:右移 左结合 < 关系运算 <= > 6 >= 左结合 == 关系运算 7 != 左结合 8 & 位运算:与 左结合 9 ^ 位运算:异或 左结合 10 | 位运算:或 左结合 11 && 逻辑运算:与 左结合 12 || 逻辑运算:或 左结合 C 语言快速入门 13 ?: 条件运算 右结合 = 赋值运算 14 复合赋值运算 右结合 15 , 逗号运算 左结合 在不记忆优先级时,可以采取括号运算符,防止发生不必要的人机误解。 现在流行一句话,叫“理解万岁”。 3.3 数据类型转化 涉及不同类型数据之间的运算问题,这在其他编程语言中通常是不允许的。 3.3.1 自动类型转换 发生在不同数据类型的量混合运算时,由编译系统自动完成。自动转换遵循以下规则: 1) 若参与运算量的类型不同,则先转换成同一类型,然后进行运算。 2) 转换按数据长度增加的方向进行,以保证精度不降低。如 int 型和 long 型运算时,先把 int 量 转成 long 型后再进行运算。 3) 所有的浮点运算都是以双精度进行的,即使仅含 float 单精度量运算的表达式,也要先转换成 double 型,再作运算。 4) char 型和 short 型参与运算时,必须先转换成 int 型。 5) 在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类 型。如果右边量的数据类型长度左边长时,将丢失一部分数据,这样会降低精度,丢失的部 分按四舍五入向前舍入。 3.3.2 强制类型转换 强制类型转换是通过类型转换运算来实现的。 其一般形式为: (类型说明符) (表达式) 其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。 例如: (float) a 把 a 转换为实型 (int)(x+y) 把 x+y 的结果转换为整型 在使用强制转换时应注意以下问题: 1) 类型说明符和表达式都必须加括号(单个变量可以不加括号),如把(int)(x+y)写成(int)x+y 则成 了把 x 转换成 int 型之后再与 y 相加了。 2) 无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性 转换,而不改变数据说明时对该变量定义的类型。 4 程序结构 如果不对程序的结构加以控制,那么,程序只能一条条按顺序执行下去。但是,似乎这也太平淡了, 如果对每个功能模块加以有条件的执行,或是自动多次的执行,那么这就需要本节所涉及到的程序结构 控制指令。 4.1 转移指令 goto;break;continue; C 语言快速入门 goto 指无条件转移,程序执行到此时,直接转移到指定标号处继续执行。用法如下: labeln:/*标号*/ /*本行表示程序注释*/ goto labeln;/*转到标号 labeln*/ break 一般与条件、循环、分支结合使用,表示结束当前循环或分支并跳出该循环或分支。 continue 一般与条件、循环结合使用,表示结束当前循环并从下一个循环的开始处继续执行。 4.2 条件结构 if(条件表达式){;}else{;} if 表示对条件表达式的判断,结果为真执行其后面的语句,否则执行 else 后的语句。 4.3 分支结构 switch(分支源变量){case 分支点常量: default:;} 分支源变量是对各种情况进行分支选择的依据,当分支源变量与某分支点常量匹配时,就转移到该 分支,否则转移到 default 分支。 由于分支结束后可能继续下一分支,所以一般在每一分支结束处加 break;结束该分支。 switch(j) { case 0: i=0;break; case 1: i=1;/*此处未加 break;将会导致继续执行 case 5:*/ /*switch 到 case 1 结束后,i 并不为 1,而是 5*/ case 5: i=5;break; default: i=-1; break; } 要求 switch 后的分支源表达式结果必须为整数变量,而分支必须具有唯一性 unique。 4.4 循环结构 有两种 for 和 while 循环结构 for(初始表达式;条件表达式;增量表达式){;} 初始表达式只在循环开始前执行一次 条件表达式在每次循环开始前进行判断 增量表达式在每次循环结束后执行 每次循环具体执行内容置于{;}中 for(i=0,j=0;i<10;i=i+1)/*初始值 i=0,j=0 *条件 i<10 由于受到循环内部的 break 影响并不能执行到 8 以后*/ C 语言快速入门 { if(i==5)continue; /*当 i 的值为 5 时,continue 将导致程序从此处跳到 for(…;i=i+1)开始 的下一循环*/ if(i==8)break; /*当 i 的值为 8 时,break 将导致程序从此处跳到 for(…){…}的循环外*/ else j=j+i; /*当 i 的值不为 5 且大于 0 小于 8 时,j 将 i 累加*/ } 循环结构 while(条件表达式){;} 每次循环前对条件表达式进行判断,结果为真执行{;}中的语句 相当于 for(;条件表达式;){;} 4.5 结构嵌套 哈,把上面所说的控制相互结合,构成多层次和分支的结构,以实现特定的流程。 值得一提的是,从多层组合结构中往外跳可不是件容易的事,定需小心应对。 100 条可靠性 99%的语句顺次执行,结果可靠性只有三分之一。 5 函数 程序中使用的完成特定功能的一段代码的表示符。参数是传递给函数对其功能进行精细控制的变 量。返回值是用来表示函数执行情况的结果。函数的高内聚、低耦合是函数模块化的基本要求。函数的 目的是使代码可重用。 5.1 声明函数 命名规则参见变量命名规则。 不能在一个函数的内部声明另一个函数。 5.2 调用函数 当然可以在一个函数的内部调用另一个函数。 5.2.1 参数传递 值复制传递 基本变量 结构变量 地址复制传递 指针 数组 5.2.2 返回值传递 值复制传递 5.2.3 递归调用 在一个函数声明的内部,又直接或间接调用函数自身。 Hanoi 塔问题。 C 语言快速入门 一块板上有三根针,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 上。 其中第一步和第三步是类同的。 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'); } 5.3 常用函数 编译系统通常提供一套公共函数接口,以头文件和库文件的方式供用户使用,以减少重复开发通用 模块的工作量。这些通用函数通常从手册中查得其用法,直接使用即可。 5.3.1 主函数 main 是一个程序的初始函数,其参数有两个:int vi ,char *vc[ ]。vi 表示 main 的第一个参数,它 是一个整形变量,指出了程序从命令行接收的参数的个数;vc 表示每个参数。main 的返回值可以 是整数 int,也可以没有 void,视程序需要而定。 int main(int vi ,char *vc[ ]){}或 void main(int vi ,char *vc[ ]){} 或 C 语言快速入门 main(){} main 表示主程序的开始,每个基本程序必须的,参数和返回值见上。 5.3.2 基本输入输出函数 getche,printf,scanf char getche():等待键盘按键,并返回该键的编码值 printf(“格式化字符串”,参数): 屏幕显示函数,用指定格式显示指定参数 格式化字符串中可以包含: 转义字符:以\引导,主要有\n,\r,\b,\\,\% \n 换行 next line \r 回车 reture \b 退格 backspace \\显示\ \%显示% 格式占位符:以%引导,主要有%c,%s,%d,%f,%ld,%lf,%e %c 一个字符占位符 %s 一串字符串占位符 %d 一个整数占位符 %f 一个小数占位符 %e 一个科学记数占位符 固定字符:其它字符 举例: printf(“This is a test! \n\r%d 圆周率 Pi=%f”,i,3.14159); scanf(“格式化字符串”,参数) 键盘接收函数,用指定格式接收指定参数,请参考 printf 函数。 与 printf 函数相比,格式化字符串小有差别,使用时应注意。 5.3.3 字符串函数 字符串长度测试 strlen 字符串比较 strcmp 字符串拷贝 strcpy 字符串连接 strcat 字符串输出 puts 字符串输入 gets 大小写字母转换 strlwr/strupr 5.3.4 文件存取函数 文件系统与文件存取方式 缓冲/非缓冲方式文件存取 C 语言快速入门 打开文件open 关闭文件close 文件定位seek 读文件read,fscanf,fgetc 写文件write,fprintf,fputc 文件操作出错检测 ferror 5.3.5 数学函数 sin,cos,tan… 5.3.6 其他函数 库函数不是 C 语言的一部分,而是编译系统提供的公共函数。 我一直使用 BC3.1 for DOS 的帮助系统做我的库函数速查手册,非常好用,隆重推荐。 6 预处理 本章内容与标准 C 语言无关,通常是 C 编译系统提供的扩展功能,只为程序扩展、调试的方便而提供。 6.1 包含 将其他文件包含进当前文件,可节省大量重复劳动。 编译系统会将文件包含合并成一个文件编译,即相当于将其他源程序文件复制到当前源程序文件中。 例: #include “mylib.h” #include 前者从当前文件目录搜索被包含的文件,若找不到则继续搜索系统指定目录。 后者只搜索系统指定目录。 6.2 宏定义 用于同一字符串在程序中的统一更改,及数据符号化。 简单替换的宏 #define Pi 3.14159 编译前系统先替换(有时称为扩展)这些宏定义,然后才进入编译阶段。 即程序编译后期及运行阶段根本已经不存在这些定义了。 含参数替换的宏 #define S(r) Pi*(r)*(r) 编译前系统先替换(有时称为扩展)这些宏定义,然后将参数替换为宏调用处对应的实际参数,再进入 编译阶段。 注意: 不同编译系统对参数扩展的域处理不同,如 TC 不扩展宏定义中的字符串内的宏参数。 #define PRINT(V) printf(“V=%d”,V) 6.3 条件编译 用于编译或调试部分代码的场合。 C 语言快速入门 #if<def> 表达式(宏定义) …//程序段落 1 #else …//程序段落 2 #endif 如果表达式成立或宏定义存在,编译程序段落 1,否则编译程序段落 2。 #ifdef DEBUG printf(“X=%d,Y=%d”,x,y); #endif 编译系统的一些编译参数,有时也通过条件编译的方式传递给源程序,暗箱操作条件编译过程。 7 C 语言与计算机硬件 由于 C 语言优良的底层接口特性,使用它来进行硬件方面的操作和控制是相当好用的。 7.1 特定内存 在声明指针变量时初始化其地址为内存任意地址,实现操作内存数据的目的。 例: char far * video=( char far *)0xb8000000; 定义一个屏幕显示缓冲区标志符,对该变量赋值即可控制屏幕显示。 7.2 IO 接口 通过 inortp/outportp 操作任何计算机硬件端口,达到控制任意硬件设备的目的。 例: outportp(COM1,’A’); 由串行口输出字符 A。 8 程序结构与算法 空间-紧凑代码 时间-执行效率 查表法是通常函数映射中最快速的方法,但映射关系复杂时数据量巨大。 循环和递归是解决重复或近似重复问题的基本方法,结构明了,但其效率通常不高。 这是一对矛盾,程序结构和算法的设计就是要在矛盾中寻求一种可接受的平衡。 9 C 语言总结 9.1 特点 语法限制不太严格,程序设计自由度大。 C 语言允许直接访问物理地址。 能进行位(bit)操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作。 生成目标代码质量高,程序执行效率高。 C 语言快速入门 9.2 风格 好的编程风格在中、大规模程序中必不可少,如果想从小培养,那么林锐博士写的《高质量 C++编程指 南(top quality cpp)》是个很好的范例。 9.3 经验 我经常发现,新程序员力图编写复杂冗长的程序,老一点的程序员总尝试编写简单短小的程序。通过这 个现象,可以总结出几点规律: 经验是在练习的基础上逐渐培养的。 编程的重点在于思想。 想的多了,就会有更好的方法,做的更轻松。 10 例子 10.1 数据例 请定义一个整数,并赋初值为-1; int a; a=-1; 或 int a=-1; 解释:在内存中存放一个-1,并把该位置标记为 a;将来要使用-1 这个值,可以直接用符号化的位置 a 代替。该位置即-1 在内存中的地址,可以用&符号+变量名得到,即-1 在内存中的位置为&a; 在内存中存放一个-1,并把该位置标记为 a;将该位置存放到另一个变量 b 中,则:b=&a; a=*b=-1; 再解释: 假设 a=-1,&a=100; 那么 b=100,*b=-1; b 存放了一个地址&a,该地址处的数据类型是什么? int 如何定义 b 的类型? int * b; 数组: 将同种类型的多个变量用同一个名称来表示,以下标表示第几个变量。 请定义一个三个整数类型的数组; int a[3]; 请定义一个字符串,第三个字符为 H; char b[10]={‘’,’’,’H’}; 或 char *b=”HHH”; 解释:b 存放的是一个字符串”HHH”在内存中的位置,即指针,即地址。 请定义一个三个浮点数构成的数组; C 语言快速入门 float a[3]; 请定义一个三个字符串构成的数组; char a[3][]; 或 char *a[3]; a[0]为第一个字符串,如 a[0]=”abc”; a[1]为第二个字符串,如 a[1]=”123456789”; a[2]为第三个字符串,如 a[2]=””; 请定义一个三个指针构成的数组; char *a[3]; 或 int *a[3]; 或 float * a[3]; 10.2 函数例 自定义一个函数,检查给定字符串中空格的个数。 10.3 程序结构与算法例 给出一个学生成绩查询系统的数据输出部分结构框架。 10.4 综合编程例 一个基于链表结构的文本编辑器软件需要考虑的若干问题。 结束语 毫无疑问,这篇文章比我在学校时整理的内容要增加了许多,主要是在工程实践中又有许多感悟又 夹杂了进去。 本文作者不善于把一个简单问题复杂化,所以文章的篇幅较常规 C 语言学习类图书要小。 本文作者只是在学校学了点 C,看过 C 在 DOS 时代的辉煌。对于她的未来命运,不敢随意评价。 但语言本身差别是不大的。希望对编程困惑的朋友通过阅读本文能够认识到编程是一件简单的事。 由本文可以看出,C 语言的高效率和贴近底层的特点使她在单片机领域获得了很大发展,有兴趣的 朋友可以关注一下。网上搜索“KeilC”、“C51”大把。 如果读者认为本文有任何不妥处,敬请拍之。 DieHeart@eyou.com 这是我的家。 权利与声明 本文内容大多可从其它资料上找的到,但作者仍希望读者保持此文档的完整性,愿意与大家共同分 享读书的乐趣。 本书娣妹篇《从 C 到 C++》和本文档的结构非常类似,讲述的是学过 C 后如果要转向 C++要考虑 的一些问题,有兴趣的朋友可以参考。 C 语言快速入门 推荐书目 DieHeart,《从 C 到 C++》,一本与本书有同样风格的 C++学习辅导书。 林锐,《高质量 C++编程指南》,一本编程风格方面的很好规范。
还剩28页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

yhx0000

贡献于2013-01-06

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf