您的当前位置:首页正文

C语言教学难点浅析

来源:画鸵萌宠网
维普资讯 http://www.cqvip.com 第24卷第2期 忻州师范学院学报 V0J.24 No.2 2008年4月 JOURNAL OF XINZHOU TEACHERS UNIVERSITY Apr.2008 C语言教学难点浅析 武星,韩瑞峰 (忻州师范学院,山西忻州034000) 摘要:C语言既具有高级语言的优点,又具有低级语言的许多特点,因而成为迄今为止 应用最成功的计算机语言之一。但由于C语言所涉及的概念比较复杂,规则繁多,使用灵活, 容易出错,不少使用者特别是初学者感到困难。通过设计相应的示例程序来解决在初学者看 来非常困难的问题,取得了良好的教学效果。 关键词:指针;自增(自减)运算符;赋值运算符 中图分类号:TP312 文献标识码:A 文章编号:1671—1491(2008)02—0033一o4 1指针的使用和误用 机值。但在一个大程序中,危险性就大了,因为排错困难,而 在C语言中,程序中定义的各种类型数据都要分配内 且可能有其他的重要信息存储在指针iptr随机包含的地址 存空间,由于指针和内存地址是相同的概念,因此借助指针 中。如果使用小存储模式,这里代码和数据占用相同的空 可以完成对各种类型数据的访问。而错误指针可能使数据 间,就会有破坏机器代码的危险。 写到内存的任何位置。如果写到程序运行期间的堆栈,则正 1.2字符串的指针 在进行的函数调用间的联结或局部变量会被破坏。如果写 我们知道,字符串可以说明为char型的指针或char型 到执行期间的标准函数库模块,则运算符或函数的语义在程 数组,这两者基本上是相同的,但有一个非常重要的区别:如 序执行时会被改变。如果写到操作系统,可能导致输出设备 果使用char型的指针,则对于字符串不分配存储空间;如果 消失或系统突然停止工作。如果写到数据区或程序代码区, 使用char型数组,则分配存储空间,并且数组变量保存该存 可能会出现程序每次运行的状态都改变的奇怪现象。为了使 储空间的地址。 初学者尽可能避免指针的误用,我们设计了许多示例程序加 错误地理解这种区别,将会引起两种类型错误。看下面 以引导,取得了不错的效果。另一个行之有效的办法就是做 的程序: 好指针的初始化工作,最大限度地避免难以跟踪、难以排除的 main() 动态错误。因为即使初始化错误,也只会产生固定的错误。 { char}name,msg[10]; 1.1使用没有初始化的指针 prinff(”What is your name?”); 将一个值赋给一个没有初始化的指针是非常危险的。 scanf(”%s”,name); 例如: msg=”Hello,”; main() prinff(”%s%s”,msg,name);} { int}iptr; 乍看上去,这似乎很好,虽有点笨拙,但仍然允许。但事 }iptr=421: 实上这个程序会产生两种错误。第一种错误是语句scanf prinff(”}iptr=%d\n”,}iptr);} (“%s”,name)引起的;这个语句从语法角度看,本身是合 这个程序从语法角度看,没有任何问题。但在运行时会 法和正确的,因为name是一个char型的指针,不需要在它 引起什么样的问题呢?问题主要是由于没有给指针iptr赋 的前面使用地址运算符(&)。然而,程序没有为name分配 值而无法控制它,指针iptr可能有一个随机地址,即值421 任何存储空间,输入的姓名将会存储在name指针碰巧具有 被存储的地方,而这个地址有可能指向系统区。上面这个示 的随机地址中,这时会出现警告信息:(possible use of"name 例程序非常小,可以很容易地修改它,从而避免指针指向随 before definition),但不是语法错误。 收稿日期.2007—12—29 作者简介:武星(1978一),男,山西五寨人,英国Abertay Dunde大学硕士,从事遗传算法、入侵检测等研究。 维普资讯 http://www.cqvip.com 忻州师范学院学报 第二种引起错误的问题是语句msg=“Hello,”,编译 程序认为用户试图改变msg为常数字符串“Hello,”的地址。 第24卷 这个例子程序中使用了两种指针,其中P是指向数组a 的行指针,它的操作特性与数组名a相同;而指针q是一维 指针,将以指向的数据类型int单位变化。分析程序的循环, 有: 不能这样做!因为数组名是不能修改的常数(就象7是常数 一样,不能写成“7=i”)。编译程序将给出一个错误信息: “Lvalue required” i=0时,( P)[i]=a[0][0]= q+1=a[0] [0]+1=2,P和q的值不变; 怎样纠正这些错误呢?最简单的方法是将nalT_le和msg 的说明互换一下,即 main() i=1时,执行与i=0时同样的赋值,a[1][1]不变, 值为4。注意P++和++q使P的值为a[1],q的值为&a {char nalTl_e[10],{msg; prinff(“What is your nalT_le?”); seanf(“%s”,n ̄3e.); msg “Hello,”; prinff(“%s%s”,msg,ha.me);} 这样就没有问题了。变量nalT_le具有保存所输入姓名 的存储空间,而msg允许赋给它常数字符串“Hello,”的地 址。然而,如果不想改变前面的说明形式,那么需要改变程 序如下: #include<alloe.h> main() { char{n舢e,msg[10]; ha.me=(char{)malloe(10); prinff(“What is your ha.me?”); scanf(“%s”,nalTl_e); strepy(msg,“Hello,”); prinff(“%s%s”,msg,nalTl_e);} 函数调用mal1oc分配10个字节的存储器空间,并且将 该存储器的地址赋给指针n ̄l'ne,这样防止了第一种错误。 函数调用strepy完成字符串复制的功能,它将常数字符串 “Hello,”复制到数组msg中。 1.3二维数组的指针 对二维数组来说,使用指针的引用方法有两种,即行指 针法和一般指针法。行指针的操作特性与数组名相同,而一 般指针是以所指向数据的基本类型为单位变化。二者最重 要的区别在于指针的基类型不同。分析下面程序的运行结 果: #include<stdio.h> void main() {int a[3][4]={{1,2,3,4},{3,4,5,6},{5,6, 7,8}},i; int( P)[4]=a, q=a[0]; for(i=0;i<3;i++) {if(i==0)( P)[i+i/2]= q+1; else P++,+ q;} for(i=0;i<3;i++) prinff(“%d”,a[i儿i]);} 运行结果为:2,4,7 [0][1]; i=2时,( P)[i]=a[1][2]= q+1=a[0] [1]+1=3,a[2][2]不变,值为7。 2 自增(自减)运算符的求值问题 C语言中的自增(自减)运算符,有前置和后置两种用 法。前置时先计算后使用,后置时先使用后计算。例如:计 算表达式(i++)+(i++)+(i++)和(++i)+ (++i)+(++i)在i=3时的值。 void main() {int i=3,k1,k2; k1=(i++) +(i++) +(i++); prinff(“k1=%d,i=%d”,k1,i); i=3; k2=(++i) +(++i) +(++i); prinff(“k2=%d,i:%d”,k2,i);} 在C语言中变量的内存空间是在编译时分配的,而且 在变量的生命周期内,一个变量名只对应一块内存区域。如 上面程序中的变量i,编译时分配内存并初始化为整数3。 在计算表达式k 和k 的值时,首先要计算i++和++i的 值,根据自增(自减)运算符在前置和后置时的求值方式(前 置时先计算后使用,后置时先使用后计算),在计算k 表达 式的值时,变量i被引用三次参与加法运算以后,才进行三 次自增运算;而在计算k 表达式的值时,变量i三次自增运 算以后的结果6,被引用三次参与加法运算,所以: k1=(i++)+(i++)+(i++)=3+3+3=9 k2=(++i)+(++i)+(++i)=6+6+6=18 可见,在C语言的程序运行过程中,变量在每一次自增 运算或赋值运算之后,其所对应内存区域中的内容就被重 写,在变量的生命周期内的每一瞬间只能有唯一一个确定的 值。所以程序的运行结果为: kI=9,i=6 k2=18,i=6 上面的例子清晰地演示了自增(自减)运算符在前置和 后置时的求值方式,由于我们是在计算出表达式的值之后进 行输出,得到了上述结果。假定我们在输出函数中直接输出 表达式的值会是一种什么情况呢?看下面的例子: void main() {int i=3; 维普资讯 http://www.cqvip.com 第2期 武星,等:c语言教学难点浅析 35 prinff(“kl=%d,i=%d”,(i++)+(i ++) +(i++),i); i=3; 当会出现意想不到的结果,而编译系统并不提示出错,全靠 程序员的经验来找出问题。使用赋值运算符时必须注意两 点:一是赋值运算符的左边必须是变量。例如,在下面的语 句中,括号是否真的需要? x=(Y=8)+(z=9); prinff(“k2=%d,i=%d”,(++i) +(++i)+ (++i),i);} 程序运行结果为: ki=12,i=3 k2=15,i=3 如果两个括号都去掉,因为算术运算符的优先级高于赋 值运算符,所以会出现向表达式赋值的情况,可见第二个括 号必须保留才能保证赋值运算规则正确。对于第一个括号, 由于赋值运算的右结合性,去掉并不影响表达式的逻辑结 为什么会出现不一样的结果呢?因为C语言中函数调 用时参数是通过堆栈传递的,每个实参变量都是首先将其值 压入堆栈,然后在出栈的同时进行表达式求值,结果送入为 形参变量开辟的临时存储空间,完成参数值的传递。在上面 的程序中输出k。的值时,参数i的值首先入栈(为3);在处 理第二个实参(i++)+(i++)+(i++)时,最右 边的参数i的值先入栈(为3),然后完成自加运算使i的值 变为4;接下来中间的参数i的值入栈(为4),然后完成自加 运算使i的值变为5;接下来左边的参数i的值入栈(为5), 然后完成自加运算使i的值变为6;至此完成了实参入栈。 出栈时,按照堆栈后进先出的组织原则,分别弹出5、4、3三 个数值,求和得到12;然后弹出数值3。所以第一条prinff函 数调用语句的输出结果为: kl=12,i=3 输出k:的值时与输出k1的值时的不同点在于,第二个 实参(++i)+(++i)+(++i)被压入堆栈时,参数i 总是先完成自加运算使i的值变化,然后入栈,因此第二条 prinff函数调用语句的输出结果为: k2=15,i=3 可见先计算表达式的值与输出时计算表达式的值,在涉 及到自增(自减)运算符时,得到的结果完全不同。那么当 自增(自减)运算符遇到带负号的变量时会发生什么呢?看 下面的例子: void main() {int k=7; prinff(“%d\n”,一k++); prinff(“%d\n”,k);} 该例中首先要求输出表达式一k++的值,变量k左边 是负号运算符,右边是自加运算符。由于自加运算符的运算 优先级高于负号运算符,所以表达式相当于一(k++),根 据自加运算符的运算规则,后置运算时变量k先投入表达式 运算得到“一7”被输出,然后完成自加运算使变量的值变为 8。所以运行结果为: 一7 8 3赋值运算符的使用问题 C语言中的赋值运算符使用起来非常灵活,运算规则看 起来比较复杂,允许在不同类型数据之间混合赋值,应用不 果。再如,考虑下列程序段的运行结果: int a=4,b=2,c=8; a+=b+=c+=9: prinff(“a=%d,b=%d,c=%d\n”,a,b,c); 根据赋值运算符的运算规则,首先完成c=c+9的运算 (变量c的值被修改为17),然后完成b=b+c的运算(变量 b的值被修改为19),最后完成a=a+b的运算(变量a的值 被修改为23)。所以程序段的运行结果为: a=23,b=19,c=17 二是碰到不同类型的数据间赋值时,按存储单元的存储 形式直接传送。只要学过补码知识,这一点是不难理解的。 下面看一个有符号数传送给无符号变量的例子: main() {unsigned a; int b=一1; a=b: prinff(“%U”,a);} 运行结果为: 65535 如果b为正值,且在0~32767之间,则赋值后数值不 变。再看一个整型与字符型混合赋值运算时临界值问题的 例子,写出下面程序的运行结果。 main() {char a=127,b=128; prinff(“(a)%d,%d,%d”,a,a+1,b); prinff(“(b)%d,%d,%d”,a,a+=1,b);} 运行结果为: (a)127,128,一128 (b)一128,一128,一128 (责编:杨春雁) 参考文献: [1] 谭浩强.C程序设计[M].北京:清华大学出版社, 2001. [2] 王士元.C高级程序设计[M].北京:清华大学出版社, 1997. [3] 韩瑞峰,胡志军.高级C程序设计教程[M].太原:山 西人民出版社,2005. 维普资讯 http://www.cqvip.com

36 忻州师范学院学报 第24卷 [4] 潘金贵,等.Turbo C程序设计技术[M].南京:南京大 学出版社,1990 Distinguish for Some Puzzles in C Language Teaching WU Xing.HAN Rui—feng (Xinzhou Teachers University,Xinzhou 034000,China) Abstract:C language is provided with both high—grade and pfima ̄lngauage’S advantages.SO it has become one of the most dominant languages of today.Many users especially beginners find it is very dificult to learn C language because it relates to complex concepts and various rules and flexible application,and it is easy to make some indiscoverable mistakes.It has achieved a satisfactory teaching result to compile corresponding programs for throwing light on beginners’puzzles. Key words:Pointer;Increment(Decrease)Operator;Assignment Operators 乔 坏 . 幂 . ! 奋 譬 (上接第32页) Visualization on Heat Conduction Equation WANG Bao—・hong (Foreign Language Normal College of Taiyuan University,Taiyuan 030012,Chia)n Abstract:In the paper the problem of visualization on heat conduction equation are studied.By analyzing the heat conduction on finite olpe,the initil problaems of 2D heat conduction equation,we express the physical meaning of the equation by drawing graphs.In the heat conduction equation,some problems ale not only diffcuh to get the solution,but physical meaning is hard to understnd.I use func—a tional software(Matlab),it can make the teaching contents realize visualization on computers.With he thelp of Matlab’S numerical cal— culation and graphic process technology,we can draw space diagram of heat conduction equation SO that we can understand physical phenomena better. Key words:MATLAB;Visualization;Heat conduction equation 

因篇幅问题不能全部显示,请点此查看更多更全内容

Top