




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、,C语言程序设计,For 123高计网,上学期有点失败,C语言用于做什么?,学习高级程序设计的基础,C程序设计语言的地盘,硬件(Hardware),操作系统(OS),应用程序(Application),应用平台,基于平台的应用程序,低级语言的地盘,高级语言的地盘,C语言的地盘,C语言最擅长什么?,与硬件打交道 资源消耗少 效率要求高,现状?,关系 族谱图,专业中的作用?,网页网站设计,计算机二级C?,软件设计,难不难?,听说:除了难,还是难!,我们怎么学?,程序设计诀窍:,多看,多练,多动手,抓住原理,人的脑袋对数据的处理对比程序对数据的处理!,一点建议,自己多看书!,考核方法,What is
2、 a computer?,目前的PC机可每秒执行十亿次加法 人用计算器一生也完不成PC机1秒钟完成的计算量,What is a computer?,硬件(hardware) 键盘、鼠标、显示器、硬盘、DVDs、内存、CPU 硬件成本下降很快 摩尔定律 每过18个月微芯片的集成度翻一番,而芯片价格保持不变 软件(software) 运行于计算机之上的程序(Program) 如何降低软件开发成本?,计算机如何工作,很像厨师的菜谱,定义了执行某个任务所需的步骤 但不幸和不同的是,你不能使用你自己的母语来写Code,What is programming language?,C程序设计语言的地盘,以往
3、的擅长: 与操作系统和基础工具有关的编程任务 操作系统核心,设备驱动程序,系统工具,网络应用,编辑器,字处理工具,编译器,某些图形和GUI应用,以及数据库应用 未来的发展: 高性能、实时中间件,嵌入式领域,并发程序设计等,机器语言,计算1+1,10111000 00000001 00000000 00000101 00000001 00000000,汇编语言,BASIC语言,PRINT 1+1,C语言,#include main() printf(%dn, 1+1); ,C语言的创世纪,一切从一个叫“Space Travel”的电子游戏开始 为了让他的游戏能在PDP-7上运行,Ken Thom
4、pson用汇编语言给PDP-7写了一个操作系统UNIX 汇编太不好用了,Thompson需要高级语言 试验了一些高级语言,包括Fortran,都不理想 他在BCPL基础上,自己设计了一个B语言 UNIX开始发展,B也不够用了 Dennis Ritchie加入,把B改造成C 开始用C重写UNIX,Ritchie和Thompson在开发UNIX,接受美国国家技术勋章,C程序设计语言,是一种高级语言 高级语言并不是“高级”,只是相对低级语言,在一个高的级别上进行编程 历史悠久,战勋卓著 诞生于上世纪70年代初,成熟于80年代(C89),修订与90年代(C99) 很多重量级软件都是用C写的 上天入地,
5、无所不能 几乎没有不能用C写出来的软件,没有不支持C的系统 很多流行语言、新生语言都借鉴了它的思想、语法 从C+,到Java,再到C#,还有php等,C语言的祖师爷Dennis M. Ritchie,http:/www.cs.bell-,Ritchie漫画像,C is quirky, flawed, and an enormous success.,计算机基本工作过程,整个过程的执行者是硬件,但硬件是受软件控制的 编程,就是编写软件,使硬件按照人的意图工作,编译运行,编译过程 程序员(Programmer)编写程序源代码(Source Code) 编译器(Compiler)把源代码转换为可被计
6、算机理解的机器代码(Machine Code),并把机器代码以可执行文件(Executable File)的形式保存在磁盘上 软件的运行 计算机把机器代码读入到内存(Memory),由CPU运行这些代码,读取输入(Input),产生输出(Output),完成程序员预定的功能,编译语言,一种编译语言对应一种编译器 程序员按照该语言的语法编写程序源代码,把自己的意图融入到代码中 编译器读入源代码,把程序员的意图转换成可执行程序,供他人使用,C语言,可执行程序,编译器,解释运行,解释运行过程 程序员编写程序源代码 解释器读入源代码,并执行源代码 解释运行的语言特点 执行速度慢 好学易用 先编译、后解
7、释 把源代码编译成更容易解释的中间代码,然后再解释运行,C程序设计语言,第1章 观其大略,简单数学题目求解步骤,题目:,从学校到石阡路费多少钱?, 设变量,x为学校到汽车站车费,y为汽车站到石阡车费, 设变量,z为全部车费, 分析题目,到石阡路线怎么走(学校-汽车站-石阡), 得出到石阡车费式子(表达式):,简单程序求解步骤,题目:,从学校到石阡路费多少钱?, 设变量,x为学校到汽车站车费,y为汽车站到石阡车费, 设变量,z为全部车费, 分析题目,到石阡路线怎么走(学校-汽车站-石阡), 得出到石阡车费式子(表达式):, 编写程序,实现功能!,简单程序实现方式1,void main()/主函数
8、(程序入口) int x,y,z;/设变量 x=10;/变量赋值 y=40; z=x+y;/得出结果50元 x=8;/变量赋值 y=40; z=x+y;/得出结果48元 ,有什么缺点?,简单程序实现方式1,void main()/主函数(程序入口) int x,y,z;/设变量 z=huijia(10,40); z=huijia(8,40); ,int huijia(int a,int b)/求路费函数 int z;/设变量 z=a+b; return z; ,实现一个功能,解答数学与编程之相同,设变量作为存储数据 如:x,y,z 表达式 如:z=x+y;z=x*y; 函数 实现某一功能 Y=
9、f(x);,解答数学与编程之不同,变量存储空间 如:x,y,z不管空间,因在脑子里 变量有类型 如:x因在脑子里,你想它放什么就放什么 但电脑是具体存放地方必须有类型,如口袋 函数 是将多个计算式子包括起来,数学仅仅是一个表达式如:抛物线函数,C语言编程几个关键概念,变量 用于存放数据 表达式 如:z=x+y; 如:xy 函数 多个表达式构成的一个功能模块,C语言程序结构,下课后去食堂吃饭,下课拿好书等,下楼走到食堂,指定饭菜刷卡,吃饭,拿饭菜,开始,结束,C语言程序运行,int x,y,z;,Void main(),入口,Int z; z=a+b;,int huijia(int a,int
10、b),(10,40),50,z = 50;,(8,40),z = 48;,48,返回,Hello, World,#include main() printf(hello, worldn); 超级无敌考考你:如何把“hello”和“world” 分别打印在两行?,hello.c,打印华氏温度与摄氏温度对照表,计算公式:C=(5/9)(F-32),打印华氏温度与摄氏温度对照表,#include /* 对 fahr = 0, 20, ., 300 打印华氏温度与摄氏温度对照表 */ main() int fahr, celsius; int lower, upper, step; lower = 0
11、; /* 温度表的下限 */ upper = 300; /* 温度表的上限 */ step = 20; /* 步长 */ fahr = lower; while (fahr = upper) celsius = 5 * (fahr-32) / 9; printf(%dt%dn, fahr, celsius); fahr = fahr + step; ,fc1.c,代码风格,#include /* 对 fahr = 0, 20, ., 300 打印华氏温度与摄氏温度对照表 */ main() int fahr, celsius; int lower, upper, step; lower = 0
12、; /* 温度表的下限 */ upper = 300; /* 温度表的上限 */ step = 20; /* 步长 */ fahr = lower; while (fahr = upper) celsius = 5 * (fahr-32) / 9; printf(%dt%dn, fahr, celsius); fahr = fahr + step; ,fc1.c,更简单、精确的对照表打印程序,#include #define LOWER 0 /* 表的下限 */ #define UPPER 300 /* 表的上限 */ #define STEP 20 /* 步长 */ /* 打印华氏-摄氏温度
13、对照表 */ main ( ) int fahr; for ( fahr = LOWER; fahr = UPPER; fahr = fahr + STEP ) printf ( %3d # %6.1fn, fahr, (5.0 / 9.0) * (fahr - 32 ) ); ,fc2.c,字符输入输出,c = getchar() 从键盘读入一个字符,赋值给变量c putchar(c) 把c输出到屏幕 拷贝的基本思想:读一个字符while ( 该字符不是文件结束指示符 )输出刚读进的字符读下一个字符,拷贝(Copy),#include /* 用于将输入复制到输出的程序;第1个版本 */ ma
14、in ( ) int c; c = getchar ( ); while ( c != EOF ) putchar ( c ); c = getchar ( ); ,copy1.c,一个更好的版本,#include /* 用于将输入复制到输出的程序;第2个版本 */ main ( ) int c; while ( (c = getchar ( ) ) != EOF ) putchar ( c ); ,copy2.c,计算行数,#include /* 统计输入的行数 */ main ( ) int c; long nl; nl = 0; while ( (c = getchar ( ) ) !=
15、 EOF ) if ( c = n ) nl+; printf(%dn, nl); ,counter.c,加法器,#include /* 计算输入的两个整数的和 */ main ( ) int a, b; printf(Please input two integers:); scanf(%d%d, ,add.c,平均分,#include /* 计算某科成绩的平均值 */ #define TOTAL_NUMBER 10 /* 总人数 */ main ( ) float sum=0, scoreTOTAL_NUMBER; int i; printf(Input %d scores:n, TOTA
16、L_NUMBER); for (i=0; iTOTAL_NUMBER; i+) scanf(%f, ,average.c,函数(Function),前面使用了系统提供的函数:printf,scanf,getchar,putchar 使用函数时,我们不用知道这个函数内部是如何运作的,只按照我们的需要和它的参数形式调用它即可 我们也可以定义自己的函数 “一个程序应该是轻灵自由的,它的函数就象串在一根线上的珍珠。”(编程之道),power函数,/* power: 求底的n次幂; n =0 */ int power(int base, int n) int i, p; p = 1; for ( i =
17、 1; i = n; +i ) p = p * base; return p; ,power.c,power函数的调用(Call),#include int power(int base, int n); /* 测试power函数 */ main ( ) int m, n; m = power(2, 1); n = power(-3, 3); printf(%d %dn, m, n); return 0; ,power.c,这一章我们学到了,#include #define main() printf(), scanf() getchar(), putchar() =, =, != int,
18、long, float 数组 while, for, if 代码风格 注释、缩进、空行、命名 函数,C程序设计语言,第2章 类型、运算符与表达式,标识符(Identifiers),用户自定义的符号叫标识符 如变量名、函数名、宏和类型名 标识符由字母、数字和下划线组成,大小写敏感 不可以是数字开头 标识符要直观,能表达它的功能 下划线和大小写通常用来增强可读性 variablename variable_name, VARIABLE_NAME VariableName, variableName 关键字(keyword)不可作为标识符 int, float, for, while, if等(教材
19、164页) 某些功能的变量采用习惯命名 如:for语句所采用的循环变量习惯用i, j, k,基本数据类型(Data Type),int 整数,在目前绝大多数机器上占4个字节 TC2中是2个字节 所占字节数取决于机器字长 float 单精度浮点数,一般是4个字节长 double 双精度浮点数,一般是8个字节长 char 字符,一般是1个字节长 用来表示256个ASCII字符,或者0255的整数,数据类型修饰符,short short int,短整数,一般2个字节长。通常简写为short long long int,长整数,一般是4个字节长。通常简写为long long double,高精度浮点数
20、,一般是10个字节长。 signed 用来修饰char、int、short和long,说明他们是有符号的整数(正整数、0和负整数)。一般缺省都是有符号的,所以这个修饰符通常省略 unsigned 用来修饰char、int、short和long,说明他们是无符号的整数(正整数和0),超出取值范围会怎样?,TC2中int的范围是-3276732767 如果我们给它一个小于-32767或者大于32767的数会如何呢? 现场编程测验,小蛇能吞下大象吗?,溢出(Overflow)造成的危害,一台安装了Windows 95/98的机器,如果连续运行49.7天没有重新启动,可能死机 原因: Windows自
21、启动时刻起,有一个计数器,记录系统已经运行了多少毫秒。这个计数器是个unsigned long 类型的变量 unsigned long的最大值是:4294967295 一天有 24*60*60*1000 = 86400000毫秒 4294967295 / 86400000 = 49.71026961805 当49.7天的时候,此计数器会溢出,引起死机,浮点数的陷阱,#include main() float f; f = 123.456; if (f = 123.456) printf(f is equal to 123.456 indeed.); else printf(In fact, f
22、 is equal to %fn, f); 运行结果会是什么?,float.c,浮点数的陷阱,float的精度低,较易发生精度带来的相等性判断问题 double精度高,这个问题发生的概率小一些,但也存在 解决办法:if (fabs(f 123.456) 1E-5),根据精度要求设定,使用变量要注意,不要对变量所占的字节数想当然 用sizeof获得变量或者数据类型的长度 用ANSI C定义的宏确定数据的表示范围,解决溢出问题,sizeof.c,常数(Constant),整型常数 123、456 123456 123l、123L、123456l、123456L 浮点常数 123.45、456.78
23、 1e-2、4.5e3 123.45f、 456.78F、1e-2f、4.5e3F 123.45l、 456.78L、1e-2l、4.5e3L,八进制与十六进制常数,以数字“0”开始的整型常数是八进制数 010和10大小不一样 因为八进制并不常用,所以此种表示法比较少见,因而常被用错 以“0 x”或者“0X”开始的整型常数是十六进制 AF和af用来表示十进制的1015 0 x11, 0 x05, 0 xFA, 0 xFF 十六进制的形式比较常用,尤其在进行位一级的控制的时候,字符常数,字符常数的表示方法 a,A,5,%,$ 单引号内只能有一个字符,除非用“”开头 就是一个普通整数,也可以参与各
24、种数学运算 每个字符具有一个0255之间的数值,可从ASCII表查出 注意:5和5的区别,A和A的区别 字符的数学运算在密码学内用得比较多,ascii.c,字符常数,转义字符 一些特殊字符(无法从键盘输入或者在C语言里有它用)用转义字符表示 转义的思想在网络协议和文件格式中经常使用,字符串(String)常数,用双引号括住的由0个或多个字符组成的字符序列 I am a string 表示空字符串 转义字符也可以在字符串中使用 引号只作为字符串开始和结束的标志 C语言内部用0表示字符串的结束 除注释外,是唯一可以出现中文的地方 x和x是不同的 里定义了一系列专门的字符串处理函数,枚举(Enume
25、ration)常数,一个几乎被遗忘的角色 从程序来窥其一斑 enum weeks MON, TUE, WED, THU, FRI, SAT, SUN ; enum weeks today, tomorrow; today = MON; tomorrow = today + 1; if (tomorrow = TUE) printf(Tomorrow is Tuesday.n); else printf(Tomorrow is NOT Tuesday.n);,enum.c,变量声明,变量必须“先定义,后使用” 所有变量必须在第一条可执行语句前定义 声明的顺序无关紧要 一条声明语句可声明若干个同类
26、型的变量,变量名之间用逗号分隔 变量定义后,即占用内存,可向其存入各种数据,并可通过变量名使用数据 声明变量,是初始化变量的最好时机 不被初始化的变量,其值为危险的随机数 char esc = ; int i = 0; int limit = MAXLINE + 1; float eps = 1.0e-5;,常量,用const修饰定义的变量为常量 const int i=0; 常量只能在定义时赋值,然后不能再改变其值 常数、常量、宏和枚举,都可以用来表示一个永远不会改变的数 前者不建议直接使用,而用后三者代替 后三者的工作机理是完全不同的,达到的效果也不尽相同,计算机只会计算 任何事物都要被表
27、示成数字和公式的形式后,才能被计算机计算(被计算机处理) 事物到数字和公式的转换过程叫数学建模 因为:事物在计算机内的处理都是一种计算 又因为:计算就要有操作数、运算法则和计算结果 所以:事物在计算机内的处理都有操作数、运算法则和计算结果 计算结果你可以留用,也可以忽略,算术运算符,+,-,*,/ 加、减、乘、除运算 四则混合运算中,先算乘除,后算加减,先算左,后算右 % 求余运算,C语言中的运算,关系运算符,,=,=,=,!= 大于,大于等于,小于,小于等于,等于,不等于 关系运算符运算出的结果为0和1 0,表示假,即该关系不成立 1,表示真,即该关系成立 在所有涉及到真假判断的地方,0表示
28、假,非0表示真,找别扭,int a=1;if (a = 0)printf(OK); int a=0;if (a = 0)printf(OK);,int a=1;if (a = 0)printf(OK); int a=0;if (a = 0)printf(OK);,= 和 =,int a;a = 0;a = 1;,int a;a = 0;a = 1;,一定要分清=和= 下面用法能起点小作用:,int a=0;if (0 = a)printf(OK);,int a=0;if (0 = a)printf(OK);,编译出错,逻辑运算符,逻辑运算也被称为布尔(Boolean)运算,运算结果也是1和0
29、下面两个语句是等价的 i = i + 2; i += 2; +、-、*、/、%、else z = b; z = (a b) ? a : b; 此种表达式切忌用得过于繁杂,优先级,( ) - . ! + - + - * else语句块2;语句块3 表达式值非0时,执行语句块1,然后语句块3;表达式值为0时,执行语句块2,然后语句块3 else部分可以没有。当表达式值为0时,直接执行语句3 if-else嵌套使用时,注意else和谁配套的问题,if.c,else-if,if的一种扩展 if (表达式1) 语句块1;else if (表达式2)语句块2;else if (表达式3)语句块3;else
30、语句块4;语句块5; else部分可以没有,switch,多路选择 switch (表达式) case 整型常数1:语句1;case 整型常数2:语句2;default:语句3; default可以没有 现场编程完成计算器 不要忘记break,switch和else-if的比较,else-if比switch的条件控制更强大一些 else-if可以依照各种逻辑运算的结果进行流程控制 switch只能进行=判断,并且只能是整数判断 switch比else-if更清晰 两者都要尽量避免用得过多、过长,尤其不要嵌套得太多 它们大大增加程序的分支,使逻辑关系显得混乱,不易维护,易出错,循环while,f
31、or,while (表达式)语句块; for (表达式1; 表达式2; 表达式3)语句块;,while,while (表达式)语句块1;语句块2; 只要表达式的值为非0,就重复执行语句块1,直到表达式值为0时止,开始执行语句块2,for,for (表达式1; 表达式2; 表达式3)语句块; 首先执行表达式1。如果表达式2的值为非0,就重复执行语句块和表达式3,直到表达式2的值为0时止 相当于:表达式1; while (表达式2)语句块;表达式3; for的所有表达式均可省略,注意,在for和while语句之后一般没有分号 有分号表示循环体就是分号之前的内容,即循环体不存在 while (i 1
32、00);i+; for (i = 0; i 100; i+);printf(%d, i); for通常有一个循环变量控制循环的次数,不要在循环体内改变这个变量,循环do-while,do语句块1;while (表达式);语句块2; 首先执行语句,然后判断表达式的值。如果表达式为0,继续向下执行,否则,再次执行语句,再次判断表达式的值 语句块1会被执行至少一次,选择三种循环的一般思路,如果循环次数已知,用for 如果循环次数未知,用while 如果循环体至少要执行一次,用do-while 只是思路,不是定律,break和continue,对for、while、do-while循环进行内部手术 b
33、reak,退出循环 continue,中断此次循环的执行,开始下一次 break和continue少用为妙 它们增加了循环执行的分支,break更增加了循环的出口 它们可以用来处理程序异常,而尽量不要用来处理正常流程,C程序设计语言,第4章 函数与程序结构,函数(function)和模块(module),函数是C语言中模块化编程的最小单位 可以把每个函数看作一个模块 若干相关的函数可以合并作一个“模块”,函数的分类,函数生来都是平等的,没有高低贵贱之分,只有main()稍微特殊一点点 库函数 ANSI C定义的标准库函数 符合标准的C语言编译器必须提供这些函数 函数的行为也要符合ANSI C的
34、定义 第三方库函数 由其它厂商自行开发的C语言函数库 不在标准范围内,能扩充C语言的功能 自定义函数 自己编写的函数 包装后,也可成为函数库,供别人使用,函数定义(definition),类型 函数名(类型 参数1, 类型 参数2, )函数体;return 表达式;,返回值类型,标识符,参数表,返回值,函数出口,函数定义(definition),函数是这样的一种运算: 函数名说明运算规则 参数是运算的操作数 返回值是运算的结果 当函数执行到return语句或时,函数的运算停止。程序从当次调用函数的地方继续执行 函数可以有多个return,但最好只有一个且是最后一行 用void定义返回值类型 函
35、数没有运算结果,没有返回值 return语句之后不需要任何表达式 用void定义参数,表示没有参数 参数表里的参数(叫形式参数,parameter)也是函数的语句块内的变量,函数调用(call),函数名(表达式1, 表达式2, ); 调用一个函数之前,先要对其返回值类型、函数名和参数进行声明(declare) 不对函数进行声明是非常危险的 函数定义也有声明函数的效果 调用函数时,提供的表达式(叫实际参数, argument)和该函数的形式参数必须匹配 数目一致 类型一一对应(会发生自动类型转换) 表达式的值赋值给对应的参数 返回值可以按需处理,realeql.c,函数调用的过程,函数的每次执行
36、都会建立一个全新的独立的环境 在“栈”中为函数的每个变量(包括形式参数)分配内存 把实际参数的值复制给形式参数 开始执行函数内的第一条语句 函数内的代码在这个独立的环境内工作 函数退出时 求出返回值,将其存入一个可以被调用者访问的地方(x86中通常使用EAX寄存器) 收回分配给所有变量(包括形式参数)的内存 程序控制权交给调用者,调用者拿到返回值,将其作为函数调用表达式的结果,main()、printf()和scanf()特 殊 吗?,main() C语言允许不对函数参数和返回值类型进行说明 甚至可以连函数名都不声明 此时默认 该函数的参数是不定个数的int型 该函数返回值为int型 永远不要
37、利用此特性! printf()、scanf() 变长参数表, 缺点:对参数类型和个数无法严格验证,易使用出错,使用函数要注意,每个函数只完成一个功能(包括main()) 对函数的功能可以用不含连词的一句话描述 函数不能过长 1986年IBM在OS/360的研究结果:大多数有错误的函数都大于500行 1991年对148,000行代码的研究表明:小于143行的函数比更长的函数更容易维护 函数一定要对传进来的非法参数做点什么 向调用者提供错误信息 assert(),safediv.c,全局变量(Global Variable),在所有函数之外定义的变量是全局变量,在定义它的位置以后都有效 全局变量自
38、动初始化为0 全局变量使函数之间的数据交换更容易,效率也高一些 但是不推荐使用,甚至禁止使用 程序的任何部分都可以改写全局变量,很难确定在程序的哪里改写了它,程序结构混乱 不得不用的时候(这种情况比较少见),要严格控制对它的改写,静态变量(static),函数的内部变量在函数退出后失效(内存释放)。再次进入函数,变量重新定义 每次函数执行都建立一个全新的执行环境,不受其它函数的干扰 把此变量定义为static,则变量的值可以保存到下次进入函数 static int i; 静态变量自动初始化为0,static.c,递归(Recursion),函数直接或间接调用自己为递归 unsigned int
39、 func(unsigned int n) if (n = 0) return 1;else return n * func(n-1); ,recur.c,模块,模块包含两部分 源文件(xxx.c):一系列相关函数的定义 头文件(xxx.h):这些函数的声明等必要信息 函数声明、外部变量声明、宏定义、类型定义 可以将模块编译为.obj文件,同.h文件一起供别人使用,从而保护了源代码 使用模块的过程 建立一个工程(project) 把各模块都加入到工程中 #include模块的头文件 开始使用此模块,编写模块的技术,模块的信息隐藏 用static定义的函数和全局变量只在此模块内有效(建议采用)
40、允许被其它模块使用的全局变量 在源文件中定义,不加static修饰 在头文件中进行声明,加extern修饰,预编译指令,编译器在开始正式编译之前处理的指令,叫预编译指令 它们不会存在于最后生成的目标代码中 文件包含:#include 用#include指定的文件内容替换#include所在的行 用或者括上文件名 表示在编译器的include目录内查找文件 表示在当前目录查找文件 文件名中可以带有路径,#define,#define 宏名字 替换文本 在#define之后,所有独立出现“宏名字”的地方(除了字符串内)都被“替换文本”替换 “替换文本”中可以有空格 宏可以有参数 #define m
41、ax(A,B) (A) (B) ? (A) : (B) 能想出带参数的宏和函数的区别吗? 定义宏的时候注意替换发生后产生的非预想结果 一般用括号可以避免,如上例,宏名中间不要有空格,与#define配套者,#undef,从现在开始取消#define的定义 #undef MAXLINE #if, #else,#elif,#endif #ifdef,#ifndef 这些预编译指令通常用来处理多文件工程和程序多版本的问题。(程序多版本一般是不同平台的版本,不同用户等级的版本,不同开发阶段的版本等),使用预编译指令的目的,增强程序可读性 但是调错时宏可能带来很多难题 精简源代码,提取变化 这一点更多时
42、候用函数的效果更好,但宏也有其不可替代的优势 不编译无用代码,精炼目标代码,C程序设计语言,第5章 指针与数组,计算机内的存储部件,活动中的所有指令和数据都保存在内存内 速度快,但是掉电即失 可以随机访问 只要指名要访问的内存单元的地址,就可以立即访问到该单元 地址是一个无符号整数(通常用16进制数),其字长与主机相同 内存中的每个字节都有唯一的一个地址,内存(Random Access Memory)地址(Address),指针的故事,“该程序执行了非法操作,即将关闭” 这种错误几乎全是由指针和数组导致的 黑客攻击服务器利用的bug绝大部分都是指针和数组造成的 有些非计算机专业的人,尽量避免
43、使用指针,指针的故事,铁杆C/C+程序员最挚爱的武器:指针 指针造就了C/C+的高效和强大 很多不可能的任务由指针完成 main()char*a=main()char*a=%c%s%c;printf(a,34,a,34);printf(a,34,a,34);,关于指针的原则,学习原则 一定要学会 其实通常的应用很简单 就是一个变量 复杂的应用也不建议使用 使用原则 永远要清楚每个指针指向了哪里 永远要清楚指针指向的位置是什么,数组(Array),若干类型相同的相关数据凑到一起,就是数组 定义 类型 数组名整型常数1整型常数2 整型常数n; int a64; 使用 a00、a12、a53 每个元
44、素都是一个普通变量 下标可以是任意整型表达式 数组的各个元素在内存中分布在一起,分布规律是array.c 思考一下一维和三维数组怎么分布呢?,从类型的角度理解数组,int a10; 定义了一个有10个int类型元素的数组 a的类型可以看作int10(只是看作,语法并不允许这么定义: int10 a) int a2010; 定义了一个有20个int10类型元素数组 a0、 a1a9的类型是int10,所以a00、 a01a199的类型是int int a302010; 这个呢? 这种特性决定了数组元素在内存的分布规律,也解释了数组的很多语法现象,数组初始化,数组定义后的初值仍然是随机数,一般需要
45、我们来初始化 int a5 = 12, 34, 56 ,78 ,9 ; int a5 = 0 ; int a = 11, 22, 33, 44, 55 ; 数组大小最好用宏来定义,以适应未来可能的变化 #define SIZE 10int aSIZE;,数组的使用,数组的下标都是从0开始 对数组每个元素的使用与普通变量无异 可以用任意表达式作为下标,动态决定访问哪个元素 for (i=0; iSIZE; i+) ai = 2 * i; 下标越界是大忌! 使用大于最大下标的下标,将访问数组以外的空间。那里的数据不是我们所想定的情况,可能带来严重后果 有时,故意越界访问数组会起到特别效果,但一定要
46、对自己在做什么了如指掌 sizeof可以用来获得数组所占字节数 sizeof(a) sizeof(a0),数组的用处与特点,保存大量同类型的相关数据 快速地随机访问 一旦定义,不能再改变大小 在编译阶段就确定了数组的大小 数组名几乎就是一个指针,指针(Pointer),int *p; 定义了一个指针变量p,简称指针p p是变量,int*是类型 变量都占用内存空间,p的大小是sizeof(int*) p用来保存地址。此时这个地址是哪呢(p指向哪呢)? int i;p = ,指针,指针也是数据类型。指向不同数据类型的指针,分别为不同的数据类型 int*、float*、char*、int*、int*
47、 指针指向非其定义时声明的数据类型,将引起warning void*类型的指针可以指向任意类型的变量 指针在初始化时一般int *p=NULL; NULL表示空指针,即无效指针 但它只是逻辑上无效,并不是真正地无效 如果指针指向一个非你控制的内存空间,并对该空间进行访问,将可能造成危险,p = ,指针与数组,数组名可以看作一个指针 只是不能修改这个指针的指向 常指针 int a10; a的类型是int10 a的类型也是int* 指针可当作数组名使用,反之亦然 int *p, a10;p = a;p1 = 0;*a = 0;,指针运算,int* p=NULL;p+;/* p的值会是多少? */
48、指针的加减运算是以其指向的类型的字长为单位的 int *p, a10;p = a; *(p+3) 等价于 a3 p+; *p 等价于 a1,指针运算,int *p, *q, a10;p = a;q = 运算法则 只能进行加减和关系运算 只能同类型指针之间或指针与整数之间运算,“类型”本不存在,存储器在保存数据时并不关心数据的类型 完全以二进制方式工作 我们向计算机发出的指令说明了某块内存里数据的类型 一块内存内保存着(61 62 63 64)16 以char类型看待每个字节: abcd 以float类型看待每个字节: 16777999408082104000000.000000 以int类型看
49、待每个字节: 1684234849,依天屠龙,强强联手,int main(void) int a = 0, 1, 2, 3, 4, 5, 6, 7, 8; int i; unsigned char *p; p = (unsigned char*)a; /* 类型强转了 */ for (i=0; isizeof(a); i+) PrintHexChar(pi); /* 把指针当数组用 */ putchar( ); 指针强转后,可以把一块内存当作另一种类型来处理 强强联手,我们可以随意控制任意内存,god.c,指针与函数,指针既然是数据类型,自然可以做函数的参数和返回值的类型 指针做参数的经典例子
50、: main() int x, y; swap(x, y); void swap(int x, int y) int temp; temp = x; x = y; y = temp;,Not Work,指针做参数,main() int x, y; swap(,这里的函数调用过程还是“实参” 的内容复制到“形参” ,千万不要理解成什么“传引用调用”,指针做返回值,printf(%s, GetInput();.char* GetInput(void)char str100;scanf(%s, str);return str; char string30;printf(%s, GetInput(st
51、ring); . char* GetInput(char* str)scanf(%s, str);return str;,三个月使用scanf目睹之怪现状,int i;scanf(%d, i); /* 这样会如何?*/ int i;scanf(%f, /* 这样呢?*/,i的值被当作地址。例如,i的值如果是100,那么输入的整数就会从地址100开始写入内存,输入被当作float,以float的二进制形式写到i所在的内存空间,输入以int的二进制形式写到c所在的内存空间。c所占内存不足以放下一个int,其后的空间也被覆盖,数组做参数,和指针一回事儿 void ProcessArray(int*
52、a). void ProcessArray(int a).,这里给定元素个数有意义吗?,动态分配内存,在 和中均定义了下面的函数 void* malloc(size_t size); size_t是在中定义的数据类型,就是一个unsigned int 向系统申请大小为size的内存块,把指向首地址的指针返回。如果申请不成功,返回NULL void free(void* block); 释放由malloc()申请的内存块。block是指向此块的指针 malloc申请的内存,在被free之前,程序的任何部分都可以使用 当然,要使用必须得到指向它的指针,动态分配内存,如果malloc()申请的内存不
53、被free()程序就退出,将产生内存泄露(Memory Leak) “内存泄露”一词类似“原料泄露”。泄露出去的原料不能被利用,导致生产过程中原料不足 malloc()时,系统找到一块未占用的内存,将其标记为已占用,然后把地址返回,表明此程序占用此块内存,其它程序不能再用它 free()时,系统标记此块内存为未占用,本程序不能继续使用,所有程序可以申请使用 如果malloc()之后不free(),此块内存将永远不会被任何程序使用,就好像这块内存泄露出去一样,防止内存泄露之道,在需要的时候才malloc,并尽量减少malloc的次数 能用自动变量解决的问题,就不要用malloc来解决 mallo
54、c一般在大块内存分配和动态内存分配时使用 malloc本身的执行效率就不高,所以过多的malloc会使程序性能下降 可以重复利用malloc申请到的内存 尽量让malloc和与之配套的free在一个函数内 尽量把malloc集中在函数的入口处,free集中在函数的出口处 以上做法只能尽量降低产生泄露的概率。完全杜绝内存泄露,关键要靠程序员的细心与责任感,字符串(String)与字符数组、字符指针,字符串 一串以0结尾的字符在C语言中被看作字符串 用双引号括起的一串字符是字符串常量,C语言自动为其添加0终结符 Hello world! 把字符串常量作为表达式直接使用,得到的值是该常量的地址 C语
55、言并没有为字符串提供任何专门的表示法,完全使用字符数组和字符指针来处理 字符数组 每个元素都是字符类型的数组 char string100; 字符指针 指向字符类型的指针 char* p; 数组和指针可以等同看待,上面三者本质上是一回事,字符串处理函数,在中定义了若干专门的字符串处理函数 strcpy: string copy char *strcpy(char *dest, const char *src); strlen: string length size_t strlen(const char *s); strcat: string combination char *strcat(char *dest, const char *src); strcmp: string comparison int strcmp(const char *s1, const char *s2); stricmp: string comparison ignoring case int stricmp(const char *s1, const char *s2);,指针、数组以及其它的类型混合,基本数据类型 int、long、char、short、float、double 指针是一种数据类
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 光伏电站运行管理目标
- 光伏电站安全管理与防护措施
- 出纳人员试用期工作总结(资料13篇)
- 数字货币与2025货币政策传导的金融创新与挑战研究报告
- 中药学考试题库及答案
- 安全考试题库及答案
- 学生常见病预防知识普及措施
- 心理咨询助力辍学学生劝返有效措施
- 消防义务队员培训及职责
- 文化创意产业发展规划项目方案计划
- XX新任校长与学校班子成员见面会上的讲话
- 芬太尼贴剂的护理
- 村集体资金入股合同范本
- 要素式民事起诉状(房屋租赁合同纠纷)
- 高级卷烟商品营销员理论知识
- 2025年中级消防设施操作员理论知识考试真题(后附专业答案和解析)
- 老年病瞻望的护理
- 2025南京市劳动合同模板
- 异常子宫出血护理措施
- 基于项目制教学的未来课堂构建探讨
- 五升六数学暑假作业每日一练打印练习
评论
0/150
提交评论