请问学PB是不是很难找工作?

852zxc 2004-02-27 10:10:06
我现在还是初手, 请问学PB是不是很难找工作?好像很多公司用的是Delphi,是吗?
...全文
19 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
目录 1、 各种技术方向分析 1 1.1、JAVA 1 1。2、。NET 1 1。3、手机端开发 2 1。4、C 2 2、 习建议 3 3、 如何习 3 4、 教程设计 4 4.1、Linux基础知识 4 4。2、C程序入门 5 4。3、C++程序入门 5 4。4、Linux系统程序编写 5 4。5、常用函数和类的封装 6 4。6、TCP/IP通讯 11 4.7、FTP编程 13 4.8、数据库知识 13 4。9、数据库编程 14 4。10、气象数据中心项目开发 16 5、 毕业总结 16 各种技术方向分析 对大部分即将进入IT行为的人来说,普遍存在一个问题,如何选择技术方向,以下谈 谈我个人的看法. 1。1、JAVA 优点 几乎可以做任何事情,适用于各种操作系统平台,在WEB应用方面有优势。 入门容易,习环境容易搭建。 缺点 入门太容易了,从事这方面的开发的人太多了. 在数据处理、传输等应用方面,性能比其它语言要低。 技术更新很快,每过几年就有新的技术和框架出来,需要不断的习。 1。2、。NET 这个不行,只能用于Windows平台,事实证明,.NET已败给了JAVA. 如果某公司招你进去,让你习.NET,这是很不负责任的行为,必定耽误你的前程。 1.3、手机端开发 手机端的开发目前有两种主流的技术,Android和IOS,目前看来,基于Android平台的 应用比IOS多很多。 如果你要习,建议选择Android,不要选择IOS,因为IOS是苹果公司独家的,如果哪一 天苹果公司不行了,估计你也行转行。 近几年,随着智能手机的普及,手机端开发非常火,开发人员短缺,跟风习的技术 员很多,这让我想起了2000年时的互联网泡沫,不知道再过几年会怎么样,会不会浮华退 去? 1.4、C C的历史有N年了,在1995年之前,计算机主要用于银行、证券、电信等大行业,那时 个,连操作界面都是用C编写的,后来的PowerBuild、VB、VC、Dephi等取代了C的界面应 用,但是,在后台应用,如传输、通讯、数据处理、监控等方面,C的地位无可取代。 优点 主要用于UNIX操作系统,在Windows系统中也有应用,但没有优势。 在传输、通讯、数据处理、监控方面有非常大的优势,效率极高。 几十年来,C语言就没怎么变过,程序员无更新换代之苦。 缺点 入门太难了,习环境难以搭建. 想精通更难,如果没有高人指点,几乎不可能. C提供的函数都只是简单的功能,但是,在实际应用中,需要程序根据自己丰富的应用 经验,预先做大量的封装工作。 C语言的应用还有嵌入式开发方向,这个我不太懂,就不说了。 C语言应用的还有Windows方向,这个没什么好说的,C语言在Windows系统下,只能做 一些小东西,重要系统的核心还是在UNIX,这一点无不容置疑。 习建议 当然选择C了。 在1995年之前,大部分程序员是做C的,30年过去了,这些人大部分都退役了,但是, 新的C程序员越来越少,这些年来,我想合适的C程序员很难,菜鸟不能用,有经验和 技术的人要价太高,没实力的公司根本请不起. 我在刚做技术的时候,什么都想搞一下,以前流行VB、PB、Dephi等,我都过,但是 ,现在VB、PB、Dephi等都已随风而去,只有C,经久不衰,做的时间越长,感觉越妙. 几十年来,C语言程序员始终是精英。 不过,C不是孤立的,还需要习UNIX操作系统和数据库系统。 如何习 人生选择方向很重要,例如字母B,往南走和往北走,意义完全不同. 这些年来,我见过很多菜鸟不到入门的方法,瞎折腾,也看过国内的培训机构的课 程,如达内,培训的内容连入门都算不上,所以,就有了想搞培训的想法. 本人1996年接触C、UNIX和Oracle数据库,2008年注册了一个皮包公司,到处奔波,折 腾项目,售前、售后、设计、编码的事都是一个人做,这些年来,也不知道做了多少个系 统,写了多少代码,这样的经验和技术水平,能不能教教各位菜鸟? 教程设计 各位菜鸟,不知道如何才能让你相信我,有个建议,请把我这个教程给有经验的C程序 员看看,从这个教程可见我的功力。 不忽悠各位了,开始教程. 4。1、Linux基础知识 课时:一星期(在本教程中,一星期不是五天,是七天。) Linux系统的优点,应用范围。 连Linux服务器的工具程序的使用(SecureCRT). Linux系统常用的命令。 telnet工具。 ftp工具。 ssh工具。 vi编辑器. Linux系统的安装、常用软件包介绍、磁盘分区方法。 熟悉Linux操作系统,具备Linux系统的安装和日常维护能力。 老师教的东西总是有限的,培养各位获取帮助和查资料的能力非常重要。 4。2、C程序入门 课时:一星期 以谭浩强的《C程序设计》为教材
STM32F103ZE I2C1调试手记近日因项目需要,使用100PIN的STM32带FSMC功能驱动3.2"TFTLCD;在使用EEPROM时发现原来的I2C程序居然无法使用,郁闷了! 先说一下我这个I2C的驱动程序是经过STM32的官方库函数整理而成,API支持全系列EEPROM以及8BIT地址的其它I2C设备,一直用的很爽。 赶紧查问题原因,发现连STA信号都无法启动完成,一直循环在等待状态while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 真是见鬼了,居然连STA信号都无法建立,首先怀疑是硬件问题,于是赶紧查硬件问题,换EEPROM,无果。奇了怪了,其它项目用的好好的程序,问百度吧?百度一下,这个问题还真多啊,而且还一模一样,问题都没结果,基本的认识是说ST芯片的问题,只能用模拟总线解决。 难道真是芯片问题?这可能吗?这可是ST啊,放眼全球有几家公司能与之抗衡啊。我首先觉得不可能,理由是为什么我其它项目用的好好的,虽说不是同PIN的芯片,可是同系列啊。于是我用其它项目的程序稍做修改,只开放IIC功能把程序下载到103ZE,运行一切正常。这就验证了我的判断,芯片不可能有问题。可为什么不行呢?难道是底层库函数的问题,于是升级库函数,移植到3.0版本的,这样捣鼓一下,一上午时间就这样没了,郁闷啊郁闷到差点绝望啊!!!先吃完中午饭,准备下午再战。再问百度吧,去还是没结果,都说是芯片问题。纠结,难不成真的要用模拟I2C总线解决。不甘心啊,土法炼钢吧,把所有初始化程序一个一个注释掉,下载测试,当注释掉LCD初始化程序时,奇迹出现了,I2C总线OK了,天啦难道是LCD造成的?查看LCD初始化程序,FSMC初始化程序,没发现问题啊,再加上LCD初始化程序,I2C又不能工作了,看来确实是LCD的功能造成,先把FSMC注释掉,运行I2C又OK了,终于到你了,就是FSMC造成的,看程序没有任何错误,还是查手册吧,在手册中看到I2C1的SDA脚与FSMC_NADV脚是共用一个端口,难道是这个引起的,查看手册《STM32F101xC/D/E 和 STM32F103xC/D/E勘误表》终于发现问题的根源。原文描述:2.7.15 FSMC和 I2C1以及TIM4_CH2问题描述如果要使用FSMC功能,NADV被配置成复用输出,该信号会被默认地置位。这样就和TIM4_CH2和I2C1的SDA信号发生冲突。暂时解决办法当使用FSMC的同时,不要使用TIM4_CH2。如果要使用I2C1并且封装允许,就把该功能重映射到PB8/PB9。2.7.13 SDIO 和经过重映射的I2C1配置条件当SDIO配置成1位或者4位模式,会和被重映射到PB8/9引脚上的I2C1发生冲突。冲突发生在以下信号之间:● I2C1_SCL和SDIO_D4● I2C1_SDA和SDIO_D5暂时解决办法当使用SDIO的同时,不要重映射I2C1的功能到PB8/PB9。问题到了,就是这个原因啊,天杀的ST,映射功能只能到PB8 PB9端口上,不能映射到任意商品上,可我的PB8 PB9端口被SDIO功能占用了。I2C2端口也被USART3功能占用了。问题是到了,可最后结果只能模拟总线来实现了。调试结论:1. ST的芯片不会有问题。ST官方的库函数是非常好用的,用的很爽的!2. 当使用大容量芯片具有FSMC功能与I2C1功能同时使用时, 需要把I2C1映射到PB9 PB9端口上去。3. 如果要同时使用FSMC I2C1 SDIO功能时,I2C1只能用模拟总线方式实现。因为SDIO功能会占用PB8 PB9端口。以下是I2C模拟总线程序源代码
适用于初第七章:结构与联合 结构类型定义和结构变量说明   在实际问题中,一组数据往往具有不同的数据类型。例如, 在生登记表中,姓名应为字符型;号可为整型或字符型; 年龄应为整型;性别应为字符型;成绩可为整型或实型。 显然不能用一个数组来存放这一组数据。 因为数组中各元素的类型和长度都必须一致,以便于编译系统处理。为了解决这个问题,C语言中给出了另一种构造数据类型——“结构”。 它相当于其它高级语言中的记录。   “结构”是一种构造类型,它是由若干“成员”组成的。 每一个成员可以是一个基本数据类型或者又是一个构造类型。 结构既是一种“构造”而成的数据类型, 那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。 一、结构的定义 定义一个结构的一般形式为: struct 结构名 { 成员表列 }; 成员表由若干个成员组成, 每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为: 类型说明符 成员名; 成员名的命名应符合标识符的书写规定。例如: struct stu { int num; char name[20]; char sex; float score; };   在这个结构定义中,结构名为stu,该结构由4个成员组成。 第一个成员为num,整型变量;第二个成员为name,字符数组;第三个成员为sex,字符变量;第四个成员为score,实型变量。 应注意在括号后的分号是不可少的。结构定义之后,即可进行变量说明。 凡说明为结构stu的变量都由上述4个成员组成。由此可见, 结构是一种复杂的数据类型,是数目固定,类型不同的若干有序变量的集合。 二、结构类型变量的说明 说明结构变量有以下三种方法。以上面定义的stu为例来加以说明。 1. 先定义结构,再说明结构变量。如: struct stu { int num; char name[20]; char sex; float score; }; struct stu boy1,boy2; 说明了两个变量boy1和boy2为stu结构类型。也可以用宏定义使一个符号常量来表示一个结构类型,例如: #define STU struct stu STU { int num; char name[20]; char sex; float score; }; STU boy1,boy2; 2. 在定义结构类型的同时说明结构变量。例如: struct stu { int num; char name[20]; char sex; float score; }boy1,boy2; 3. 直接说明结构变量。例如: struct { int num; char name[20]; char sex; float score; }boy1,boy2;   第三种方法与第二种方法的区别在于第三种方法中省去了结构名,而直接给出结构变量。三种方法中说明的boy1,boy2变量都具有图7.1所示的结构。说明了boy1,boy2变量为stu类型后,即可向这两个变量中的各个成员赋值。在上述stu结构定义中,所有的成员都是基本数据类型或数组类型。成员也可以又是一个结构, 即构成了嵌套的结构。例如,图7.2给出了另一个数据结构。 按图7.2可给出以下结构定义: struct date{ int month; int day; int year; } struct{ int num; char name[20]; char sex; struct date birthday; float score; }boy1,boy2;   首先定义一个结构date,由month(月)、day(日)、year(年) 三个成员组成。 在定义并说明变量 boy1 和 boy2 时, 其中的成员birthday被说明为data结构类型。成员名可与程序中其它变量同名,互不干扰。结构变量成员的表示方法在程序中使用结构变量时, 往往不把它作为一个整体来使用。   在ANSI C中除了允许具有相同类型的结构变量相互赋值以外, 一般对结构变量的使用,包括赋值、输入、输出、 运算等都是通过结构变量的成员来实现的。   表示结构变量成员的一般形式是: 结构变量名.成员名 例如:boy1.num 即第一个人的号 boy2.sex 即第二个人的性别 如果成员本身又是一个结构则必须逐级到最低级的成员才能使用。例如:boy1.birthday.month 即第一个人出生的月份成员可以在程序中单独使用,与普通变量完全相同。 结构变量的赋值 前面已经介绍,结构变量的赋值就是给各成员赋值。 可用输入语句或赋值语句来完成。 [例7.1]给结构变量赋值并输出其值。 main(){ struct stu { int num; char *name; char sex; float score; } boy1,boy2; boy1.num=102; boy1.name="Zhang ping"; printf("input sex and score\n"); scanf("%c %f",&boy1.sex,&boy1.score); boy2=boy1; printf("Number=%d\nName=%s\n",boy2.num,boy2.name); printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score); }   本程序中用赋值语句给num和name两个成员赋值,name是一个字符串指针变量。用scanf函数动态地输入sex和score成员值,然后把boy1的所有成员的值整体赋予boy2。最后分别输出boy2 的各个成员值。本例表示了结构变量的赋值、输入和输出的方法。 结构变量的初始化   如果结构变量是全局变量或为静态变量, 则可对它作初始化赋值。对局部或自动结构变量不能作初始化赋值。 [例7.2]外部结构变量初始化。 struct stu /*定义结构*/ { int num; char *name; char sex; float score; } boy2,boy1={102,"Zhang ping",'M',78.5}; main() { boy2=boy1; printf("Number=%d\nName=%s\n",boy2.num,boy2.name); printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score); } struct stu { int num; char *name; char sex; float score; }boy2,boy1={102,"Zhang ping",'M',78.5}; main() { boy2=boy1; …… } 本例中,boy2,boy1均被定义为外部结构变量,并对boy1作了初始化赋值。在main函数中,把boy1的值整体赋予boy2, 然后用两个printf语句输出boy2各成员的值。 [例7.3]静态结构变量初始化。 main() { static struct stu /*定义静态结构变量*/ { int num; char *name; char sex; float score; }boy2,boy1={102,"Zhang ping",'M',78.5}; boy2=boy1; printf("Number=%d\nName=%s\n",boy2.num,boy2.name); printf("Sex=%c\nScore=%f\n",boy2.sex,boy2.score); } static struct stu { int num; char *name; char sex; float score; }boy2,boy1={102,"Zhang ping",'M',78.5};   本例是把boy1,boy2都定义为静态局部的结构变量, 同样可以作初始化赋值。 结构数组 数组的元素也可以是结构类型的。 因此可以构成结构型数组。结构数组的每一个元素都是具有相同结构类型的下标结构变量。 在实际应用中,经常用结构数组来表示具有相同数据结构的一个群体。如一个班的生档案,一个车间职工的工资表等。 结构数组的定义方法和结构变量相似,只需说明它为数组类型即可。例如: struct stu { int num; char *name; char sex; float score; }boy[5]; 定义了一个结构数组boy1,共有5个元素,boy[0]~boy[4]。每个数组元素都具有struct stu的结构形式。 对外部结构数组或静态结构数组可以作初始化赋值,例如: struct stu { int num; char *name; char sex; float score; }boy[5]={ {101,"Li ping","M",45}, {102,"Zhang ping","M",62.5}, {103,"He fang","F",92.5}, {104,"Cheng ling","F",87}, {105,"Wang ming","M",58}; } 当对全部元素作初始化赋值时,也可不给出数组长度。 [例7.4]计算生的平均成绩和不及格的人数。 struct stu { int num; char *name; char sex; float score; }boy[5]={ {101,"Li ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58}, }; main() { int i,c=0; float ave,s=0; for(i=0;i<5;i++) { s+=boy[i].score; if(boy[i].score<60) c+=1; } printf("s=%f\n",s); ave=s/5; printf("average=%f\ncount=%d\n",ave,c); } 本例程序中定义了一个外部结构数组boy,共5个元素, 并作了初始化赋值。在main函数中用for语句逐个累加各元素的score 成员值存于s之中,如score的值小于60(不及格)即计数器C加1, 循环完毕后计算平均成绩,并输出全班总分,平均分及不及格人数。 [例7.5]建立同通讯录 #include"stdio.h" #define NUM 3 struct mem { char name[20]; char phone[10]; }; main() { struct mem man[NUM]; int i; for(i=0;i成员名 例如: (*pstu).num或者: pstu->num 应该注意(*pstu)两侧的括号不可少, 因为成员符“.”的优先级高于“*”。如去掉括号写作*pstu.num则等效于*(pstu.num),这样,意义就完全不对了。 下面通过例子来说明结构指针变量的具体说明和使用方法。 [例7.6] struct stu { int num; char *name; char sex; float score; } boy1={102,"Zhang ping",'M',78.5},*pstu; main() { pstu=&boy1; printf("Number=%d\nName=%s\n",boy1.num,boy1.name); printf("Sex=%c\nScore=%f\n\n",boy1.sex,boy1.score); printf("Number=%d\nName=%s\n",(*pstu).num,(*pstu).name); printf("Sex=%c\nScore=%f\n\n",(*pstu).sex,(*pstu).score); printf("Number=%d\nName=%s\n",pstu->num,pstu->name); printf("Sex=%c\nScore=%f\n\n",pstu->sex,pstu->score); }   本例程序定义了一个结构stu,定义了stu类型结构变量boy1 并作了初始化赋值,还定义了一个指向stu类型结构的指针变量pstu。在main函数中,pstu被赋予boy1的地址,因此pstu指向boy1 。然后在printf语句内用三种形式输出boy1的各个成员值。 从运行结果可以看出: 结构变量.成员名 (*结构指针变量).成员名 结构指针变量->成员名   这三种用于表示结构成员的形式是完全等效的。结构数组指针变量结构指针变量可以指向一个结构数组, 这时结构指针变量的值是整个结构数组的首地址。 结构指针变量也可指向结构数组的一个元素,这时结构指针变量的值是该结构数组元素的首地址。设ps为指向结构数组的指针变量,则ps也指向该结构数组的0号元素,ps+1指向1号元素,ps+i则指向i号元素。 这与普通数组的情况是一致的。 [例7.7]用指针变量输出结构数组。 struct stu { int num; char *name; char sex; float score; }boy[5]={ {101,"Zhou ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"Liou fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58}, }; main() { struct stu *ps; printf("No\tName\t\t\tSex\tScore\t\n"); for(ps=boy;psnum,ps->name,ps->sex,ps-> score); }   在程序中,定义了stu结构类型的外部数组boy 并作了初始化赋值。在main函数内定义ps为指向stu类型的指针。在循环语句for的表达式1中,ps被赋予boy的首地址,然后循环5次,输出boy数组中各成员值。 应该注意的是, 一个结构指针变量虽然可以用来访问结构变量或结构数组元素的成员,但是,不能使它指向一个成员。 也就是说不允许取一个成员的地址来赋予它。因此,下面的赋值是错误的。 ps=&boy[1].sex;而只能是:ps=boy;(赋予数组首地址) 或者是: ps=&boy[0];(赋予0号元素首地址) 结构指针变量作函数参数   在ANSI C标准中允许用结构变量作函数参数进行整体传送。 但是这种传送要将全部成员逐个传送, 特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。 因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。 这时由实参传向形参的只是地址,从而减少了时间和空间的开销。 [例7.8]题目与例7.4相同,计算一组生的平均成绩和不及格人数。 用结构指针变量作函数参数编程。 struct stu { int num; char *name; char sex; float score;}boy[5]={ {101,"Li ping",'M',45}, {102,"Zhang ping",'M',62.5}, {103,"He fang",'F',92.5}, {104,"Cheng ling",'F',87}, {105,"Wang ming",'M',58}, }; main() { struct stu *ps; void ave(struct stu *ps); ps=boy; ave(ps); } void ave(struct stu *ps) { int c=0,i; float ave,s=0; for(i=0;i<5;i++,ps++) { s+=ps->score; if(ps->score<60) c+=1; } printf("s=%f\n",s); ave=s/5; printf("average=%f\ncount=%d\n",ave,c); }   本程序中定义了函数ave,其形参为结构指针变量ps。boy 被定义为外部结构数组,因此在整个源程序中有效。在main 函数中定义说明了结构指针变量ps,并把boy的首地址赋予它,使ps指向boy 数组。然后以ps作实参调用函数ave。在函数ave 中完成计算平均成绩和统计不及格人数的工作并输出结果。与例7.4程序相比,由于本程序全部采用指针变量作运算和处理,故速度更快,程序效率更高。. topoic=动态存储分配   在数组一章中,曾介绍过数组的长度是预先定义好的, 在整个程序中固定不变。C语言中不允许动态数组类型。例如: int n;scanf("%d",&n);int a[n]; 用变量表示长度,想对数组的大小作动态说明, 这是错误的。但是在实际的编程中,往往会发生这种情况, 即所需的内存空间取决于实际输入的数据,而无法预先确定。对于这种问题, 用数组的办法很难解决。为了解决上述问题,C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间, 也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。 常用的内存管理函数有以下三个: 1.分配内存空间函数malloc 调用形式: (类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区域。函数的返回值为该区域的首地址。 “类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。例如: pc=(char *) malloc (100); 表示分配100个字节的内存空间,并强制转换为字符数组类型, 函数的返回值为指向该字符数组的指针, 把该指针赋予指针变量pc。 2.分配内存空间函数 calloc calloc 也用于分配内存空间。调用形式: (类型说明符*)calloc(n,size) 功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。(类型说明符*)用于强制类型转换。calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的结构长度。因此该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。 3.释放内存空间函数free 调用形式: free(void*ptr); 功能:释放ptr所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域:[例7.9]分配一块区域,输入一个生数据。 main() { struct stu { int num; char *name; char sex; float score; } *ps; ps=(struct stu*)malloc(sizeof(struct stu)); ps->num=102; ps->name="Zhang ping"; ps->sex='M'; ps->score=62.5; printf("Number=%d\nName=%s\n",ps->num,ps->name); printf("Sex=%c\nScore=%f\n",ps->sex,ps->score); free(ps); }   本例中,定义了结构stu,定义了stu类型指针变量ps。 然后分配一块stu大内存区,并把首地址赋予ps,使ps指向该区域。再以ps为指向结构的指针变量对各成员赋值,并用printf 输出各成员值。最后用free函数释放ps指向的内存空间。 整个程序包含了申请内存空间、使用内存空间、释放内存空间三个步骤, 实现存储空间的动态分配。链表的概念在例7.9中采用了动态分配的办法为一个结构分配内存空间。每一次分配一块空间可用来存放一个生的数据, 我们可称之为一个结点。有多少个生就应该申请分配多少块内存空间, 也就是说要建立多少个结点。当然用结构数组也可以完成上述工作, 但如果预先不能准确把握生人数,也就无法确定数组大小。 而且当生留级、退之后也不能把该元素占用的空间从数组中释放出来。 用动态存储的方法可以很好地解决这些问题。 有一个生就分配一个结点,无须预先确定生的准确人数,某生退, 可删去该结点,并释放该结点占用的存储空间。从而节约了宝贵的内存资源。 另一方面,用数组的方法必须占用一块连续的内存区域。 而使用动态分配时,每个结点之间可以是不连续的(结点内是连续的)。 结点之间的联系可以用指针实现。 即在结点结构中定义一个成员项用来存放下一结点的首地址,这个用于存放地址的成员,常把它称为指针域。可在第一个结点的指针域内存入第二个结点的首地址, 在第二个结点的指针域内又存放第三个结点的首地址, 如此串连下去直到最后一个结点。最后一个结点因无后续结点连接,其指针域可赋为0。这样一种连接方式,在数据结构中称为“链表”。图7.3为链表的示意图。   在图7.3中,第0个结点称为头结点, 它存放有第一个结点的首地址,它没有数据,只是一个指针变量。 以下的每个结点都分为两个域,一个是数据域,存放各种实际的数据,如号num,姓名name,性别sex和成绩score等。另一个域为指针域, 存放下一结点的首地址。链表中的每一个结点都是同一种结构类型。例如, 一个存放号和成绩的结点应为以下结构: struct stu { int num; int score; struct stu *next; }   前两个成员项组成数据域,后一个成员项next构成指针域, 它是一个指向stu类型结构的指针变量。链表的基本操作对链表的主要操作有以下几种: 1.建立链表; 2.结构的查与输出; 3.插入一个结点; 4.删除一个结点; 下面通过例题来说明这些操作。 [例7.10]建立一个三个结点的链表,存放生数据。 为简单起见, 我们假定生数据结构中只有号和年龄两项。 可编写一个建立链表的函数creat。程序如下: #define NULL 0 #define TYPE struct stu #define LEN sizeof (struct stu) struct stu { int num; int age; struct stu *next; }; TYPE *creat(int n) { struct stu *head,*pf,*pb; int i; for(i=0;ipb=(TYPE*) malloc(LEN); printf("input Number and Age\n"); scanf("%d%d",&pb->num,&pb->age); if(i==0) pf=head=pb; else pf->next=pb; pb->next=NULL; pf=pb; } return(head); }   在函数外首先用宏定义对三个符号常量作了定义。这里用TYPE表示struct stu,用LEN表示sizeof(struct stu)主要的目的是为了在以下程序内减少书写并使阅读更加方便。结构stu定义为外部类型,程序中的各个函数均可使用该定义。   creat函数用于建立一个有n个结点的链表,它是一个指针函数,它返回的指针指向stu结构。在creat函数内定义了三个stu结构的指针变量。head为头指针,pf 为指向两相邻结点的前一结点的指针变量。pb为后一结点的指针变量。在for语句内,用malloc函数建立长度与stu长度相等的空间作为一结点,首地址赋予pb。然后输入结点数据。如果当前结点为第一结点(i==0),则把pb值 (该结点指针)赋予head和pf。如非第一结点,则把pb值赋予pf 所指结点的指针域成员next。而pb所指结点为当前的最后结点,其指针域赋NULL。 再把pb值赋予pf以作下一次循环准备。   creat函数的形参n,表示所建链表的结点数,作为for语句的循环次数。图7.4表示了creat函数的执行过程。 [例7.11]写一个函数,在链表中按号查该结点。 TYPE * search (TYPE *head,int n) { TYPE *p; int i; p=head; while (p->num!=n && p->next!=NULL) p=p->next; /* 不是要的结点后移一步*/ if (p->num==n) return (p); if (p->num!=n&& p->next==NULL) printf ("Node %d has not been found!\n",n }   本函数中使用的符号常量TYPE与例7.10的宏定义相同,等于struct stu。函数有两个形参,head是指向链表的指针变量,n为要查号。进入while语句,逐个检查结点的num成员是否等于n,如果不等于n且指针域不等于NULL(不是最后结点)则后移一个结点,继续循环。如到该结点则返回结点指针。 如循环结束仍未到该结点则输出“未到”的提示信息。 [例7.12]写一个函数,删除链表中的指定结点。删除一个结点有两种情况: 1. 被删除结点是第一个结点。这种情况只需使head指向第二个结点即可。即head=pb->next。其过程如图7.5所示。 2. 被删结点不是第一个结点,这种情况使被删结点的前一结点指向被删结点的后一结点即可。即pf->next=pb->next。其过程如图7.6所示。 函数编程如下: TYPE * delete(TYPE * head,int num) { TYPE *pf,*pb; if(head==NULL) /*如为空表, 输出提示信息*/ { printf("\nempty list!\n"); goto end;} pb=head; while (pb->num!=num && pb->next!=NULL) /*当不是要删除的结点,而且也不是最后一个结点时,继续循环*/ {pf=pb;pb=pb->next;}/*pf指向当前结点,pb指向下一结点*/ if(pb->num==num) {if(pb==head) head=pb->next; /*如到被删结点,且为第一结点,则使head指向第二个结点, 否则使pf所指结点的指针指向下一结点*/ else pf->next=pb->next; free(pb); printf("The node is deleted\n");} else printf("The node not been foud!\n"); end: return head; }   函数有两个形参,head为指向链表第一结点的指针变量,num删结点的号。 首先判断链表是否为空,为空则不可能有被删结点。若不为空,则使pb指针指向链表的第一个结点。进入while语句后逐个查被删结点。到被删结点之后再看是否为第一结点,若是则使head指向第二结点(即把第一结点从链中删去),否则使被删结点的前一结点(pf所指)指向被删结点的后一结点(被删结点的指针域所指)。如若循环结束未到要删的结点, 则输出“末到”的提示信息。最后返回head值。 [例7.13]写一个函数,在链表中指定位置插入一个结点。在一个链表的指定位置插入结点, 要求链表本身必须是已按某种规律排好序的。例如,在生数据链表中, 要求号顺序插入一个结点。设被插结点的指针为pi。 可在三种不同情况下插入。 1. 原表是空表,只需使head指向被插结点即可。见图7.7(a) 2. 被插结点值最小,应插入第一结点之前。这种情况下使head指向被插结点,被插结点的指针域指向原来的第一结点则可。即:pi->next=pb; head=pi; 见图7.7(b) 3. 在其它位置插入,见图7.7(c)。这种情况下,使插入位置的前一结点的指针域指向被插结点,使被插结点的指针域指向插入位置的后一结点。即为:pi->next=pb;pf->next=pi; 4. 在表末插入,见图7.7(d)。这种情况下使原表末结点指针域指向被插结点,被插结点指针域置为NULL。即: pb->next=pi; pi->next=NULL; TYPE * insert(TYPE * head,TYPE *pi) { TYPE *pf,*pb; pb=head; if(head==NULL) /*空表插入*/ (head=pi; pi->next=NULL;} else { while((pi->num>pb->num)&&(pb->next!=NULL)) {pf=pb; pb=pb->next; }/*插入位置*/ if(pi->num<=pb->num) {if(head==pb)head=pi;/*在第一结点之前插入*/ else pf->next=pi;/*在其它位置插入*/ pi->next=pb; } else {pb->next=pi; pi->next=NULL;} /*在表末插入*/ } return head;}   本函数有两个形参均为指针变量,head指向链表,pi 指向被插结点。函数中首先判断链表是否为空,为空则使head指向被插结点。表若不空,则用while语句循环查插入位置。到之后再判断是否在第一结点之前插入,若是则使head 指向被插结点被插结点指针域指向原第一结点,否则在其它位置插入, 若插入的结点大于表中所有结点,则在表末插入。本函数返回一个指针, 是链表的头指针。 当插入的位置在第一个结点之前时, 插入的新结点成为链表的第一个结点,因此head的值也有了改变, 故需要把这个指针返回主调函数。 [例7.14]将以上建立链表,删除结点,插入结点的函数组织在一起,再建一个输出全部结点的函数,然后用main函数调用它们。 #define NULL 0 #define TYPE struct stu #define LEN sizeof(struct stu) struct stu { int num; int age; struct stu *next; }; TYPE * creat(int n) { struct stu *head,*pf,*pb; int i; for(i=0;ipb=(TYPE *)malloc(LEN); printf("input Number and Age\n"); scanf("%d%d",&pb->num,&pb->age); if(i==0) pf=head=pb; else pf->next=pb; pb->next=NULL; pf=pb; } return(head); } TYPE * delete(TYPE * head,int num) { TYPE *pf,*pb; if(head==NULL) { printf("\nempty list!\n"); goto end;} pb=head; while (pb->num!=num && pb->next!=NULL) {pf=pb;pb=pb->next;} if(pb->num==num) { if(pb==head) head=pb->next; else pf->next=pb->next; printf("The node is deleted\n"); } else free(pb); printf("The node not been found!\n"); end: return head; } TYPE * insert(TYPE * head,TYPE * pi) { TYPE *pb ,*pf; pb=head; if(head==NULL) { head=pi; pi->next=NULL; } else { while((pi->num>pb->num)&&(pb->next!=NULL)) { pf=pb; pb=pb->next; } if(pi->num<=pb->num) { if(head==pb) head=pi; else pf->next=pi; pi->next=pb; } else { pb->next=pi; pi->next=NULL; } } return head; } void print(TYPE * head) { printf("Number\t\tAge\n"); while(head!=NULL) { printf("%d\t\t%d\n",head->num,head->age); head=head->next; } } main() { TYPE * head,*pnum; int n,num; printf("input number of node: "); scanf("%d",&n); head=creat(n); print(head); printf("Input the deleted number: "); scanf("%d",&num); head=delete(head,num); print(head); printf("Input the inserted number and age: "); pnum=(TYPE *)malloc(LEN); scanf("%d%d",&pnum->num,&pnum->age); head=insert(head,pnum); print(head); }   本例中,print函数用于输出链表中各个结点数据域值。函数的形参head的初值指向链表第一个结点。在while语句中,输出结点值后,head值被改变,指向下一结点。若保留头指针head, 则应另设一个指针变量,把head值赋予它,再用它来替代head。在main函数中,n为建立结点的数目, num为待删结点的数据域值;head为指向链表的头指针,pnum为指向待插结点的指针。 main函数中各行的意义是: 第六行输入所建链表的结点数; 第七行调creat函数建立链表并把头指针返回给head; 第八行调print函数输出链表; 第十行输入待删结点的号; 第十一行调delete函数删除一个结点; 第十二行调print函数输出链表; 第十四行调malloc函数分配一个结点的内存空间, 并把其地址赋予pnum; 第十五行输入待插入结点的数据域值; 第十六行调insert函数插入pnum所指的结点; 第十七行再次调print函数输出链表。   从运行结果看,首先建立起3个结点的链表,并输出其值;再删103号结点,只剩下105,108号结点;又输入106号结点数据, 插入后链表中的结点为105,106,108。联合“联合”也是一种构造类型的数据结构。 在一个“联合”内可以定义多种不同的数据类型, 一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据。 这在前面的各种数据类型中都是办不到的。例如, 定义为整型的变量只能装入整型数据,定义为实型的变量只能赋予实型数据。   在实际问题中有很多这样的例子。 例如在校的教师和生中填写以下表格: 姓 名 年 龄 职 业 单位 “职业”一项可分为“教师”和“生”两类。 对“单位”一项生应填入班级编号,教师应填入某系某教研室。 班级可用整型量表示,教研室只能用字符类型。 要求把这两种类型不同的数据都填入“单位”这个变量中, 就必须把“单位”定义为包含整型和字符型数组这两种类型的“联合”。   “联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。如前面介绍的“单位”变量, 如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后, 才能把变量说明为该联合类型。 一、联合的定义 定义一个联合类型的一般形式为: union 联合名 { 成员表 }; 成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。 例如: union perdata { int class; char office[10]; };   定义了一个名为perdata的联合类型,它含有两个成员,一个为整型,成员名为class;另一个为字符数组,数组名为office。联合定义之后,即可进行联合变量说明,被说明为perdata类型的变量,可以存放整型量class或存放字符数组office。 二、联合变量的说明   联合变量的说明和结构变量的说明方式相同, 也有三种形式。即先定义,再说明;定义同时说明和直接说明。以perdata类型为例,说明如下: union perdata { int class; char officae[10]; }; union perdata a,b; /*说明a,b为perdata类型*/ 或者可同时说明为: union perdata { int class; char office[10]; }a,b;或直接说明为: union { int class; char office[10]; }a,b 经说明后的a,b变量均为perdata类型。 它们的内存分配示意图如图7—8所示。a,b变量的长度应等于 perdata 的成员中最长的长度, 即等于 office数组的长度,共10个字节。从图中可见,a,b变量如赋予整型值时,只使用了2个字节,而赋予字符数组时,可用10个字节。 联合变量的赋值和使用   对联合变量的赋值,使用都只能是对变量的成员进行。 联合变量的成员表示为: 联合变量名.成员名 例如,a被说明为perdata类型的变量之后,可使用 a.class a.office 不允许只用联合变量名作赋值或其它操作。 也不允许对联合变量作初始化赋值,赋值只能在程序中进行。还要再强调说明的是,一个联合变量, 每次只能赋予一个成员值。换句话说,一个联合变量的值就是联合变员的某一个成员值。 [例7.15]设有一个教师与生通用的表格,教师数据有姓名,年龄,职业,教研室四项。生有姓名,年龄,职业,班级四项。 编程输入人员数据, 再以表格输出。 main() { struct { char name[10]; int age; char job; union { int class; char office[10]; } depa; }body[2]; int n,i; for(i=0;i<2;i++) { printf("input name,age,job and department\n"); scanf("%s %d %c",body[i].name,&body[i].age,&body[i].job); if(body[i].job=='s') scanf("%d",&body[i].depa.class); else scanf("%s",body[i].depa.office); } printf("name\tage job class/office\n"); for(i=0;i<2;i++) { if(body[i].job=='s') printf("%s\t%3d %3c %d\n",body[i].name,body[i].age ,body[i].job,body[i].depa.class); else printf("%s\t%3d %3c %s\n",body[i].name,body[i].age, body[i].job,body[i].depa.office); } }   本例程序用一个结构数组body来存放人员数据, 该结构共有四个成员。其中成员项depa是一个联合类型, 这个联合又由两个成员组成,一个为整型量class,一个为字符数组office。在程序的第一个for语句中,输入人员的各项数据,先输入结构的前三个成员name,age和job,然后判别job成员项,如为"s"则对联合depa?class输入(对生赋班级编号)否则对depa?office输入(对教师赋教研组名)。   在用scanf语句输入时要注意,凡为数组类型的成员,无论是结构成员还是联合成员,在该项前不能再加"&"运算符。如程序第18行中 body[i].name是一个数组类型,第22行中的body[i].depa.office也是数组类型,因此在这两项之间不能加"&"运算符。程序中的第二个for语句用于输出各成员项的值: 本章小结 1. 结构和联合是两种构造类型数据,是用户定义新数据类型的重要手段。结构和联合有很多的相似之处,它们都由成员组成。成员可以具有不同的数据类型。成员的表示方法相同。都可用三种方式作变量说明。 2. 在结构中,各成员都占有自己的内存空间,它们是同时存在的。一个结构变量的总长度等于所有成员长度之和。在联合中,所有成员不能同时占用它的内存空间,它们不能同时存在。联合变量的长度等于最长的成员的长度。 3. “.”是成员运算符,可用它表示成员项,成员还可用“->”运算符来表示。 4. 结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而联合变量不能作为函数参数,函数也不能返回指向联合的指针变量。但可以使用指向联合变量的指针,也可使用联合数组。 5. 结构定义允许嵌套,结构中也可用联合作为成员,形成结构和联合的嵌套。 6. 链表是一种重要的数据结构,它便于实现动态的存储分配。本章介绍是单向链表,还可组成双向链表,循环链表等。 资料收集:beck Copyright 2002 www.vcok.com, All Rights Reserved

398

社区成员

发帖
与我相关
我的任务
社区描述
PowerBuilder 非技术版
社区管理员
  • 非技术版社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧