语言的数据结构.ppt

上传人:本田雅阁 文档编号:3307001 上传时间:2019-08-10 格式:PPT 页数:139 大小:2.84MB
返回 下载 相关 举报
语言的数据结构.ppt_第1页
第1页 / 共139页
语言的数据结构.ppt_第2页
第2页 / 共139页
语言的数据结构.ppt_第3页
第3页 / 共139页
语言的数据结构.ppt_第4页
第4页 / 共139页
语言的数据结构.ppt_第5页
第5页 / 共139页
点击查看更多>>
资源描述

《语言的数据结构.ppt》由会员分享,可在线阅读,更多相关《语言的数据结构.ppt(139页珍藏版)》请在三一文库上搜索。

1、C程序设计语言,机器语言,计算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 Thompson用汇编语言给PDP-7写了一个操作系统UNIX 汇编太不好用了,Thompson需要高级语言 试验了一些高级语言,包括Fortran,都不理想 他在BCPL基础上,自己设计了一个B语言 UNIX

2、开始发展,B也不够用了 Dennis Ritchie加入,把B改造成C 开始用C重写UNIX,Ritchie和Thompson在开发UNIX,接受美国国家技术勋章,C程序设计语言,是一种高级语言 高级语言并不是“高级”,只是相对低级语言,在一个高的级别上进行编程 历史悠久,战勋卓著 诞生于上世纪70年代初,成熟于80年代(C89),修订与90年代(C99) 很多重量级软件都是用C写的 上天入地,无所不能 几乎没有不能用C写出来的软件,没有不支持C的系统 很多流行语言、新生语言都借鉴了它的思想、语法 从C+,到Java,再到C#,还有php等,C语言的祖师爷 Dennis M. Ritchie,

3、http:/www.cs.bell- is quirky, flawed, and an enormous success.,计算机基本工作过程,整个过程的执行者是硬件,但硬件是受软件控制的 编程,就是编写软件,使硬件按照人的意图工作,编译运行,编译过程 程序员(Programmer)编写程序源代码(Source Code) 编译器(Compiler)把源代码转换为可被计算机理解的机器代码(Machine Code),并把机器代码以可执行文件(Executable File)的形式保存在磁盘上 软件的运行 计算机把机器代码读入到内存(Memory),由CPU运行这些代码,读取输入(Input)

4、,产生输出(Output),完成程序员预定的功能,编译语言,一种编译语言对应一种编译器 程序员按照该语言的语法编写程序源代码,把自己的意图融入到代码中 编译器读入源代码,把程序员的意图转换成可执行程序,供他人使用,C语言,可执行程序,编译器,解释运行,解释运行过程 程序员编写程序源代码 解释器读入源代码,并执行源代码 解释运行的语言特点 执行速度慢 好学易用 先编译、后解释 把源代码编译成更容易解释的中间代码,然后再解释运行,C程序设计语言,第1章 观其大略,Hello, World,#include main() printf(“hello, worldn“); 超级无敌考考你: 如何把“h

5、ello”和“world” 分别打印在两行?,hello.c,打印华氏温度与摄氏温度对照表,计算公式: C=(5/9)(F-32),打印华氏温度与摄氏温度对照表,#include /* 对 fahr = 0, 20, ., 300 打印华氏温度与摄氏温度对照表 */ main() int fahr, celsius; int lower, upper, step; lower = 0; /* 温度表的下限 */ upper = 300; /* 温度表的上限 */ step = 20; /* 步长 */ fahr = lower; while (fahr = upper) celsius = 5

6、 * (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; /* 温度表的下限 */ upper = 300; /* 温度表的上限 */ step = 20; /* 步长 */ fahr = lower; while (fahr = upper) celsius =

7、 5 * (fahr-32) / 9; printf(“%dt%dn“, fahr, celsius); fahr = fahr + step; ,fc1.c,更简单、精确的对照表打印程序,#include #define LOWER 0 /* 表的下限 */ #define UPPER 300 /* 表的上限 */ #define STEP 20 /* 步长 */ /* 打印华氏-摄氏温度对照表 */ main ( ) int fahr; for ( fahr = LOWER; fahr = UPPER; fahr = fahr + STEP ) printf ( “%3d # %6.1fn

8、“, fahr, (5.0 / 9.0) * (fahr - 32 ) ); ,fc2.c,字符输入输出,c = getchar() 从键盘读入一个字符,赋值给变量c putchar(c) 把c输出到屏幕 拷贝的基本思想: 读一个字符 while ( 该字符不是文件结束指示符 ) 输出刚读进的字符 读下一个字符,拷贝(Copy),#include /* 用于将输入复制到输出的程序;第1个版本 */ main ( ) int c; c = getchar ( ); while ( c != EOF ) putchar ( c ); c = getchar ( ); ,copy1.c,一个更好的版

9、本,#include /* 用于将输入复制到输出的程序;第2个版本 */ main ( ) int c; while ( (c = getchar ( ) ) != EOF ) putchar ( c ); ,copy2.c,计算行数,#include /* 统计输入的行数 */ main ( ) int c; long nl; nl = 0; while ( (c = getchar ( ) ) != EOF ) if ( c = n ) nl+; printf(“%dn“, nl); ,counter.c,加法器,#include /* 计算输入的两个整数的和 */ main ( ) in

10、t 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“, TOTAL_NUMBER); for (i=0; iTOTAL_NUMBER; i+) scanf(“%f“, ,average.c,函数(Function),前面使用了系统提供的函数:

11、printf,scanf,getchar,putchar 使用函数时,我们不用知道这个函数内部是如何运作的,只按照我们的需要和它的参数形式调用它即可 我们也可以定义自己的函数 “一个程序应该是轻灵自由的,它的函数就象串在一根线上的珍珠。”(编程之道),power函数,/* power: 求底的n次幂; n =0 */ int power(int base, int n) int i, p; p = 1; for ( i = 1; i = n; +i ) p = p * base; return p; ,power.c,power函数的调用(Call),#include int power(in

12、t 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, long, float 数组 while, for, if 代码风格 注释、缩进、空行、命名 函数,C程序设计语言,第2章 类型、运算符与表达式,标识符(Identifi

13、ers),用户自定义的符号叫标识符 如变量名、函数名、宏和类型名 标识符由字母、数字和下划线组成,大小写敏感 不可以是数字开头 标识符要直观,能表达它的功能 下划线和大小写通常用来增强可读性 variablename variable_name, VARIABLE_NAME VariableName, variableName 关键字(keyword)不可作为标识符 int, float, for, while, if等(教材164页) 某些功能的变量采用习惯命名 如:for语句所采用的循环变量习惯用i, j, k,基本数据类型(Data Type),int 整数,在目前绝大多数机器上占4个字

14、节 TC2中是2个字节 所占字节数取决于机器字长 float 单精度浮点数,一般是4个字节长 double 双精度浮点数,一般是8个字节长 char 字符,一般是1个字节长 用来表示256个ASCII字符,或者0255的整数,数据类型修饰符,short short int,短整数,一般2个字节长。通常简写为short long long int,长整数,一般是4个字节长。通常简写为long long double,高精度浮点数,一般是10个字节长。 signed 用来修饰char、int、short和long,说明他们是有符号的整数(正整数、0和负整数)。一般缺省都是有符号的,所以这个修饰符通

15、常省略 unsigned 用来修饰char、int、short和long,说明他们是无符号的整数(正整数和0),超出取值范围会怎样?,TC2中int的范围是-3276732767 如果我们给它一个小于-32767或者大于32767的数会如何呢? 现场编程测验,小蛇能吞下大象吗?,溢出(Overflow)造成的危害,一台安装了Windows 95/98的机器,如果连续运行49.7天没有重新启动,可能死机 原因: Windows自启动时刻起,有一个计数器,记录系统已经运行了多少毫秒。这个计数器是个unsigned long 类型的变量 unsigned long的最大值是:4294967295 一

16、天有 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 is equal to %fn“, f); 运行结果会是什么?,float.c,浮点数的陷阱,float的精度低,较易发生精度带来的相等性判断问题 double精

17、度高,这个问题发生的概率小一些,但也存在 解决办法: if (fabs(f 123.456) 1E-5) ,根据精度要求设定,使用变量要注意,不要对变量所占的字节数想当然 用sizeof获得变量或者数据类型的长度 用ANSI C定义的宏确定数据的表示范围,解决溢出问题,sizeof.c,常数(Constant),整型常数 123、456 123456 123l、123L、123456l、123456L 浮点常数 123.45、456.78 1e-2、4.5e3 123.45f、 456.78F、1e-2f、4.5e3F 123.45l、 456.78L、1e-2l、4.5e3L,八进制与十六进

18、制常数,以数字“0”开始的整型常数是八进制数 010和10大小不一样 因为八进制并不常用,所以此种表示法比较少见,因而常被用错 以“0x”或者“0X”开始的整型常数是十六进制 AF和af用来表示十进制的1015 0x11, 0x05, 0xFA, 0xFF 十六进制的形式比较常用,尤其在进行位一级的控制的时候,字符常数,字符常数的表示方法 a,A,5,%,$ 单引号内只能有一个字符,除非用“”开头 就是一个普通整数,也可以参与各种数学运算 每个字符具有一个0255之间的数值,可从ASCII表查出 注意:5和5的区别,A和A的区别 字符的数学运算在密码学内用得比较多,ascii.c,字符常数,转

19、义字符 一些特殊字符(无法从键盘输入或者在C语言里有它用)用转义字符表示 转义的思想在网络协议和文件格式中经常使用,字符串(String)常数,用双引号括住的由0个或多个字符组成的字符序列 “I am a string“ “表示空字符串 转义字符也可以在字符串中使用 引号只作为字符串开始和结束的标志 C语言内部用0表示字符串的结束 除注释外,是唯一可以出现中文的地方 “x“和x是不同的 里定义了一系列专门的字符串处理函数,枚举(Enumeration)常数,一个几乎被遗忘的角色 从程序来窥其一斑 enum weeks MON, TUE, WED, THU, FRI, SAT, SUN ; en

20、um 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,变量声明,变量必须“先定义,后使用” 所有变量必须在第一条可执行语句前定义 声明的顺序无关紧要 一条声明语句可声明若干个同类型的变量,变量名之间用逗号分隔 变量定义后,即占用内存,可向其存入各种数据,并可通过变量名使用数据 声明变量,是初始化变量的最好时机 不被初始化的变量

21、,其值为危险的随机数 char esc = ; int i = 0; int limit = MAXLINE + 1; float eps = 1.0e-5;,常量,用const修饰定义的变量为常量 const int i=0; 常量只能在定义时赋值,然后不能再改变其值 常数、常量、宏和枚举,都可以用来表示一个永远不会改变的数 前者不建议直接使用,而用后三者代替 后三者的工作机理是完全不同的,达到的效果也不尽相同,计算机只会计算 任何事物都要被表示成数字和公式的形式后,才能被计算机计算(被计算机处理) 事物到数字和公式的转换过程叫数学建模 因为:事物在计算机内的处理都是一种计算 又因为:计算就

22、要有操作数、运算法则和计算结果 所以:事物在计算机内的处理都有操作数、运算法则和计算结果 计算结果你可以留用,也可以忽略,算术运算符,+,-,*,/ 加、减、乘、除运算 四则混合运算中,先算乘除,后算加减, 先算左,后算右 % 求余运算,C语言中的运算,关系运算符,,=,=,=,!= 大于,大于等于,小于,小于等于,等于,不等于 关系运算符运算出的结果为0和1 0,表示假,即该关系不成立 1,表示真,即该关系成立 在所有涉及到真假判断的地方,0表示假,非0表示真,找别扭,int a=1; if (a = 0) printf(“OK“); int a=0; if (a = 0) printf(“

23、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 & 与运算 ( a b & b c);a大于b,并且b大于c | 或运算 ( a b | b

24、 c); a大于b,或者b大于c ! 求反 ( !a );如果a是0,结果非0;如果a是非0,结果是0 并不改变a的值,类型转换,在进行赋值操作时,会发生类型转换 将取值范围小的类型转为取值范围大的类型是安全的 反之是不安全的 如果大类型的值在小类型能容纳的范围之内,则平安无事 但是,浮点数转为整数,会丢失小数部分(非四舍五入) 反之,转换后的结果必然是错误的,具体结果与机器和实现方式有关。避免如此使用,字符串与数值类型之间的转换,int i = “123“ 这样用是不行地 atof(),atoi(),atol() 把字符串转为double,int和long 定义在stdlib.h中 spri

25、ntf() 可以用来把各种类型的数值转为字符串 定义在stdio.h中,自动类型转换,两个同种数据类型的运算结果,还是该类型 两个不同种数据类型的运算结果,是两种类型中取值范围更大的那种 long double double float long int short char 只要两者中有一个是unsigned,就都转为unsigned再计算 把数据赋值给另外一种类型变量也会发生自动类型转换 从小到大,顺利转换 从大到小,发出警告(好的编译器会给出),类型强转,可以通过“(类型)表达式”的方式把表达式的值转为任意类型 强转时,你必须知道你在做什么 强转与指针,并称C语言两大神器,用好了可以呼风

26、唤雨,用坏了就损兵折将,屠 龙 刀,倚 天 剑,加一和减一运算符,i+,i-,+i,-i +让参与运算的变量加1,-让参与运算的变量减1 运算符为后缀,先取i的值,然后加/减1 运算符为前缀,先加/减1,然后取i的值 在一行语句中,使用加1或者减1运算的变量只能出现 不仅可读性差,而且因为编译器实现的方法不同,容易导致不同编译器运行效果不一样,贻害无穷,位操作运算符,& 按位与运算 | 按位或运算 按位异或运算, 按位右移运算 按位求反,赋值运算符,赋值运算的结果是被赋值变量赋值后的值 a = b = c = 0; 下面两个语句是等价的 i = i + 2; i += 2; +、-、*、/、%

27、、&、|运算符都可以按此种方式处理 这种形式看起来更直观,而且执行效率一般也能更高一些,条件表达式,把a和b中的最大值放入z中 if (a b) z = a; else z = b; z = (a b) ? a : b; 此种表达式切忌用得过于繁杂,优先级,( ) - . ! + - + - * & (类型) sizeof * / % + - = = != & | & | ? : = += -= *= /= %= &= = |= = ,优先级,能背下优先级表的人凤毛麟角 脑细胞太宝贵了,不能用来死记硬背 用括号来控制运算顺序更直观、方便,并减少出错的概率 先算乘除,后算加减,有括号就先算括号里

28、的 括号太多,有时候不清晰 注意用空格做好分隔 实在不行就拆分表达式,C程序设计语言,第3章 控制流,三种基本结构,顺序结构、选择结构、循环结构 已经证明,任何程序均可只用这三种结构实现 Bhm, Corrado, and Jacopini Guiseppe. “Flow diagrams, Turing machines and languages with only two formation rules.“ Communication of ACM, 9(5):366-371, May 1966. 只用这三种结构的程序,叫结构化程序 程序“必须”符合结构化规则,流程图,顺序结构,选择结构

29、,循环结构,语句块(Block),括住的若干条语句构成一个语句块 语句块内可以定义变量 变量必须在语句块的开头定义 变量仅在定义它的语句块内(包括下层语句块)有效(scope.c) 同一个语句块内的变量不可同名,不同语句块可以同名(homonym.c) 各司其职、下层优先 尽量不要在下层语句块内定义变量,也尽量不要定义同名变量 语句块可以用在任何可以使用语句的地方,但没有道理要乱加语句块,if-else,选择结构的一种最常用形式 if (表达式) 语句块1; else 语句块2; 语句块3 表达式值非0时,执行语句块1,然后语句块3; 表达式值为0时,执行语句块2,然后语句块3 else部分可

30、以没有。当表达式值为0时,直接执行语句3 if-else嵌套使用时,注意else和谁配套的问题,if.c,else-if,if的一种扩展 if (表达式1) 语句块1; else if (表达式2) 语句块2; else if (表达式3) 语句块3; else 语句块4; 语句块5; else部分可以没有,switch,多路选择 switch (表达式) case 整型常数1: 语句1; case 整型常数2: 语句2; default: 语句3; default可以没有 现场编程完成计算器 不要忘记break,switch和else-if的比较,else-if比switch的条件控制更强大

31、一些 else-if可以依照各种逻辑运算的结果进行流程控制 switch只能进行=判断,并且只能是整数判断 switch比else-if更清晰 两者都要尽量避免用得过多、过长,尤其不要嵌套得太多 它们大大增加程序的分支,使逻辑关系显得混乱,不易维护,易出错,循环while,for,while (表达式) 语句块; for (表达式1; 表达式2; 表达式3) 语句块;,while,while (表达式) 语句块1; 语句块2; 只要表达式的值为非0,就重复执行语句块1,直到表达式值为0时止,开始执行语句块2,for,for (表达式1; 表达式2; 表达式3) 语句块; 首先执行表达式1。如果

32、表达式2的值为非0,就重复执行语句块和表达式3,直到表达式2的值为0时止 相当于: 表达式1; while (表达式2) 语句块; 表达式3; for的所有表达式均可省略,注意,在for和while语句之后一般没有分号 有分号表示循环体就是分号之前的内容,即循环体不存在 while (i 100); i+; for (i = 0; i 100; i+); printf(“%d“, i); for通常有一个循环变量控制循环的次数,不要在循环体内改变这个变量,循环do-while,do 语句块1; while (表达式); 语句块2; 首先执行语句,然后判断表达式的值。如果表达式为0,继续向下执行

33、,否则,再次执行语句,再次判断表达式的值 语句块1会被执行至少一次,选择三种循环的一般思路,如果循环次数已知,用for 如果循环次数未知,用while 如果循环体至少要执行一次,用do-while 只是思路,不是定律,break和continue,对for、while、do-while循环进行内部手术 break,退出循环 continue,中断此次循环的执行,开始下一次 break和continue少用为妙 它们增加了循环执行的分支,break更增加了循环的出口 它们可以用来处理程序异常,而尽量不要用来处理正常流程,C程序设计语言,第4章 函数与程序结构,函数(function)和模块(mo

34、dule),函数是C语言中模块化编程的最小单位 可以把每个函数看作一个模块 若干相关的函数可以合并作一个“模块”,函数的分类,函数生来都是平等的,没有高低贵贱之分,只有main()稍微特殊一点点 库函数 ANSI C定义的标准库函数 符合标准的C语言编译器必须提供这些函数 函数的行为也要符合ANSI C的定义 第三方库函数 由其它厂商自行开发的C语言函数库 不在标准范围内,能扩充C语言的功能 自定义函数 自己编写的函数 包装后,也可成为函数库,供别人使用,函数定义(definition),类型 函数名(类型 参数1, 类型 参数2, ) 函数体; return 表达式; ,返回值类型,标识符,

35、参数表,返回值,函数出口,函数定义(definition),函数是这样的一种运算: 函数名说明运算规则 参数是运算的操作数 返回值是运算的结果 当函数执行到return语句或时,函数的运算停止。程序从当次调用函数的地方继续执行 函数可以有多个return,但最好只有一个且是最后一行 用void定义返回值类型 函数没有运算结果,没有返回值 return语句之后不需要任何表达式 用void定义参数,表示没有参数 参数表里的参数(叫形式参数,parameter)也是函数的语句块内的变量,函数调用(call),函数名(表达式1, 表达式2, ); 调用一个函数之前,先要对其返回值类型、函数名和参数进行

36、声明(declare) 不对函数进行声明是非常危险的 函数定义也有声明函数的效果 调用函数时,提供的表达式(叫实际参数, argument)和该函数的形式参数必须匹配 数目一致 类型一一对应(会发生自动类型转换) 表达式的值赋值给对应的参数 返回值可以按需处理,realeql.c,函数调用的过程,函数的每次执行都会建立一个全新的独立的环境 在“栈”中为函数的每个变量(包括形式参数)分配内存 把实际参数的值复制给形式参数 开始执行函数内的第一条语句 函数内的代码在这个独立的环境内工作 函数退出时 求出返回值,将其存入一个可以被调用者访问的地方(x86中通常使用EAX寄存器) 收回分配给所有变量(

37、包括形式参数)的内存 程序控制权交给调用者,调用者拿到返回值,将其作为函数调用表达式的结果,main()、printf()和scanf() 特 殊 吗?,main() C语言允许不对函数参数和返回值类型进行说明 甚至可以连函数名都不声明 此时默认 该函数的参数是不定个数的int型 该函数返回值为int型 永远不要利用此特性! printf()、scanf() 变长参数表, 缺点:对参数类型和个数无法严格验证,易使用出错,使用函数要注意,每个函数只完成一个功能(包括main()) 对函数的功能可以用不含连词的一句话描述 函数不能过长 1986年IBM在OS/360的研究结果:大多数有错误的函数都

38、大于500行 1991年对148,000行代码的研究表明:小于143行的函数比更长的函数更容易维护 函数一定要对传进来的非法参数做点什么 向调用者提供错误信息 assert(),safediv.c,全局变量(Global Variable),在所有函数之外定义的变量是全局变量,在定义它的位置以后都有效 全局变量自动初始化为0 全局变量使函数之间的数据交换更容易,效率也高一些 但是不推荐使用,甚至禁止使用 程序的任何部分都可以改写全局变量,很难确定在程序的哪里改写了它,程序结构混乱 不得不用的时候(这种情况比较少见),要严格控制对它的改写,静态变量(static),函数的内部变量在函数退出后失效

39、(内存释放)。再次进入函数,变量重新定义 每次函数执行都建立一个全新的执行环境,不受其它函数的干扰 把此变量定义为static,则变量的值可以保存到下次进入函数 static int i; 静态变量自动初始化为0,static.c,递归(Recursion),函数直接或间接调用自己为递归 unsigned int func(unsigned int n) if (n = 0) return 1; else return n * func(n-1); ,recur.c,模块,模块包含两部分 源文件(xxx.c):一系列相关函数的定义 头文件(xxx.h):这些函数的声明等必要信息 函数声明、外部

40、变量声明、宏定义、类型定义 可以将模块编译为.obj文件,同.h文件一起供别人使用,从而保护了源代码 使用模块的过程 建立一个工程(project) 把各模块都加入到工程中 #include模块的头文件 开始使用此模块,编写模块的技术,模块的信息隐藏 用static定义的函数和全局变量只在此模块内有效(建议采用) 允许被其它模块使用的全局变量 在源文件中定义,不加static修饰 在头文件中进行声明,加extern修饰,预编译指令,编译器在开始正式编译之前处理的指令,叫预编译指令 它们不会存在于最后生成的目标代码中 文件包含:#include 用#include指定的文件内容替换#includ

41、e所在的行 用或者“括上文件名 表示在编译器的include目录内查找文件 “表示在当前目录查找文件 文件名中可以带有路径,#define,#define 宏名字 替换文本 在#define之后,所有独立出现“宏名字”的地方(除了字符串内)都被“替换文本”替换 “替换文本”中可以有空格 宏可以有参数 #define max(A,B) (A) (B) ? (A) : (B) 能想出带参数的宏和函数的区别吗? 定义宏的时候注意替换发生后产生的非预想结果 一般用括号可以避免,如上例,宏名中间不要有空格,与#define配套者,#undef,从现在开始取消#define的定义 #undef MAXLI

42、NE #if, #else,#elif,#endif #ifdef,#ifndef 这些预编译指令通常用来处理多文件工程和程序多版本的问题。(程序多版本一般是不同平台的版本,不同用户等级的版本,不同开发阶段的版本等),使用预编译指令的目的,增强程序可读性 但是调错时宏可能带来很多难题 精简源代码,提取变化 这一点更多时候用函数的效果更好,但宏也有其不可替代的优势 不编译无用代码,精炼目标代码,C程序设计语言,第5章 指针与数组,计算机内的存储部件,活动中的所有指令和数据都保存在内存内 速度快,但是掉电即失 可以随机访问 只要指名要访问的内存单元的地址,就可以立即访问到该单元 地址是一个无符号整

43、数(通常用16进制数),其字长与主机相同 内存中的每个字节都有唯一的一个地址,内存(Random Access Memory) 地址(Address),指针的故事,“该程序执行了非法操作,即将关闭” 这种错误几乎全是由指针和数组导致的 黑客攻击服务器利用的bug绝大部分都是指针和数组造成的 有些非计算机专业的人,尽量避免使用指针,指针的故事,铁杆C/C+程序员最挚爱的武器:指针 指针造就了C/C+的高效和强大 很多不可能的任务由指针完成 main()char*a=“main()char*a=%c%s%c;printf(a,34,a,34);“;printf(a,34,a,34);,关于指针的原

44、则,学习原则 一定要学会 其实通常的应用很简单 就是一个变量 复杂的应用也不建议使用 使用原则 永远要清楚每个指针指向了哪里 永远要清楚指针指向的位置是什么,数组(Array),若干类型相同的相关数据凑到一起,就是数组 定义 类型 数组名整型常数1整型常数2 整型常数n; int a64; 使用 a00、a12、a53 每个元素都是一个普通变量 下标可以是任意整型表达式 数组的各个元素在内存中分布在一起,分布规律是array.c 思考一下一维和三维数组怎么分布呢?,从类型的角度理解数组,int a10; 定义了一个有10个int类型元素的数组 a的类型可以看作int10(只是看作,语法并不允许

45、这么定义: int10 a) int a2010; 定义了一个有20个int10类型元素数组 a0、 a1a9的类型是int10,所以a00、 a01a199的类型是int int a302010; 这个呢? 这种特性决定了数组元素在内存的分布规律,也解释了数组的很多语法现象,数组初始化,数组定义后的初值仍然是随机数,一般需要我们来初始化 int a5 = 12, 34, 56 ,78 ,9 ; int a5 = 0 ; int a = 11, 22, 33, 44, 55 ; 数组大小最好用宏来定义,以适应未来可能的变化 #define SIZE 10 int aSIZE;,数组的使用,数组

46、的下标都是从0开始 对数组每个元素的使用与普通变量无异 可以用任意表达式作为下标,动态决定访问哪个元素 for (i=0; iSIZE; i+) ai = 2 * i; 下标越界是大忌! 使用大于最大下标的下标,将访问数组以外的空间。那里的数据不是我们所想定的情况,可能带来严重后果 有时,故意越界访问数组会起到特别效果,但一定要对自己在做什么了如指掌 sizeof可以用来获得数组所占字节数 sizeof(a) sizeof(a0),数组的用处与特点,保存大量同类型的相关数据 快速地随机访问 一旦定义,不能再改变大小 在编译阶段就确定了数组的大小 数组名几乎就是一个指针,指针(Pointer),

47、int *p; 定义了一个指针变量p,简称指针p p是变量,int*是类型 变量都占用内存空间,p的大小是sizeof(int*) p用来保存地址。此时这个地址是哪呢(p指向哪呢)? int i; p = ,指针,指针也是数据类型。指向不同数据类型的指针,分别为不同的数据类型 int*、float*、char*、int*、int* 指针指向非其定义时声明的数据类型,将引起warning void*类型的指针可以指向任意类型的变量 指针在初始化时一般int *p=NULL; NULL表示空指针,即无效指针 但它只是逻辑上无效,并不是真正地无效 如果指针指向一个非你控制的内存空间,并对该空间进行访

48、问,将可能造成危险,&与*运算符,*和指针的组合是一个变量,该变量的地址和类型分别是指针指向的地址和指针定义时指向的类型 int i, *p; p = ,指针与数组,数组名可以看作一个指针 只是不能修改这个指针的指向 常指针 int a10; a的类型是int10 a的类型也是int* 指针可当作数组名使用, 反之亦然 int *p, a10; p = a; p1 = 0; *a = 0;,指针运算,int* p=NULL; p+; /* p的值会是多少? */ 指针的加减运算是以其指向的类型的字长为单位的 int *p, a10; p = a; *(p+3) 等价于 a3 p+; *p 等价于 a1,指针运算,int *p, *q, a10; p = a; q = 运算法则 只能进行加减和关系运算 只能同类型指针之间或指针与整数之间运算,“类型”本不存在,存储器在保存数据时并不关心数据的类型 完全以二进制方式工作 我们向计算机发出的指令说明了某块内存里数

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1