




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C语言程序设计For 123高计网上学期有点失败C语言用于做什么?学习高级程序设计的基础编制程序C程序设计语言的地盘C语言最擅长什么?与硬件打交道资源消耗少效率要求高现状?关系族谱图专业中的作用?网页网站设计计算机二级C?软件设计难不难?听说:除了难,还是难!我们怎么学?程序设计诀窍:多看,多练,多动手抓住原理人的脑袋对数据的处理对比程序对数据的处理!一点建议自己多看书!考核方法平时考勤 20%上机实验上机实验 30%期末项目期末项目 30%期末考试20%What is a computer?What is a computer?硬件(hardware) 键盘、鼠标、显示器、硬盘、DVDs、内
2、存、CPU 硬件成本下降很快 摩尔定律 每过18个月微芯片的集成度翻一番,而芯片价格保持不变软件(software) 运行于计算机之上的程序(Program)如何降低软件开发成本?计算机如何工作 很像厨师的菜谱,定义了执行某个任务所需的步骤 但不幸和不同的是,你不能使用你自己的母语来写CodeWhat is programming language? C程序设计语言的地盘 以往的擅长: 与操作系统和基础工具有关的编程任务 操作系统核心,设备驱动程序,系统工具,网络应用,编辑器,字处理工具,编译器,某些图形和GUI应用,以及数据库应用 未来的发展: 高性能、实时中间件,嵌入式领域,并发程序设计等
3、机器语言 计算1+1汇编语言BASIC语言C语言C语言的创世纪 一切从一个叫“Space Travel”的电子游戏开始 为了让他的游戏能在PDP-7上运行,Ken Thompson用汇编语言给PDP-7写了一个操作系统UNIX 汇编太不好用了,Thompson需要高级语言 试验了一些高级语言,包括Fortran,都不理想 他在BCPL基础上,自己设计了一个B语言 UNIX开始发展,B也不够用了 Dennis Ritchie加入,把B改造成C 开始用C重写UNIXRitchie和Thompson在开发UNIX接受美国国家技术勋章C程序设计语言 是一种高级语言 高级语言并不是“高级”,只是相对低级
4、语言,在一个高的级别上进行编程 历史悠久,战勋卓著 诞生于上世纪70年代初,成熟于80年代(C89),修订与90年代(C99) 很多重量级软件都是用C写的 上天入地,无所不能 几乎没有不能用C写出来的软件,没有不支持C的系统 很多流行语言、新生语言都借鉴了它的思想、语法 从C+,到Java,再到C#,还有php等C语言的祖师爷Dennis M. RitchieRitchie漫画像C is quirky, flawed, and an enormous success.计算机基本工作过程 整个过程的执行者是硬件,但硬件是受软件控制的 编程,就是编写软件,使硬件按照人的意图工作编译运行 编译过程
5、程序员(Programmer)编写程序源代码(Source Code) 编译器(Compiler)把源代码转换为可被计算机理解的机器代码(Machine Code),并把机器代码以可执行文件(Executable File)的形式保存在磁盘上 软件的运行 计算机把机器代码读入到内存(Memory),由CPU运行这些代码,读取输入(Input),产生输出(Output),完成程序员预定的功能编译语言 一种编译语言对应一种编译器 程序员按照该语言的语法编写程序源代码,把自己的意图融入到代码中 编译器读入源代码,把程序员的意图转换成可执行程序,供他人使用解释运行 解释运行过程 程序员编写程序源代码
6、解释器读入源代码,并执行源代码 解释运行的语言特点 执行速度慢 好学易用 先编译、后解释 把源代码编译成更容易解释的中间代码,然后再解释运行C程序设计语言第1章 观其大略简单数学题目求解步骤题目:题目:yxz简单程序求解步骤题目:题目:yxz简单程序实现方式1void main()/主函数(程序入口)int x,y,z;/设变量x=10;/变量赋值y=40;z=x+y;/得出结果50元x=8;/变量赋值y=40;z=x+y;/得出结果48元简单程序实现方式1void main()/主函数(程序入口)int x,y,z;/设变量z=huijia(10,40);z=huijia(8,40);解答数
7、学与编程之相同 设变量作为存储数据 如:x,y,z 表达式 如:z=x+y;z=x*y; 函数 实现某一功能 Y=f(x);解答数学与编程之不同 变量存储空间 如:x,y,z不管空间,因在脑子里 变量有类型 如:x因在脑子里,你想它放什么就放什么 但电脑是具体存放地方必须有类型,如口袋 函数 是将多个计算式子包括起来,数学仅仅是一个表达式如:抛物线函数C语言编程几个关键概念 变量 用于存放数据 表达式 如:z=x+y; 如:xy 函数 多个表达式构成的一个功能模块C语言程序结构C语言程序运行Hello, World#include main()printf(hello, worldn); 超级
8、无敌考考你:如何把“hello”和“world” 分别打印在两行?打印华氏温度与摄氏温度对照表 计算公式: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 * (fahr-
9、32) / 9;printf(%dt%dn, fahr, celsius);fahr = fahr + step;代码风格#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 * (fahr-32) / 9; pri
10、ntf(%dt%dn, fahr, celsius); fahr = fahr + step; 更简单、精确的对照表打印程序#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, fahr, (5.0 / 9.0) * (fahr - 32 ) );字符输入输
11、出 c = getchar() 从键盘读入一个字符,赋值给变量c putchar(c) 把c输出到屏幕 拷贝的基本思想:读一个字符while ( 该字符不是文件结束指示符 )输出刚读进的字符读下一个字符拷贝(Copy)#include /* 用于将输入复制到输出的程序;第1个版本 */main ( )int c;c = getchar ( );while ( c != EOF ) putchar ( c );c = getchar ( );一个更好的版本#include /* 用于将输入复制到输出的程序;第2个版本 */main ( )int c;while ( (c = getchar (
12、) ) != EOF ) putchar ( c );计算行数#include /* 统计输入的行数 */main ( )int c;long nl;nl = 0;while ( (c = getchar ( ) ) != EOF )if ( c = n )nl+;printf(%dn, nl);加法器#include /* 计算输入的两个整数的和 */main ( )int a, b;printf(Please input two integers:);scanf(%d%d, &a, &b);printf(Sum=%dn, a+b);平均分#include /* 计算某科成绩的平均值 */#
13、define TOTAL_NUMBER 10 /* 总人数 */main ( )float sum=0, scoreTOTAL_NUMBER;int i;printf(Input %d scores:n, TOTAL_NUMBER);for (i=0; i=0 */int power(int base, int n)int i, p;p = 1;for ( i = 1; i = n; +i )p = p * base;return p;power函数的调用(Call)#include int power(int base, int n);/* 测试power函数 */main ( )int m
14、, n;m = power(2, 1);n = power(-3, 3);printf(%d %dn, m, n);return 0;这一章我们学到了 #include #define main() printf(), scanf() getchar(), putchar() =, =, != int, long, float 数组 while, for, if 代码风格 注释、缩进、空行、命名 函数C程序设计语言第2章 类型、运算符与表达式标识符(Identifiers) 用户自定义的符号叫标识符 如变量名、函数名、宏和类型名 标识符由字母、数字和下划线组成,大小写敏感 不可以是数字开头 标
15、识符要直观,能表达它的功能 下划线和大小写通常用来增强可读性 variablename variable_name, VARIABLE_NAME VariableName, variableName 关键字(keyword)不可作为标识符 int, float, for, while, if等(教材164页) 某些功能的变量采用习惯命名 如:for语句所采用的循环变量习惯用i, j, k基本数据类型(Data Type) int 整数,在目前绝大多数机器上占4个字节 TC2中是2个字节 所占字节数取决于机器字长 float 单精度浮点数,一般是4个字节长 double 双精度浮点数,一般是8个
16、字节长 char 字符,一般是1个字节长 用来表示256个ASCII字符,或者0255的整数数据类型修饰符 short short int,短整数,一般2个字节长。通常简写为short long long int,长整数,一般是4个字节长。通常简写为long long double,高精度浮点数,一般是10个字节长。 signed 用来修饰char、int、short和long,说明他们是有符号的整数(正整数、0和负整数)。一般缺省都是有符号的,所以这个修饰符通常省略 unsigned 用来修饰char、int、short和long,说明他们是无符号的整数(正整数和0)超出取值范围会怎样? T
17、C2中int的范围是-3276732767 如果我们给它一个小于-32767或者大于32767的数会如何呢? 现场编程测验溢出(Overflow)造成的危害 一台安装了Windows 95/98的机器,如果连续运行49.7天没有重新启动,可能死机 原因: Windows自启动时刻起,有一个计数器,记录系统已经运行了多少毫秒。这个计数器是个unsigned long 类型的变量 unsigned long的最大值是:4294967295 一天有 24*60*60*1000 = 86400000毫秒 4294967295 / 86400000 = 49.71026961805 当49.7天的时候,
18、此计数器会溢出,引起死机浮点数的陷阱#include main()float f;f = 123.456;if (f = 123.456)printf(f is equal to 123.456 indeed.);elseprintf(In fact, f is equal to %fn, f);运行结果会是什么?浮点数的陷阱 float的精度低,较易发生精度带来的相等性判断问题 double精度高,这个问题发生的概率小一些,但也存在 解决办法:if (fabs(f 123.456) 1E-5)使用变量要注意 不要对变量所占的字节数想当然 用sizeof获得变量或者数据类型的长度 用ANSI
19、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八进制与十六进制常数 以数字“0”开始的整型常数是八进制数 010和10大小不一样 因为八进制并不常用,所以此种表示法比较少见,因而常被用错 以“0 x”或者“0X”开始的整型常数是十六进制 AF和af用来表示十进制的1015 0 x11, 0 x05, 0 x
20、FA, 0 xFF 十六进制的形式比较常用,尤其在进行位一级的控制的时候字符常数 字符常数的表示方法 a,A,5,%,$ 单引号内只能有一个字符,除非用“”开头 就是一个普通整数,也可以参与各种数学运算 每个字符具有一个0255之间的数值,可从ASCII表查出 注意:5和5的区别,A和A的区别 字符的数学运算在密码学内用得比较多字符常数 转义字符 一些特殊字符(无法从键盘输入或者在C语言里有它用)用转义字符表示 转义的思想在网络协议和文件格式中经常使用字符串(String)常数 用双引号括住的由0个或多个字符组成的字符序列 I am a string 表示空字符串 转义字符也可以在字符串中使用
21、 引号只作为字符串开始和结束的标志 C语言内部用0表示字符串的结束 除注释外,是唯一可以出现中文的地方 x和x是不同的 里定义了一系列专门的字符串处理函数枚举(Enumeration)常数一个几乎被遗忘的角色从程序来窥其一斑 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 NO
22、T Tuesday.n);变量声明 变量必须“先定义,后使用” 所有变量必须在第一条可执行语句前定义 声明的顺序无关紧要 一条声明语句可声明若干个同类型的变量,变量名之间用逗号分隔 变量定义后,即占用内存,可向其存入各种数据,并可通过变量名使用数据 声明变量,是初始化变量的最好时机 不被初始化的变量,其值为危险的随机数char esc = ;int i = 0;int limit = MAXLINE + 1;float eps = 1.0e-5;常量 用const修饰定义的变量为常量 const int i=0; 常量只能在定义时赋值,然后不能再改变其值 常数、常量、宏和枚举,都可以用来表示一
23、个永远不会改变的数 前者不建议直接使用,而用后三者代替 后三者的工作机理是完全不同的,达到的效果也不尽相同 计算机只会计算 任何事物都要被表示成数字和公式的形式后,才能被计算机计算(被计算机处理) 事物到数字和公式的转换过程叫数学建模 因为:事物在计算机内的处理都是一种计算 又因为:计算就要有操作数、运算法则和计算结果 所以:事物在计算机内的处理都有操作数、运算法则和计算结果 计算结果你可以留用,也可以忽略算术运算符 +,-,*,/ 加、减、乘、除运算 四则混合运算中,先算乘除,后算加减,先算左,后算右 % 求余运算关系运算符 ,=, b & b c);a大于b,并且b大于c | 或运算 (
24、a b | b c); a大于b,或者b大于c ! 求反 ( !a );如果a是0,结果非0;如果a是非0,结果是0 并不改变a的值类型转换 在进行赋值操作时,会发生类型转换 将取值范围小的类型转为取值范围大的类型是安全的 反之是不安全的 如果大类型的值在小类型能容纳的范围之内,则平安无事 但是,浮点数转为整数,会丢失小数部分(非四舍五入) 反之,转换后的结果必然是错误的,具体结果与机器和实现方式有关。避免如此使用字符串与数值类型之间的转换 int i = 123 这样用是不行地 atof(),atoi(),atol() 把字符串转为double,int和long 定义在stdlib.h中 s
25、printf() 可以用来把各种类型的数值转为字符串 定义在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. 只用这三种结构的程序,叫结构化程序 程序“必须”符合结构化规则流程图语句块(Block) 括住的若干条语句构成一个语句块 语句块内可以定义变量 变量必须在语句块的
29、开头定义 变量仅在定义它的语句块内(包括下层语句块)有效(scope.c) 同一个语句块内的变量不可同名,不同语句块可以同名(homonym.c) 各司其职、下层优先 尽量不要在下层语句块内定义变量,也尽量不要定义同名变量 语句块可以用在任何可以使用语句的地方,但没有道理要乱加语句块if-else 选择结构的一种最常用形式 if (表达式)语句块1;else语句块2;语句块3 表达式值非0时,执行语句块1,然后语句块3;表达式值为0时,执行语句块2,然后语句块3 else部分可以没有。当表达式值为0时,直接执行语句3 if-else嵌套使用时,注意else和谁配套的问题else-if if的一
30、种扩展 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可以没有 现场编程完成计算器 不要忘记breakswitch和else-if的比较 else-if比switch的条件控制更强大一些 else-if可以依照各种逻辑运算的结果进行流程控制 switch只能进行=判断,并且只能是整数判断 switch比else-if更清晰 两者都要尽量避
31、免用得过多、过长,尤其不要嵌套得太多 它们大大增加程序的分支,使逻辑关系显得混乱,不易维护,易出错循环while,for while (表达式)语句块; for (表达式1; 表达式2; 表达式3)语句块;while while (表达式)语句块1;语句块2; 只要表达式的值为非0,就重复执行语句块1,直到表达式值为0时止,开始执行语句块2for for (表达式1; 表达式2; 表达式3)语句块; 首先执行表达式1。如果表达式2的值为非0,就重复执行语句块和表达式3,直到表达式2的值为0时止 相当于:表达式1; while (表达式2)语句块;表达式3; for的所有表达式均可省略注意 在f
32、or和while语句之后一般没有分号 有分号表示循环体就是分号之前的内容,即循环体不存在 while (i 100);i+; for (i = 0; i 100; i+);printf(%d, i); for通常有一个循环变量控制循环的次数,不要在循环体内改变这个变量循环do-while do语句块1;while (表达式);语句块2; 首先执行语句,然后判断表达式的值。如果表达式为0,继续向下执行,否则,再次执行语句,再次判断表达式的值 语句块1会被执行至少一次选择三种循环的一般思路 如果循环次数已知,用for 如果循环次数未知,用while 如果循环体至少要执行一次,用do-while 只
33、是思路,不是定律break和continue 对for、while、do-while循环进行内部手术 break,退出循环 continue,中断此次循环的执行,开始下一次 break和continue少用为妙 它们增加了循环执行的分支,break更增加了循环的出口 它们可以用来处理程序异常,而尽量不要用来处理正常流程C程序设计语言第4章 函数与程序结构函数(function)和模块(module) 函数是C语言中模块化编程的最小单位 可以把每个函数看作一个模块 若干相关的函数可以合并作一个“模块”函数的分类 函数生来都是平等的,没有高低贵贱之分,只有main()稍微特殊一点点 库函数 ANS
34、I C定义的标准库函数 符合标准的C语言编译器必须提供这些函数 函数的行为也要符合ANSI C的定义 第三方库函数 由其它厂商自行开发的C语言函数库 不在标准范围内,能扩充C语言的功能 自定义函数 自己编写的函数 包装后,也可成为函数库,供别人使用函数定义(definition)类型 函数名(类型 参数1, 类型 参数2, )函数体;return 表达式;函数定义(definition) 函数是这样的一种运算: 函数名说明运算规则 参数是运算的操作数 返回值是运算的结果 当函数执行到return语句或时,函数的运算停止。程序从当次调用函数的地方继续执行 函数可以有多个return,但最好只有一
35、个且是最后一行 用void定义返回值类型 函数没有运算结果,没有返回值 return语句之后不需要任何表达式 用void定义参数,表示没有参数 参数表里的参数(叫形式参数,parameter)也是函数的语句块内的变量函数调用(call) 函数名(表达式1, 表达式2, ); 调用一个函数之前,先要对其返回值类型、函数名和参数进行声明(declare) 不对函数进行声明是非常危险的 函数定义也有声明函数的效果 调用函数时,提供的表达式(叫实际参数, argument)和该函数的形式参数必须匹配 数目一致 类型一一对应(会发生自动类型转换) 表达式的值赋值给对应的参数 返回值可以按需处理函数调用的
36、过程 函数的每次执行都会建立一个全新的独立的环境 在“栈”中为函数的每个变量(包括形式参数)分配内存 把实际参数的值复制给形式参数 开始执行函数内的第一条语句 函数内的代码在这个独立的环境内工作 函数退出时 求出返回值,将其存入一个可以被调用者访问的地方(x86中通常使用EAX寄存器) 收回分配给所有变量(包括形式参数)的内存 程序控制权交给调用者,调用者拿到返回值,将其作为函数调用表达式的结果main()、printf()和scanf()特 殊 吗? main() C语言允许不对函数参数和返回值类型进行说明 甚至可以连函数名都不声明 此时默认 该函数的参数是不定个数的int型 该函数返回值为
37、int型 永远不要利用此特性! printf()、scanf() 变长参数表, 缺点:对参数类型和个数无法严格验证,易使用出错使用函数要注意 每个函数只完成一个功能(包括main()) 对函数的功能可以用不含连词的一句话描述 函数不能过长 1986年IBM在OS/360的研究结果:大多数有错误的函数都大于500行 1991年对148,000行代码的研究表明:小于143行的函数比更长的函数更容易维护 函数一定要对传进来的非法参数做点什么 向调用者提供错误信息 assert()全局变量(Global Variable) 在所有函数之外定义的变量是全局变量,在定义它的位置以后都有效 全局变量自动初始
38、化为0 全局变量使函数之间的数据交换更容易,效率也高一些 但是不推荐使用,甚至禁止使用 程序的任何部分都可以改写全局变量,很难确定在程序的哪里改写了它,程序结构混乱 不得不用的时候(这种情况比较少见),要严格控制对它的改写静态变量(static) 函数的内部变量在函数退出后失效(内存释放)。再次进入函数,变量重新定义 每次函数执行都建立一个全新的执行环境,不受其它函数的干扰 把此变量定义为static,则变量的值可以保存到下次进入函数 static int i; 静态变量自动初始化为0递归(Recursion) 函数直接或间接调用自己为递归 unsigned int func(unsigned
39、 int n) if (n = 0) return 1;else return n * func(n-1); 模块模块包含两部分 源文件(xxx.c):一系列相关函数的定义 头文件(xxx.h):这些函数的声明等必要信息函数声明、外部变量声明、宏定义、类型定义可以将模块编译为.obj文件,同.h文件一起供别人使用,从而保护了源代码使用模块的过程1. 建立一个工程(project)2. 把各模块都加入到工程中3. #include模块的头文件4. 开始使用此模块编写模块的技术 模块的信息隐藏 用static定义的函数和全局变量只在此模块内有效(建议采用) 允许被其它模块使用的全局变量 在源文件中
40、定义,不加static修饰 在头文件中进行声明,加extern修饰预编译指令 编译器在开始正式编译之前处理的指令,叫预编译指令 它们不会存在于最后生成的目标代码中 文件包含:#include 用#include指定的文件内容替换#include所在的行 用或者括上文件名 表示在编译器的include目录内查找文件 表示在当前目录查找文件 文件名中可以带有路径#define #define 宏名字 替换文本 在#define之后,所有独立出现“宏名字”的地方(除了字符串内)都被“替换文本”替换 “替换文本”中可以有空格 宏可以有参数 #define max(A,B) (A) (B) ? (A)
41、: (B) 能想出带参数的宏和函数的区别吗? 定义宏的时候注意替换发生后产生的非预想结果 一般用括号可以避免,如上例与#define配套者 #undef,从现在开始取消#define的定义 #undef MAXLINE #if, #else,#elif,#endif #ifdef,#ifndef 这些预编译指令通常用来处理多文件工程和程序多版本的问题。(程序多版本一般是不同平台的版本,不同用户等级的版本,不同开发阶段的版本等)使用预编译指令的目的 增强程序可读性 但是调错时宏可能带来很多难题 精简源代码,提取变化 这一点更多时候用函数的效果更好,但宏也有其不可替代的优势 不编译无用代码,精炼目
42、标代码C程序设计语言第5章 指针与数组 计算机内的存储部件,活动中的所有指令和数据都保存在内存内 速度快,但是掉电即失 可以随机访问 只要指名要访问的内存单元的地址,就可以立即访问到该单元地址是一个无符号整数(通常用16进制数),其字长与主机相同 内存中的每个字节都有唯一的一个地址内存(Random Access Memory)地址(Address)指针的故事 “该程序执行了非法操作,即将关闭” 这种错误几乎全是由指针和数组导致的 黑客攻击服务器利用的bug绝大部分都是指针和数组造成的 有些非计算机专业的人,尽量避免使用指针指针的故事 铁杆C/C+程序员最挚爱的武器:指针 指针造就了C/C+的
43、高效和强大 很多不可能的任务由指针完成 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; 这个呢? 这种特性决定了数组元素在内存的分布规律,也解释了数组的很多语法现象数组初始化 数组定义后的初值仍然是随机数,一般需要我们来初始化 int a5 = 12, 34, 56 ,78 ,9 ; int a5 =
45、 0 ; int a = 11, 22, 33, 44, 55 ; 数组大小最好用宏来定义,以适应未来可能的变化 #define SIZE 10int aSIZE;数组的使用 数组的下标都是从0开始 对数组每个元素的使用与普通变量无异 可以用任意表达式作为下标,动态决定访问哪个元素 for (i=0; iSIZE; i+) ai = 2 * i; 下标越界是大忌! 使用大于最大下标的下标,将访问数组以外的空间。那里的数据不是我们所想定的情况,可能带来严重后果 有时,故意越界访问数组会起到特别效果,但一定要对自己在做什么了如指掌 sizeof可以用来获得数组所占字节数 sizeof(a) siz
46、eof(a0)数组的用处与特点 保存大量同类型的相关数据 快速地随机访问 一旦定义,不能再改变大小 在编译阶段就确定了数组的大小 数组名几乎就是一个指针指针(Pointer) int *p; 定义了一个指针变量p,简称指针p p是变量,int*是类型 变量都占用内存空间,p的大小是sizeof(int*) p用来保存地址。此时这个地址是哪呢(p指向哪呢)? int i;p = &i; *p就像普通的变量一样使用,其值是p指向的内存的内容,类型是int(在上例和i等价) p可以动态(任意)地指向不同内存,从而使*p代表不同的变量 p = 0; p = &a0;指针 指针也是数据类型。指向不同数据
47、类型的指针,分别为不同的数据类型 int*、float*、char*、int*、int* 指针指向非其定义时声明的数据类型,将引起warning void*类型的指针可以指向任意类型的变量 指针在初始化时一般int *p=NULL; NULL表示空指针,即无效指针 但它只是逻辑上无效,并不是真正地无效 如果指针指向一个非你控制的内存空间,并对该空间进行访问,将可能造成危险&与*运算符 &运算的结果指向该变量的指针 int i, *p;p = &i; int *p, a10;p = a; int *p, a10;p = &a0; int *p, a10;p = &a5; *和指针的组合是一个变量
48、,该变量的地址和类型分别是指针指向的地址和指针定义时指向的类型 int i, *p;p = &i;*p = 0; int *p, a10;p = a;*p = 0; int *p, a10;p = &a0;*p = 0; int *p, a10;p = &a5;*p = 0;指针与数组 数组名可以看作一个指针 只是不能修改这个指针的指向 常指针 int a10; a的类型是int10 a的类型也是int* 指针可当作数组名使用,反之亦然 int *p, a10;p = a;p1 = 0;*a = 0;指针运算 int* p=NULL;p+;/* p的值会是多少? */ 指针的加减运算是以其指向
49、的类型的字长为单位的 int *p, a10;p = a; *(p+3) 等价于 a3 p+; *p 等价于 a1指针运算 int *p, *q, a10;p = a;q = &a2; q - p = ? q = p + 3; 运算法则 只能进行加减和关系运算 只能同类型指针之间或指针与整数之间运算“类型”本不存在 存储器在保存数据时并不关心数据的类型 完全以二进制方式工作 我们向计算机发出的指令说明了某块内存里数据的类型 一块内存内保存着(61 62 63 64)16 以char类型看待每个字节: abcd 以float类型看待每个字节: 16777999408082104000000.00
50、0000 以int类型看待每个字节: 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( ); 指针强转后,可以把一块内存当作另一种类型来处理强强联手,我们可以随意控制任意内存指针与函数 指针既然是数据类型,自然可以做函数的参数和返回值的类型 指针做参数的经典
51、例子: main() int x, y; swap(x, y); void swap(int x, int y) int temp; temp = x; x = y; y = temp;指针做参数 main() int x, y; swap(&x, &y); void swap(int *px, int *py) int temp; temp = *px; *px = *py; *py = temp;指针做返回值 printf(%s, GetInput();.char* GetInput(void)char str100;scanf(%s, str);return str; char stri
52、ng30;printf(%s, GetInput(string); . char* GetInput(char* str)scanf(%s, str);return str;三个月使用scanf目睹之怪现状 int i;scanf(%d, i); /* 这样会如何?*/ int i;scanf(%f, &i); /* 这样又会如何?*/ char c;scanf(%d, &c); /* 这样呢?*/数组做参数 和指针一回事儿 void ProcessArray(int* a). void ProcessArray(int a).动态分配内存 在 和中均定义了下面的函数 void* malloc
53、(size_t size); size_t是在中定义的数据类型,就是一个unsigned int 向系统申请大小为size的内存块,把指向首地址的指针返回。如果申请不成功,返回NULL void free(void* block); 释放由malloc()申请的内存块。block是指向此块的指针 malloc申请的内存,在被free之前,程序的任何部分都可以使用 当然,要使用必须得到指向它的指针动态分配内存 如果malloc()申请的内存不被free()程序就退出,将产生内存泄露(Memory Leak) “内存泄露”一词类似“原料泄露”。泄露出去的原料不能被利用,导致生产过程中原料不足 ma
54、lloc()时,系统找到一块未占用的内存,将其标记为已占用,然后把地址返回,表明此程序占用此块内存,其它程序不能再用它 free()时,系统标记此块内存为未占用,本程序不能继续使用,所有程序可以申请使用 如果malloc()之后不free(),此块内存将永远不会被任何程序使用,就好像这块内存泄露出去一样防止内存泄露之道 在需要的时候才malloc,并尽量减少malloc的次数 能用自动变量解决的问题,就不要用malloc来解决 malloc一般在大块内存分配和动态内存分配时使用 malloc本身的执行效率就不高,所以过多的malloc会使程序性能下降 可以重复利用malloc申请到的内存 尽量
55、让malloc和与之配套的free在一个函数内 尽量把malloc集中在函数的入口处,free集中在函数的出口处 以上做法只能尽量降低产生泄露的概率。完全杜绝内存泄露,关键要靠程序员的细心与责任感字符串(String)与字符数组、字符指针 字符串 一串以0结尾的字符在C语言中被看作字符串 用双引号括起的一串字符是字符串常量,C语言自动为其添加0终结符 Hello world! 把字符串常量作为表达式直接使用,得到的值是该常量的地址 C语言并没有为字符串提供任何专门的表示法,完全使用字符数组和字符指针来处理 字符数组 每个元素都是字符类型的数组 char string100; 字符指针 指向字符
56、类型的指针 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 指针是一种数据类型 是从其它类型派生的类型 XX类型的指针 数组也是一种数据类型 是从其它类型派生的类型 每个元素都有一个类型 任何类型都可以做指针或者数组的基础类
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025-2030四星级酒店行业市场深度分析及供需形势与投资价值研究报告
- 2025-2030兽用疫苗行业市场深度分析及发展前景与投资机会研究报告
- 2025-2030中国高铬铸铁行业供需趋势及投资风险研究报告
- 女性乳房缩小的临床护理
- 2025至2031年中国机房桥架行业投资前景及策略咨询研究报告
- 2025-2030中国轿车悬架弹簧行业运行状况与未来前景研究报告
- 2025-2030中国脑炎疫苗行业市场发展趋势与前景展望战略研究报告
- 2025-2030中国速溶咖啡行业供需分析及发展前景研究报告
- 2025-2030中国贝诺酯行业市场全景调研及投资价值评估咨询报告
- 2025-2030中国菜籽油行业发展分析及发展趋势预测与投资风险研究报告
- 医患沟通技巧与人文关怀课件
- 保密管理与人工智能技术发展
- 毒理学新技术
- 新生儿贫血护理查房课件
- 电信电源专业应急预案
- “0”何去何从-小数近似数的教学思考与实践 论文
- GMW 17408-2017 Flexible Expanded Rubber And Rubber-Like Materials原版完整文件
- 上市公司固定资产减值研究 -以美的集团股份有限公司为例
- DB14T+2779-2023营造林工程监理规范
- 2023年中考任务型阅读真题(20+)
- 新团员入团仪式PPT模板
评论
0/150
提交评论