定义Web2.0是困难的,所以就产生了很多混乱。有人从“Web”的角度质疑,有人从“2.0”的角度争论。最终这个问题变得像“什么是blog”一样,鸡一嘴鸭一嘴,公说公有理,婆说婆有理,越争越乱。所以Chris Anderson说,blog圈子的第一原则就是不要去概括blog圈子。他认为,Web 2.0缺乏清晰的定义正好是它的一个特性,而不是一个bug。
Tim O'Reilly仍然试图解释,什么是Web 2.0。不过他换了一种方式,用例子来解释,而不是用定义,并通过具体的例子,引申出某些Web 2.0原则。关键问题在于,Web 2.0不是一种技术升级,而是一种观念升级。下面是Tim绘制的一张图,就像太阳系一样,Web 2.0有着核心的原则,也有一些离核心或远或近的具体实践。

在Tim看来,Web 2.0最核心的观念就是,“Web作为平台”。我想,其意义大概就像1980年代操作系统作为平台一样。与操作系统平台不同的是,任何人不拥有这个平台,Web平台是通过分享或参与体系搭建起来的,是对“集体智慧”的充分理解和利用,而不再是大企业的资源垄断。
Web 1.0 -->Web 2.0
DoubleClick-->Google AdSense
Ofoto-->Flickr
Akamai-->BitTorrent
mp3.com-->Napster
不列颠百科全书在线-->Wikipedia
个人网站-->blogging
evite-->upcoming.org and EVDB
域名投机-->搜索引擎优化
页面浏览量-->每点击成本
screen scraping-->web services
出版-->参与
内容管理系统(CMS)-->wikis
目录(分类法)-->tagging ("民间分类")
粘性-->内容聚合
Tim分别对Web 1.0和Web 2.0时代的代表性企业进行了对比,如Netscape和Google、DoubleClick和Overture与AdSense、Akamai和BitTorrent等。他总结的Web 2.0的七大特性包括:Web作为平台、借力集体智慧、数据成为下一个Intel Inside、软件发行周期的终结、轻量级编程模式、超越单一设备层次的软件、富用户体验。他进而提出了八项Web 2.0设计范式:长尾、数据成为下一个Intel Inside、用户增添价值、网络作用作为默认值、保留部分权利、永远的beta、合作而不是控制、软件超越单一设备层次。
如果说互联网是通过大幅度降低信息获取的门槛,发掘出传统经济中被忽视的价值的话,那么Web 2.0则是借助用户参与体系,将上一代网络公司忽视的长尾的价值发掘出来。Tim说,借助消费者自服务和基于算法的数据管理,服务延伸至整个web——抵达边缘而不仅仅是中心,抵达长尾而不仅仅是头部。
Tangos也列出了他所理解的Web 2.0的四个核心特性:可重用的微内容、以用户(人)为中心,而不是以物为中心、社会性、用户参与的架构。实际上后三个特性应该算是一个特性,即开放的参与架构。归根结底,这是一种观念。观念的改变,有时候要远远难于技术的升级。
Undersound认为:“其实Web2.0在某种程度上不依赖于技术,而是一种态度:用户体验,资源共享,集体思维,平台开放。这些不是你做不到,而是你没有想到而已。”其实不完全如此,因为Web 2.0对用户、对整个互联网产业是好消息,但对某些互联网公司,却是坏消息。比如不列颠百科全书在线就一定不喜欢Wikipedia,收费的在线广告也一定不喜欢免费的Craigslist。O'Reilly Media的编辑Nathan Torkington就说过,Web 2.0在某种程度上很像日本的合气道,即用对手的力量战胜他们。因此,对有些人来说,Web 2.0确实是个坏消息。
Web 2.0的原则之一就是,与其相信自己的所谓优势,不如相信你的用户。本质上,这也是自由软件运动的原则之一。我常常觉得,Web 2.0的实践者们一定是从自由软件运动中得到了某种启示和灵感。相信集体智慧,相信开放的开发模式,相信参与者越多质量越好等等,这些Web 2.0的原则,恰好也都是自由软件所崇尚的。因此我想,Web 2.0如果也算是一股潮流的话,那么它应该是自由Web潮流,或者叫Web的开放源代码。微软无法接受自由软件,是因为Windows、Office至今仍是它的核心竞争力和命脉所在。
因此可以说,Web 2.0是互联网原有商业规则的破坏者。贴着Web 2.0的标签,干着Web 1.0的事儿,这是投机。商业可以投机,但观念无法投机。看看他是想垄断还是想开放,想控制还是想合作,一切就一清二白了。
Tim列出了七条他认为的Web 2.0公司的核心能力:
有着可测量成本效益的服务,而非软件包(Services, not packaged software, with cost-effective scalability);
控制独有的、难以再造的数据源,这些数据源使用的人越多就越丰富(Control over unique, hard-to-recreate data sources that get richer as more people use them);
把用户当成合作开发者一般信任(Trusting users as co-developers);
借力于集体智慧(Harnessing collective intelligence);
从消费者自服务中产生长尾优势(Leveraging the long tail through customer self-service);
超越单一设备层次的软件(Software above the level of a single device);
轻量级的用户界面、开发模式和商业模式(Lightweight user interfaces, development models, AND business models)。
如果你能连上网络,那么世界上最大的FTP服务器就是一台跑着FreeBSD的PC,配备着Pentium
Pro 200MHZ,512G的硬盘,1G的内存, 同时可以让3500人download的PC服务器。很多上网的人都知道著名的Yahoo和hotmail这两个站点,但是你知道这些站点的背后是用什么服务器构成的吗?答案令人吃惊,yahoo和hotmail都是在使用FreeBSD的操作系统来构成他们的服务。当然yahoo和hotmail这么大系统的背后一定不只是一台PC罗,目前hotmail服务2000万的用户,用了五百多台FreeBSD的服务器。
现在有许多人在PC上运行免费的Unix-like的操作系统,或许有人已经在用Linux了,那究竟Linux,FreeBSD、以及其他的免费操作系统有什么不同呢?这个我们将放在下一篇中告诉你。
从Linux到FreeBSD
其实玩操作系统最先开始于大学毕业,当时我记得给我印象最深刻的是93年刚出来的WIN NT3.1,它的多任务性让我爱不释手,我甚至认为NT就是未来的操作系统。但是1年后我的观点发生了动摇,在一次偶然的机会里接触了Sco Unix和Linux。Unix里面丰富的工具,稳定的性能和优秀的多任务性,似乎让人真正认识什么是稳定的多任务的操作系统。
就在我沉迷于slackware 2.0的时候,另一个免费操作系统FreeBSD正在以不可思议的速度四处蔓延,成为许多网站的服务器。那时候我记得yahoo的50台服务器都是用了FreeBSD。当然ftp.cdrom.com早已经用了。
究竟为什么FreeBSD具有如此巨大的魅力呢?这个问题恐怕要等你亲自用了FreeBSD之后才会揭晓。
FreeBSD的特色
简单的来说,FreeBSD是一个在PC上运行的免费Unix系统,FreeBSD让你的PC变成一台先进的强大的。
安装容易
FreeBSD相当容易安装,彩色的选单式安装画面让你轻松的安装,当然你也可以通过FTP,NFS,CDROM来安装了。
完全免费
FreeBSD不但是完全免费,而且还提供100%的完整操作系统源程序码(source code),让你可以随心所欲的控制已经修改你的系统,让你避开为操作系统公司操作的命运。
先进的多任务性
目前网易的个人主页服务器就是用了FreeBSD,你看看就明白了。
优越的网络性能
FreeBSD非常适合担任Internet的网络服务器,提供完整的TCP/IP的网络功能,包括SLIP,PPP,NFS,NIS以及其他的协议。目前FreeBSD上可以稳定运行的网络应用系统有:www,email,FTP,NFS,firewall,BBS,Routing,News,Proxy
完整的32位操作系统
FreeBSD可以根据你的CPU的特性,打造出一个合适的kernel .目前对于SMP的主机FreeBSD比单CPU可以提高40%的运行效率。
系统安全性高
FreeBSD不但拥有高度的系统安全性,还拥有特殊的登入控制和文件保护,可以保障你的系统和资料不被破坏。
此外,还有许多的高手专门研究各种安全性问题,一旦发现任何的漏洞便会立刻修复,使用者也可以利用source code可以更新,不需要等待新的版本。
讲了那么多,写这片文字的目的就是想告诉你,在你计划花费数万元去购买那些昂贵的服务器的时候,不防先尝尝这个不花费一毛钱的好东西。
FreeBSD站点:
http://www.freebsd.org
FreeBSD在国内应用的主要站点有:
网易的个人主页服务器
163.net免费邮件系统(用了4台)
网易的中文搜索引擎(2台)
北京263.net的免费邮件系统
国外的应用:
www.hotmail.com
www.yahoo.com
还有其他的更多...
FreeBSD国内下载点:
网易下载
名家谈网目录
从BBS到虚拟社区
PC不只是很便宜的工作站
现在就是感觉很爽,现在用的就是它了,当然你也可以用Red hat fedora As4 suse等。。。。。。,开始你会发现它有些不好用,当你用多了,你就会知道它的安全感是多么的好,也不必为你的杀毒软件是否搞好了,升级了,那就是一个用它就是让你感觉不会有什么担心有一天背叛了你,染上了什么病毒。。。。哈哈。。。。
这是我本人的真的感受,当然,罗卜青菜各有爱,我是一个害怕麻烦的人当然不想老是升级病毒库阿,什么的。就知道能干点事情。。。所以,仅代表个人观点。。。。。
[root@localhost bin]# ./startup.sh
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JAVA_HOME: /usr/local/jdk1.6.0
touch: cannot touch ‘/usr/local/tomcat/logs/catalina.out’: 没有那个文件或目录
/usr/local/tomcat/bin/catalina.sh: line 233: /usr/local/tomcat/logs/catalina.out: 没有那个文件或目录
游人提出:
catalina.sh里添加:
JAVA_HOME="/usr/java/j2sdk1.4.2_02"
CATALINA_HOME="/usr/local/tomcat"
expert JAVA_HOME
expert CATALINA_HOME
然后执行:
#usr/local/tomcat/bin/catalina.sh start
看有什么提示
我的tomcat配置成功了,而且已经将它作为开机启动服务,使用JDBC驱动链接mysql数据库也成功了,详细过程有些快忘记了,我可以查一下,然后告诉你。
我使用一下:
[root@localhost bin]# ./startup.sh
/usr/local/tomcat/bin/catalina.sh: line 45: expert: command not found
/usr/local/tomcat/bin/catalina.sh: line 46: expert: command not found
我就去掉添加的两行,然后在它要的目录下建立了这个文件夹和文件即可,然后8080端口就通了 .......,.文件权限不用设置)
[1]关于操作系统的选择
PHP在Linux/FreeBSD下面的性能比在Windows主机下的性能更好。如果您没有必须使用Windows主机的理由(比如需要使用ASP.net或者SQL Server或者Windows Media Server或者Serv-U),那么我推荐您使用Linux/FreeBSD。
如果您需要使用Windows,推荐使用Windows 2003 32位企业版,不推荐使用Windows 2000(性能较差)。
如果您需要使用Linux,推荐使用RedHat Enterprise Linux(最新版本是RedHat Enterprise Linux 4 beta1)。
如果您需要使用FreeBSD,推荐使用最新版本FreeBSD 5.2.1。

一、教你一招:使用IE不能打开新窗口的解决办法
最近发现我的IE不能打开新窗口,具体表现形式是:用鼠标左键点击超链接没有反应,用鼠标右键点击超链接,在弹出的菜单中选择“在新窗口打开”也没有动静。怎么办呢?经过查找试验,终于找到了解决方法:
1、在“开始”菜单中打开“运行”窗口,在其中输入“regsvr32 actxprxy.dll”,然后“确定”,接着会出现一个信息对话框“DllRegisterServer in actxprxy.dll succeeded”,再次点击“确定”。
2、再次打开“运行”窗口,输入“regsvr32 shdocvw.dll”,“确定”后在出现的信息对话框中点击“确定”。
3、重新启动Windows,运行IE,随便打开一个网页,点击一个超链接,你会发现IE又能打开新窗口。再试试用鼠标右键选择“在新窗口打开”,问题解决。
二、MIE2浏览网页时,随时会出现如下故障提示: A Unhandled Exception Occured. Error Code=C0000005
解决方法:换用ie或者卸载MYIE2试验试验~如果信得过“windows优化大师”可以扫描一下错误或者用其所带的“系统医生检测一下”,估计是系统问题或者软件兼容问题。
三、开一个浏览器看网页,CPU使用率就能到100%
解决方法:
1、通俗一些,你的系统是否稍微有些弱或配件搭配不好存在“瓶颈”。反映在内存方面,个人感觉是否开双通道是关键(128M单条跟64x2应该速度差异挺大),另外就是内存大小(单位p4 2.x 128M 硕泰克 vs 家用xp1800+ 256x2 磐正8RDA,后者性能明显优于前者,前者p4机器有你所谓的“短路”现象)。
2、病毒防火墙或网络防火墙导致系统“短路”现象,试将其关闭后观察。
3、长时间使用,系统错误过多,导致“短路”现象,建议用优化大师进行彻底的清理和优化。
4、重装系统,尽量少安装应用软件,此时观察是否有“短路”现象。如有,说明硬件问题。
5、上贴所说再加一条内存开双通道(需主板支持)
6、升级主板coms(不建议采用)
另:关于你所说的“CPU使用率就能到100%”的现象,不知是否看错了?是空闲100%还是使用率100%?如果长时间“100%”,相信你会经常死机的~另外看看是什么导致你cpu使用率100%
四、IE看网页时有的flsah动画不能显示
解决方法:
1、系统禁用了FLSH播放功能,打开即可
2、升级浏览器到6.0以上版本,或干脆修复安装
五、IE非法操作,即将关闭
解决方法:
1、可能是系统冲突,建议修复安装
2、如果修复不可用,高格后安装,如果是刚刚安装的系统出项以上问题,可能是你的98安装盘或备份的系统的有问题,换一个光盘即可解决。
六、alonetree 的经验总结
第一招:在、属性、常规、设置、查看文件中删掉所有插件,通常是3721或FLASH及一些国外色情站点的插件,不能删掉的,在添加删除程序里卸掉,能解决大部分IE不稳定的问题(如非法操作等、窗口自动关闭的-跟3721的广告拦截有一些关系);
第二招:常备98、2K、XP的才安装后的Internet Exploer注册表键值,出现莫名其妙的问题时,直接删掉原来的主键,再导入备份的注册表(不能打开链接或第二层链接的,百试百灵)
第三招:实在没辙,升级到最高版IE6.0SP1
第四招:如果以上几招不足以解决问题,估计系统核心文件受损,在排除病毒影响后,最好格了重装。
一般来说,前二招足以解决80%的问题了,用到第四招不到1%,如果有更好的方法,请帖出来大家共学哦!
其实道理很简单:IE也是一个软件,起作用的就是程序文件夹里的几个文件和注册表里的一些设置,同时调用到系统核心里的一些DLL,这些DLL在2K或XP中受系统保护,一般很少出问题。大家只要对文件或设置对症下药即可
七、WIN2000 SERVER+IE6.0 打开浏览器一会,就会出现错误,提示发送或不发送,不管点击什么浏览器就关闭了。
解决方法:由于IE核心的某些DLL文件在注册表中的相关内容(注册信息)丢失或者出错,一般是因为安装了某些软件引起的,特别是一些设计存在问题的软件。解决步骤如下:
1、开始----运行
2、输入命令regsvr32 actxprxy.dll------确定
3、输入命令regsvr32 shdocvw.dll------确定
4、重启,一般可以解决。
如果还有问题,按上面的方法把以下几个DLL文件同样进行注册:
mshtml.dll,urlmon.dll,msjava.dll,browseui.dll,oleaut32.dll,shell32.dll
5、修复安装或升级到6.0 的浏览器或用其他的浏览器也可以
①对IE 5.0的重装可按以下步骤进行:
第一步:打开“注册表编辑器”,找到[HKEY_LOCAL_ MACHINE\Software\Microsoft\Internet Explorer],单击其下的Version Vector键。
第二步:在右侧窗格中双击IE子键,将原来的“5.0002”改为“4.0”,单击“确定”后退出“注册表编辑器”。
第三步:重启后,就可以重装IE 5.0了。
②IE 6.0的重装有两种方法:
方法1:打开“注册表编辑器”,找到[HKEY_LOCAL_ MACHINE\SOFTWARE\Microsoft\Active Setup\Installed Components\{89820200-ECBD-11cf-8B85-00AA005B4383}],将IsInstalled的DWORD值改为0就可以了。
方法2:放入Windows XP安装盘,在“开始→运行”窗口键入“rundll32.exe setupapi,InstallHinfSection DefaultInstall 132 %windir%\Inf\ie.inf”。
6、针对不同情况,可分别用以下方法关闭IE发送错误报告功能:
①对IE 5.x用户,执行“控制面板→添加或删除程序”,在列表中选择“Internet Explorer Error Reporting”选项,然后单击“更改/删除”按钮,将其从系统中删除。
②对Windows 9x/Me/NT/2000下的IE 6.0用户,则可打开“注册表编辑器”,找到[HKEY_LOCAL_MACHINE\Software \Microsoft\Internet Explorer\Main],在右侧窗格创建名为IEWatsonEnabled的DWORD双字节值,并将其赋值为0。
③对Windows XP的IE 6.0用户,执行“控制面板→系统”,切换到“高级”选项卡,单击“错误报告”按钮,选中“禁用错误报告”选项,并选中“但在发生严重错误时通知我”,最后单击“确定”按钮。
八、使IE浏览器窗口打开后为最大化
IE有记忆功能,即重新开启的窗口默认为前一次关闭的状态,使它最大化,进行如下操作:运行注册表:
开始——运行——regedit,找到
HKEY-CURRENT-USER\software\microsoft\internet explorer\main 子键,在右边窗口中删除“windows_piacement".
HKEY-CURRENT-USER\software\microsoft\internet explorer\desktop\old work-areas子键,在右边删除"old workarearects",关闭编辑器,重新启动计算机,连续辆次最大化(最大化-最小化-最大化)后,关闭浏览器,重新启动浏览器即可。
九、联网状态下,浏览器无法打开某些站点
【故障现象】上网后,在浏览某些站点时遇到各种不同的连接错误。
【故障点评】这种错误一般是由于网站发生故障或者你没有浏览权限所引起。
【故障解决】针对不同的连接错误,IE会给出不同的错误信息提示,比较常见的有以下几个:
①提示信息:404 NOT FOUND这是最为常见的IE错误信息。主要是因为IE不能找到你所要求的网页文件,该文件可能根本不存在或者已经被转移到了其他地方。
②提示信息:403 FORBIDDEN常见于需要注册的网站。一般情况下,可以通过在网上即时注册来解决该问题,但有一些完全“封闭”的网站还是不能访问的。
③提示信息:500 SERVER ERROR通常由于所访问的网页程序设计错误或者数据库错误而引起,你只有等待对方网页纠正错误后再浏览了。
十、怎样修复浏览器
1、设置主页 主页就是你打开浏览器时自动打开的网页,设置方法:
工具\internet选项\常规\主页\可更改主页地址,输入你常去的网址,或在打开网页后,工具\internet选项\常规\主页\可更改主页地址,按"使用当前页"即可。
2、有些恶意网站会更改你的主页,更可恶的是,修改主页按钮变成灰色,即不让你修改。这是因为网站程序修改了你的注册表,你必须修改注册表才能激活该选项,但修改注册表是复杂且危险的操作,一般用户不敢轻易动手,且有些网站还会把你的注册表锁定,这里推荐几个小程序,轻松搞定 (相关软件下载区)
3、第二天,当你打开电脑时,可能那个可恶的网站一开机就自动打开了,你昨天白忙乎。这是因为该网站修改了你的启动程序。好在windows自带了一个小程序帮你修改注册表,方法是:开始\程序\附件\系统工具\系统信息\系统配置实用程序\启动,把含有网址的可疑项前面的"√"去掉,确定,OK了。还有一个方法调用系统配置实用程序:开始\运行\输入"msconfig",确定。
十、如何清除浏览器上设置的分级审查密码
简单点----如果有的话,用兔子和大师也可以做到
复杂点----关于注册表的修改
点击“开始→运行”,输入Regedit.exe,按下“确定”按钮启动注册表编辑器。依次选择“HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies”,删除Ratings键值,然后在C:\Windows\System目录下将Ratings.pol文件重命名为其他文件名,重新启动电脑,在下一次使用分级审查时,系统将会提示你输入一个新的密码。
十一、手工快速清除恶意网页灌水
上网最讨厌的莫过于恶意网页,现在越流行越历害,种类越多,今撰文以谢网友支持。
恶意网页的种类:
一、开机画面弹出型,在系统启过程中,弹出“请登陆少女脱衣网站 www.???.com",这类网站不会对系统产生危害,但这话语不登大雅之堂,使许多网友在异性朋友\上司\家长面前,很狼狈,通常重装系统不启任何作用;
二、自动弹出收费 *** 或黄色网站型,你一但登陆过该网站,下次只要打开IE,这类网站就会弹出,无限超链接,你关也关不了,越关越多,越发着紧急,因为这类话语不堪入目,图片下流,打搅了你的正常上网,甚至你无法下载清除恶意网页之类的软件;
三、无止境发问型,你只要打开该网页,就会出现诸如“你吃过了吗?”,你点确定,他又问“你找对象发吗?”,一直问这些无聊的话语;
四、锁死鼠标\键盘\定格画面型,你要是受到危害,就会使你的机子假死。
恶意网页产生原因:
一、邮箱,当你打开一封信件,一开始你并不知道他是内容,打开后就大呼上当;
二、收费电影网,你一但浏览譔网站,又改变注意不要注册,那么你就受骚拢了,下次你要打开IE,他就超链接到此处;
三、QQ,网友恶作剧,你俩聊天时他告诉你一个网站,你打开后就上当了,他在偷偷笑;
四、黄色网站(成年人网站),你要是打开就会出现这种情况;
五、鼠标经过浮动的动画图标,这完全是不自觉的,但也是无赖的。
恶意网页形成原理:
一、在网站原代码之中加入恶意代码,诸如鼠标经过热区,进行超链接;
二、恶意修改注册表,使你的机子为他调配;
三、恶意黑客程序。
恶意网页清除之法:
这里完全是中子教授首创。上述一\二类情况,在该类别发生时,你先不要惊慌,一类情况重装系统不起作用,格式化才行,二类情况重装系统后完全清除,但中子教授不赞成这种无谓的方式,你要先准备一张纸,一支笔,把这些恶意网址全部记下来,然后开始-------运行--------regedit--------编辑-------查找--------你刚才记下的网站,如“www.???.com”,一个一个来,不要心急给大家推荐几个修复浏览器的软件。
修复浏览器的软件
1、IE修复器 3.0 Build 2003.10.01
软件大小:1376KB
软件语言:简体中文
软件类别:国产软件 / 免费版 / 浏览辅助
运行环境:Win9x/WinNT/2000/ME/XP
软件介绍:
是否觉得您的IE浏览器经常在浏览的时候被恶意更改呢?时候觉得束手无策呢?不用怕,IE修复器的发布是你的福音,可以使你随心所欲地尽情在网上冲浪,再也不用怕哪些恶意网页对您爱机中的IE浏览器进行恶意地修改,可以保证到你的爱机的安全。内置工具有IE修复器、IE监测器及快速启动程序,通过这些工具的完美结合,可以令你在网上冲浪更加安心。
下载地址: http://www.86down.com/downinfo/9723.html
2、黄山IE修复专家 7.0
软件大小:4382KB
软件语言:简体中文
软件类别:国产软件 / 免费版 / 浏览辅助
运行环境:Win9x/WinNT/2000/ME/XP
软件介绍:
修复恶意网页破坏的IE ,系统优化.
http://www.superdown.com/soft/6022.htm
3、瑞星杀毒软件2003免A盘免序列号原版光盘 ISO 精简版
http://www.86down.com/downinfo/18261.html
http://www.86down.com/downinfo/6329.html
这个不用多说了,大家都知道了
4、江民的杀毒软件有修复浏览器的功能,大家可以到一下地址去看
http://www.jiangmin.com/
5、瑞星的注册表修复工具: http://it.rising.com.cn/service/technology/RegClean_download.htm
瑞星的在线修复网站:
http://online.rising.com.cn/free/index.htm
6、毒霸的注册表修复工具: http://db.kingsoft.com/download/3/8.shtml
7、推荐几个很好的在线修复:http://www.cw88.net/cw88com/new/fixie.htm
http://cn.zs.yahoo.com/start.htm
http://www.v111.com/wz/ie.htm
还没有追人家呢,就有人说:东哥,听说你在13楼搞定一个,真是,帮忙占位的玩得好的,总会有闲人乱说话了,世界总不能没有这些人,要好好利用这帮人,。。。。。。。纯属炒作,让他们炒作去吧,生活太平淡来点报料岂不更好,哈哈
肯定有问题。。。。 我肯定有有问题吗?。。。。。^_^,还是该改注意一下放肆的生活作风了,呵呵!
它人笑我太疯巅,我笑他人看不穿。。。,人的身体太软弱了,哈哈,还好有坚强的意念来补充,世俗太炎凉了,还好有哲学来指引,实事求是,要客观对待,其实意识也是客观的,否则就会出问题,遭遇失败,这是我的新的突破,总算没有白看马哲学。。。。

介绍个网上听歌的地方:以前可以在www.aboutplayer.com听,现在不行了,http://zjp.hubu.net/aboutplayer/?page=3可以听的,很好的喔。。。。。有歌词。。。

一、扩展你的Smarty
1、准备功夫
PHP代码:--------------------------------------------------------------------------------
function Smarty_function_page ( $params, &$Smarty )
{
$href = '#';
$space = ' ';
$frist = NULL;
$last = NULL;
$page = 5;
extract($params);
if ( !$row || $row <= 1 ) return ' ';
$pages = $row;
$curr_page = $now ? $now : 1;
$offset = 2;
$from = $curr_page - $offset;
$to = $curr_page + $page - $offset - 1;
if ( $page > $pages )
{
$from = 1;
$to = $pages;
}
else
{
if ( $from < 1)
{
$to = $curr_page + 1 - $from;
$from = 1;
if ( ( $to - $from ) < $page && ( $to - $from ) < $pages )
{
$to = $page;
}
}
elseif ( $to > $pages )
{
$from = $curr_page - $pages + $to;
$to = $pages;
if ( ( $to - $from ) < $page && ( $to - $from) < $pages )
{
$from = $pages - $page + 1;
}
}
}
if ( $frist && ( $curr_page - 1 ) >= 1 ) $p['frist'] = '' . $frist . '';
if ( $prev && ( $i = $curr_page - 1 ) >= 1 ) $p['prev'] = '' . $prev . '';
for( $i = $from; $i <= $to; $i ++ )
{
if ( $i == $curr_page )
{
$p[$i] = '[' . $i . ']';
}
else
{
$p[$i] = '' . $i . '';
}
}
if ( $next && ( $i = $curr_page + 1 ) <= $pages ) $p['next'] = '' . $next . '';
if ( $last && ( $curr_page + 1 ) <= $pages ) $p['last'] = '' . $last . '';
return implode( $space, $p );
} // end func
--------------------------------------------------------------------------------
将上面的代码命名为"function.page.php"保存到Smarty的plugins目录里
代码:--------------------------------------------------------------------------------
{page row=10}
{page row=10 now=5}
{page row=10 now=5 href="plugins.php?a=1&b=2&page=" frist="第一页" prev="上一页" next="下一页" last="最后页"}
{page row=10 now=5 href="plugins.php?a=1&b=2&page=" frist="第一页" prev="上一页" next="下一页" last="最后页"}
{page row=10 now=1 href="plugins.php?a=1&b=2&page=" frist="第一页" prev="上一页" next="下一页" last="最后页"}
{page row=10 now=10 href="plugins.php?a=1&b=2&page=" frist="第一页" prev="上一页" next="下一页" last="最后页"}
--------------------------------------------------------------------------------
将上面的代码命名为"plugins.html"保存到Smarty的template目录里
2、测试程序
PHP代码:--------------------------------------------------------------------------------
$Smarty->display( 'plugins.html' );
--------------------------------------------------------------------------------
3、使用说明
我懒得打了,对比一下"plugins.html"的5个{page}用法,以及看看显示出来的效果就明白是什么了
4、插件说明
“《Smarty手册》第十六章.以插件扩展Smarty ”的应用。像中文字符截取之类的都可以以plugins扩展Smarty,Smarty自带的截取不支持中文。
__________________
二、Smarty自动生成静态页面
如果你的文件扩展名为".html"~~~~~嘿嘿,这不就是静态页面了吗?-_-!
至于怎么取得静态的文件名呢?
PHP代码:--------------------------------------------------------------------------------
/**
*
*/
class template extends Smarty
{
/**
*
*/
function template ()
{
$this->Smarty();
} // end func
/**
*
*/
function name ( $tpl_file, $cache_id = null, $compile_id = null )
{
if (!isset($compile_id)) $compile_id = $this->compile_id;
$_auto_id = $this->_get_auto_id( $cache_id, $compile_id );
$_cache_file = $this->_get_auto_filename( $this->cache_dir, $tpl_file, $_auto_id );
return basename( $_cache_file );
} // end func
} // end class
$Smarty = new template;
$file_name = $Smarty->name( 'plugins.html', 'cache_name' );#html文件的名字(不包含路径)
$Smarty->cache_lifetime = -1;#静态文件永不过期
$Smarty->fetch( 'plugins.html', 'cache_name' );#生成静态html文件
这是村里翻译的,实际上Smarty的应用还远不止于此。
One of the unique aspects about Smarty is the template compling. This means Smarty reads the template files and creates PHP scripts from them. Once they are created, they are executed from then on. Therefore there is no costly template file parsing for each request, and each template can take full advantage of PHP compiler cache solutions such as Zend Accelerator (http://www.zend.com) or PHP Accelerator (http://www.php-accelerator.co.uk).
Smarty的特点之一是"模板编译"。意思是Smarty读取模板文件然后用他们创建php脚本。这些脚本创建以后将被执行。因此并没有花费模板文件的语法解析,同时每个模板可以享受到诸如Zend加速器(http://www.zend.com) 或者PHP加速器(http://www.php-accelerator.co.uk)。这样的php编译器高速缓存解决方案。
Some of Smarty's features:
Smaty的一些特点:
It is extremely fast.
非常非常的快!
It is efficient since the PHP parser does the dirty work.
用php分析器干这个苦差事是有效的
No template parsing overhead, only compiles once.
不需要多余的模板语法解析,仅仅是编译一次
It is smart about recompiling only the template files that have changed.
仅对修改过的模板文件进行重新编译
You can make custom functions and custom variable modifiers, so the template language is extremely extensible.
可以编辑'自定义函数'和自定义'变量',因此这种模板语言完全可以扩展
Configurable template delimiter tag syntax, so you can use {}, {{}}, , etc.
可以自行设置模板定界符,所以你可以使用{}, {{}}, , 等等
The if/elseif/else/endif constructs are passed to the PHP parser, so the {if ...} expression syntax can be as simple or as complex as you like.
诸如 if/elseif/else/endif 语句可以被传递到php语法解析器,所以 {if ...} 表达式是简单的或者是复合的,随你喜欢啦
Unlimited nesting of sections, ifs, etc. allowed.
如果允许的话,section之间可以无限嵌套
It is possible to embed PHP code right in your template files, although this may not be needed (nor recommended) since the engine is so customizable.
引擎是可以定制的.可以内嵌php代码到你的模板文件中,虽然这可能并不需要(不推荐)
Built-in caching support
内建缓存支持
Arbitrary template sources
独立模板文件
Custom cache handling functions
可自定义缓存处理函数
Plugin architecture
插件体系结构
键盘在所有的驱动之中最为简单的一种,但它却包含了驱动的基本框架,对以后继续深入学习其他复杂的驱动大有裨益,以下便为你逐步剖析驱动的开发。采用的是查询方式。
一.内核模块的注册和撤销
在加载模块的时候,首先运行的是内核模块的注册函数。它的功能包括内核注册设备以及变量的初始化。
static int head,tail;
int _init Keypad_init(void)
{
int result;
result=register_chrdev(KEY_LED_MAJOR,KEY_LED_NAME,&Keypad_fops);
Keypad_clear();
init_waitqueue_head(&queue);
prink("%s %s initialized.\n",KEY_LED_NAME,KEY_LED_VERSION);//不能用prinf
return 0;
}
module_init(Keypad_init);//加载模块
void _exit Keypad_cleanup(void)
{
del_timer(&timer);
unregister_chrdev(KEY_LED_MAJOR,KEY_LED_NAME);
prink("Keypad driver removed \n");
}
module_exit(Keypad_cleanup);//卸载该模块
二.虚拟文件系统与硬件驱动的接口
static struct file_operations Keypad_fops={
open:Keypad_open,
read:Keypad_read,
poll:Keypad_poll,
fasync:Keypad_fasync,
release:Keypad_release,
};
该接口定义完之后一些便是对这几个具体函数的实现了!现在我们一起进入下一步吧,是不是觉得其实没什么难度的呢?别那么早开心着呢?这几个函数的实现时候,涉及到很多技术,包括内核定时器,等待队列的具体实现(阻塞方式),异步方式的具体实现技巧,循环队列。看到这么多技术你是否感到很兴奋呢?以下本人将以通俗的方式为你讲解,希望你能理解。
三.设备的打开操作接口函数具体实现(Keypad_open)
设备打开一般包括两大操作,一是完成设备的初始化,二是设备引用计数器加1
static int Keypad_open(struct inode *inode,struct file *filp)
{
read_xy();
try_module_get(THIS_MODULE);//此函数为linux 2.6内核增加的,不同于2.4内核,功能是计数器的值加1
return 0;
}
static void read_xy(void)
{
new_data();//获取键值函数
keypad_starttimer();//开启内核定时器,在固定周期时间内获取键盘新的变化
}
以下实现键盘键值获取函数read_xy()
主要是从KEY_CS(对应的读入地址,之前可以根据具体的硬件设备定义,比如#define kEY_CS(*
(volatile unsigned short *)(0xf820000))此处应该根据具体的不同而不同!
将读入的键值存入buf[]缓存中,环形缓冲的写指针是head,读指针是tail,前面已经定义过了
////////////////////////////////键盘事件的数据结构定义/////////////////////////////////
typedef struct{
ulong status;//按键的值
ulong click;//是否有按键按下,1表示有,0表示没有
}KEY_EVENT
static KEY_EVENT cur_data,buf[BUFSIZE];//BUFSIZE为宏定义,用于定义环形缓冲的大小
static void new_data(void)
{
if((KEY_CS & 0xff)!=0xff) //从KEY_CS地址读入数据,若有一个为0则表示有一个按键被按下了(此处硬件电路为低电平有效)
{
switch(KEY_CS & 0xff){
case ~KEY0 & 0xff:
cur_data.status=1;///////1被按下
break;
case ~KEY1 & 0xff:
cur_data.status=2;//2被按下
break;
/////////其他一样添加,懂吗??
}
cur_data.click=1;
}
else if(KEY_CS & 0xff==0xff){
cur_data.click=0;
cur_data.status=0;
}
if(head!=tail){////////循环队列缓冲区的应用在此开始了^_^
int last=head--;
if(last<0)////////若已经到了对首之前,则跳到队尾,以实现循环队列
last=BUFSIZE-1;
}
//////按键信息存入循环队列缓冲区中
buf[head]=cur_data;
if(++head==BUFSIZE)
head=0;
if(head==tail && tail++=BUFSIZE)
tail=0;
if(fasync)
kill_fasync(&fasyc,SIGIO,POLL_IN);
wake_up_interruptible(&queue);
}
接下来我们介绍其他几个文件接口函数的实现
四.先介绍关闭函数keypad_release(),为什么先介绍它呢?道理很简单,应该它比较简单,先让大家做下热身运动,在介绍完这个之后,继续会介绍一个比较复杂的函数.
关闭操作主要实现的是:关闭设备异步通知,设备计数器减1,删除定时器信号中断
static int Keypad_release(struct inode *inode,struct)
{
Keypad_fasync(-1,filp,0);
module_put(THIS_MODULE);
del_timer(&timer);
return 0;
}
五.设备读取操作接口函数实现Keypad_read()
主要作用是从缓冲区读取键值,通过调用get_data()实现,通过copy_to_user()函数将键值复制到用户的数据区中
static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)
{
DECLEARE_WAITQUEUE(wait,current);//声明等待队列,将当前进程加入到等待队列中
KEY_EVENT t;
ulong out_buf[2];
if(head==tail)//当前循环队列中没有数据可以读取
{
if(filp->f_flags & O_NONBLOCK)//假如用户采用的是非堵塞方式读取
return _EAGAIN;
add_wait_queue(&queue,&wait);//将当前进程加入等待队列
current->state=TASK_INTERRUPTIBLE;//设置当前进程的状态
while((head==tail)&&!signal_pending(current))//假若还没有数据到循环队列并且当前进程没有受到信号
{
shedule();//进程调度
current->state=TASK_INTERRUPTIBLE;
}
current->state=TASK_RUNNING;
remove_wait_queue(&queue,&wait);
if(head==tail)
return count;
t=get_data();//调用get_data()函数,得到缓冲区中的数据,下面将给予详细的 介绍
out_buf[0]=t.status;
out_buf[1]=t.click;
copy_to_user(buf,&out_buf,sizeof(out_buf));//将得到的键值拷贝到用户数据区
return count;
}
}
很自然我们就应该要介绍get_data()函数的实现了,该函数的功能就是从我们定义的循环队列缓冲区中读出我们要的键值,所以其实很简单的如果理解循环队列的原理,在此不多加解释,大家应该具备一般的数据结构相关的知识吧
static KEY_EVENT get_data(void)
{
int last=tail
if(++tail==BUFSIZE)
tail=0;
return buf[last];
}
上面如果你看得懂得话,那么可以进入下面的学习了,主要介绍的是内核定时器的使用,利用等待队列实现阻塞型I/O,poll系统调用,异步通知方式,介绍完之后,我将给出一个应用实例,对于有使用过文件操作系统调用的来说,对我们所写的键盘驱动来说,他们基本上是一样的。废话少说,我们马上开始我们精彩的驱动开发!
六.内核定时器的使用
在该驱动中,我们假设对键盘的获取是以0.2s为周期执行。源代码如下
static struct timer_list timer;///////我们定义的定时器,也许你会问timer_list是什么来的,其实一看名称就应该就知道了,而为什么要用到list那么多定时器呢?其实在linux中还有很多相同的定义,比如说信号,我们定义的也是信号集,你可以定义该list是一个元素的,也可以是多个的。所以对于 timer_list就可以这样描述:在未来某一个特定时刻执行某一系列特定任务的功能。下面我们还会给出内核中timer_list的具体描述,
static int Keypad_starttimer(void)
{
init_timer(&timer);//初始化定时器结构
timer.function=Keypad_timer;//超时服务程序
timer.expires=jiffies+20;//当前时刻加0.2s
add_timer(&timer);
return 0;
}
///超时服务程序
static void Keypad_timer(unsigned long data)
{
read_xy();
}
/////////接下来说下timer-list这个数据结构,如果你不感兴趣的话可以跳过,该结构在
include\linux\timer.h中定义
struct timer_list
{
struct list_head entry;
unsigned long expries;
spinlock_t lock;
unsigned long magic;
void (*function)(unsigned long);
unsigner long data;
struct tvec_t_base_s *base;
}
七.利用等待队列实现阻塞型I\O
在用户程序执行读操作的时候有可能尚且没有数据可以读取,为此需要让read操作等待,直到有数据可以读取,这就是阻塞型i\o,阻塞型io可以通过使用进程休眠方法实现。在无数据可以读取的时候,采用等待队列让进程休眠,直到有数据到达的时候才唤醒进程完成数据的读操作。
在本驱动中的read,若循环队列缓冲区中没有数据,则进程进入休眠态,定时器函数每隔0.2s读取键值一次,将按键状态放入缓冲并且适时唤醒进程读取数据。
等待队列的使用流程如下:
1.声明一个等待队列
2.把当前进程加入到等待队列中
3.把进程的状态设置为TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;
4.调用schedule,以让出cpu
5.检测所需要的资源是否可用,若是,把当前进程从等待队列中删除,否则转3循环
接下来我们在对read中有关等待队列阻塞实现做具体的解释
static ssize_t Keypad_read(struct file *filp,char *buf,ssize_t count,loff_t *l)
{
DECLEARE_WAITQUEUE(wait,current);//声明等待队列,将当前进程加入到等待队列中
KEY_EVENT t;
ulong out_buf[2];
if(head==tail)//当前循环队列中没有数据可以读取
{
if(filp->f_flags & O_NONBLOCK)//假如用户采用的是非堵塞方式读取
return _EAGAIN;
add_wait_queue(&queue,&wait);//将当前进程加入等待队列
current->state=TASK_INTERRUPTIBLE;//设置当前进程的状态
while((head==tail)&&!signal_pending(current))//假若还没有数据到循环队列并且当前进程没有受到信号(该类信号具体来说是未决的休眠)
{
shedule();//进程调度
current->state=TASK_INTERRUPTIBLE;
}
current->state=TASK_RUNNING;//该进程恢复执行
remove_wait_queue(&queue,&wait);//移出等待队列
if(head==tail)
return count;
t=get_data();//调用get_data()函数,得到缓冲区中的数据,下面将给予详细的 介绍
out_buf[0]=t.status;
out_buf[1]=t.click;
copy_to_user(buf,&out_buf,sizeof(out_buf));//将得到的键值拷贝到用户数据区
return count;
}
}
八.poll系统调用操作接口函数
当程序需要进行对多个文件读写时,如果某个文件没有准备好,则系统就会处于读写阻塞的状态,这影响了其他文件的读写,为了避免读写阻塞,一般可以在应用程序中使用poll或者select函数。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标志读写相应的文件,实现非阻塞的读写,poll()函数通过poll系统调用,调用对应设备驱动的poll()接口函数,poll返回不同的标志,告诉主进程文件是否可以读写,这些返回标志存放在include\asm\poll.h中
标志 含义
POLLIN 如果设备无阻塞的读,就返回该值
POLLRDNORM 通常的数据已经准备好,可以读了,就返回
该值。通常的做法是会返回(POLLLIN|POLLRDNORA)
POLLRDBAND 如果可以从设备读出带外数据,就返回该值,它只可在linux内核的某些网络代码中使用,通常不用在设备驱动程序中
POLLPRI 如果可以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select
报告文件发生异常,以为select八带外数据当作异常处理POLLHUP 当读设备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。
POLLERR 如果设备发生错误,就返回该值。
POLLOUT 如果设备可以无阻塞地些,就返回该值
POLLWRNORM 设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM)
POLLWRBAND 于POLLRDBAND类似
在本章地驱动程序中,Keypad_poll()函数在缓冲区有新数据时(当head!=tail),返回一个
POLLIN|POLLRDNORM,告诉主进程有新的
九.在设备驱动中实现异步通知
虽然大多数时候阻塞型和非阻塞型操作的组合及poll方法可以有效查询设备是否可以读写,但是如果驱动程序能避免主动的查询,改主动为被动的信号通知触发,则可以提高程序的效率,这也就是异步通知的目的。异步通知向进程发送SIGIO信号,通知访问设备的进程,表示该设备已经准备好IO读写了。
之后就是如何实现异步通知的问题了,要启动异步通知,必须执行两个步骤:首先,须要制定某个作为文件的“属主”。文件属主的进程ID保存在filp- >f_owner中,这可以通过fcntl()系统调用执行F_SETOWN命令设置。此外,用户程序还必须曙色之设备的FASYNC标志,以真正启动异步通知机制。这里的FASYNC标志也使用fcntl()设置。
在完成这两个步骤之后,当新数据到达时就会产生一个SIGNO信号,此信号发送到存放在filp->owner中的进程。
从驱动的角度看,则主要时通过调用两个内核提供的函数来实现就是了。他们分别是:int
fasync_helper()和void kill_fasync();这两个函数定义在:include\linux\fs\fcntl.h
要实现异步,驱动中只要如下编写即可
static struct fasync_struct *fasync;//首先是定义一个结构体
static int Keypad_release(struct inode *inode,struct file *filp)
{
Keypad_fasync(-1,filp,0);//这是一个异步通知
。。。。。。。
}
static int Keypad_fasync(int fd,struct file *filp,int on)
{
int retval;
retval=fasync_helper(fd,filp,on,&fasync);
if(retval<0)
return retval;
return 0;
}
到此为止,键盘驱动已经介绍完了,接下来就介绍下一个利用使用驱动的应用实例了。
以下程序的主体是一个条件循环,每次循环执行一次,就读取一次键值。
1。打开Keypad设备
#define DEV_NAME "/dev/Keypad"
int fb=0;
fb=open(DEV_NAME,O_RNONLY);
if(!fb){
printf("Error:cannot open Keypad device.\n");
exit(1);
}
printf("The Keypad device was opened successfully.\n");
}
2.读取键值
unsigned long keydata[2];
int input=1;
while(input!=0)
{
if(read(fd,(char*)keydata,sizeof(keydata))==-1){
printf("Error reading the keypad data");
close(fb);
exit(2);
}
if(keydata[0]){
switch(keydata[1]){
case 1:printf("KEYPUSED 1");//1键被按下
input=0;////下此循环退出
break;
。。。。。。。。。。。。。。。。。。
}
}
}
3。关闭Keypad设备
close(fb);
printf("Good bye Keypad");
键盘驱动到此介绍完毕!!
C语言的最大特点是:功能强、使用方便灵活。C编译的程序对语法检查并不象其它高级语
言那么严格,这就给编程人员留下“灵活的余地”,但还是由于这个灵活给程序的调试带
来了许多不便,尤其对初学C语言的人来说,经常会出一些连自己都不知道错在哪里的错误
。看着有错的程序,不知该如何改起,本人通过对C的学习,积累了一些C编程时常犯的错
误,写给各位学员以供参考。
1.书写标识符时,忽略了大小写字母的区别。
main()
{
int a=5;
printf("%d",A);
}
编译程序把a和A认为是两个不同的变量名,而显示出错信息。C认为大写字母和小写字母是
两个不同的字符。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。
2.忽略了变量的类型,进行了不合法的运算。
main()
{
float a,b;
printf("%d",a%b);
}
%是求余运算,得到a/b的整余数。整型变量a和b可以进行求余运算,而实型变量则不允许
进行“求余”运算。
3.将字符常量与字符串常量混淆。
char c;
c="a";
在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字
符串常量是一对双引号括起来的字符序列。C规定以“\”作字符串结束标志,它是由系统
自动加上的,所以字符串“a”实际上包含两个字符:‘a'和‘\',而把它赋给一个字符变
量是不行的。
4.忽略了“=”与“==”的区别。
在许多高级语言中,用“=”符号作为关系运算符“等于”。如在BASIC程序中
可以写
if (a=3) then …
但C语言中,“=”是赋值运算符,“==”是关系运算符。如:
if (a==3) a=b;
前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。由于习惯问题,
初学者往往会犯这样的错误。
5.忘记加分号。
分号是C语句中不可缺少的一部分,语句末尾必须有分号。
a=1
b=2
编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一
部分,这就会出现语法错误。改错时,有时在被指出有错的一行中\未发现错误,就需要看
一下上一行是否漏掉了分号。
{ z=x+y;
t=z/100;
printf("%f",t);
}
对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL
不同的)。
6.多加分号。
对于一个复合语句,如:
{ z=x+y;
t=z/100;
printf("%f",t);
};
复合语句的花括号后不应再加分号,否则将会画蛇添足。
又如:
if (a%3==0);
I++;
本是如果3整除a,则I加1。但由于if (a%3==0)后多加了分号,则if语句到此结束,程序将
执行I++语句,不论3是否整除a,I都将自动加1。
再如:
for (I=0;I<5;I++);
{scanf("%d",&x);
printf("%d",x);}
本意是先后输入5个数,每输入一个数后再将它输出。由于for()后多加了一个分号,使循
环体变为空语句,此时只能输入一个数并输出它。
7.输入变量时忘记加地址运算符“&”。
int a,b;
scanf("%d%d",a,b);
这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指
a在内存中的地址。
8.输入数据的方式与要求不符。
①scanf("%d%d",&a,&b);
输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:
3,4
输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。
②scanf("%d,%d",&a,&b);
C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应
输入与这些字符相同的字符。下面输入是合法的:
3,4
此时不用逗号而用空格或其它字符是不对的。
3 4 3:4
又如:
scanf("a=%d,b=%d",&a,&b);
输入应如以下形式:
a=3,b=4
9.输入字符的格式与要求不一致。
在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。
scanf("%c%c%c",&c1,&c2,&c3);
如输入a b c
字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后
面不需要用空格作为两个字符的间隔。
10.输入输出的数据类型与所用格式说明符不一致。
例如,a已定义为整型,b定义为实型
a=3;b=4.5;
printf("%f%d\n",a,b);
编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。
11.输入数据时,企图规定精度。
scanf("%7.2f",&a);
这样做是不合法的,输入数据时不能规定精度。
12.switch语句中漏写break语句。
例如:根据考试成绩的等级打印出百分制数段。
switch(grade)
{ case 'A':printf("85~100\n");
case 'B':printf("70~84\n");
case 'C':printf("60~69\n");
case 'D':printf("<60\n");
default:printf("error\n");
由于漏写了break语句,case只起标号的作用,而不起判断作用。因此,当grade值为A时,
printf函数在执行完第一个语句后接着执行第二、三、四、五个printf函数语句。正确写
法应在每个分支后再加上“break;”。例如
case 'A':printf("85~100\n");break;
13.忽视了while和do-while语句在细节上的区别。
(1)main()
{int a=0,I;
scanf("%d",&I);
while(I<=10)
{a=a+I;
I++;
}
printf("%d",a);
}
(2)main()
{int a=0,I;
scanf("%d",&I);
do
{a=a+I;
I++;
}while(I<=10);
printf("%d",a);
}
可以看到,当输入I的值小于或等于10时,二者得到的结果相同。而当I>10时,二者结果就
不同了。因为while循环是先判断后执行,而do-while循环是先执行后判断。对于大于10的
数while循环一次也不执行循环体,而do-while语句则要执行一次循环体。
14.定义数组时误用变量。
int n;
scanf("%d",&n);
int a[n];
数组名后用方括号括起来的是常量表达式,可以包括常量和符号常量。即C不允许对数组的
大小作动态定义。
15.在定义数组时,将定义的“元素个数”误认为是可使的最大下标值。
main()
{static int a[10]={1,2,3,4,5,6,7,8,9,10};
printf("%d",a[10]);
}
C语言规定:定义时用a[10],表示a数组有10个元素。其下标值由0开始,所以数组元素a[
10]是不存在的。
16.在不应加地址运算符&的位置加了地址运算符。
scanf("%s",&str);
C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入
项是字符数组名,不必要再加地址符&。应改为:scanf("%s",str);
17.同时定义了形参和函数中的局部变量。
int max(x,y)
int x,y,z;
{z=x>y?x:y;
return(z);
}
形参应该在函数体外定义,而局部变量应该在函数体内定义。应改为:
int max(x,y)
int x,y;
{int z;
z=x>y?x:y;
return(z);
}
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。
先声明几个指针放着做例子:
例一:
(1)int*ptr;
(2)char*ptr;
(3)int**ptr;
(4)int(*ptr)[3];
(5)int*(*ptr)[4];
指针的类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)int*ptr;//指针的类型是int*
(2)char*ptr;//指针的类型是char*
(3)int**ptr;//指针的类型是int**
(4)int(*ptr)[3];//指针的类型是int(*)[3]
(5)int*(*ptr)[4];//指针的类型是int*(*)[4]
怎么样?找出指针的类型的方法是不是很简单?
指针所指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr;//指针所指向的类型是int
(2)char*ptr;//指针所指向的的类型是char
(3)int**ptr;//指针所指向的的类型是int*
(4)int(*ptr)[3];//指针所指向的的类型是int()[3]
(5)int*(*ptr)[4];//指针所指向的的类型是int*()[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。
指针的值,或者叫指针所指向的内存区或地址
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?
指针本身所占据的内存区
指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。
指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
指针的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:
例二:
1、chara[20];
2、int*ptr=a;
...
...
3、ptr++;
在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。
我们可以用一个指针和一个循环来遍历一个数组,看例子:
例三:
intarray[20];
int*ptr=array;
...
//此处略去为整型数组赋值的代码。
...
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
}
这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。
再看例子:
例四:
1、chara[20];
2、int*ptr=a;
...
...
3、ptr+=5;
在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。
如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。
总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说, ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和 ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说, ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。
运算符&和*
这里&是取地址运算符,*是...书上叫做"间接运算符"。
&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。
*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。
例五:
inta=12;
intb;
int*p;
int**ptr;
p=&a;
//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
*p=24;
//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
ptr=&p;
//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int **。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。
*ptr=&b;
//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以用&b来给*ptr赋值就是毫无问题的了。
**ptr=34;
//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。
指针表达式
一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表式。
下面是一些指针表达式的例子:
例六:
inta,b;
intarray[10];
int*pa;
pa=&a;//&a是一个指针表达式。
int**ptr=&pa;//&pa也是一个指针表达式。
*ptr=&b;//*ptr和&b都是指针表达式。
pa=array;
pa++;//这也是指针表达式。
例七:
char*arr[20];
char**parr=arr;//如果把arr看作指针的话,arr也是指针表达式
char*str;
str=*parr;//*parr是指针表达式
str=*(parr+1);//*(parr+1)是指针表达式
str=*(parr+2);//*(parr+2)是指针表达式
由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。
在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。
数组和指针的关系
数组的数组名其实可以看作一个指针。看下例:
例八:
intarray[10]={0,1,2,3,4,5,6,7,8,9},value;
...
...
value=array[0];//也可写成:value=*array;
value=array[3];//也可写成:value=*(array+3);
value=array[4];//也可写成:value=*(array+4);
上例中,一般而言数组名array代表数组本身,类型是int[10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int*,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+ 3)等于3。其它依此类推。
例九:
char*str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
chars[80];
strcpy(s,str[0]);//也可写成strcpy(s,*str);
strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));
上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char*。
*str 也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即'H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char*。
*(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向 "Hi,goodmorning."的第一个字符'H',等等。
下面总结一下数组的数组名的问题。声明了一个数组TYPEarray[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE[n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。
在不同的表达式中数组名array可以扮演不同的角色。
在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。
在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。
表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。
例十
intarray[10];
int(*ptr)[10];
ptr=&array;:
上例中ptr是一个指针,它的类型是int(*)[10],他指向的类型是int[10] ,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。
本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:
int(*ptr)[10];
则在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。
指针和结构类型的关系
可以声明一个指向结构类型对象的指针。
例十一:
structMyStruct
{
inta;
intb;
intc;
}
MyStructss={20,30,40};
//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct*ptr=&ss;
//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。
int*pstr=(int*)&ss;
//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。
请问怎样通过指针ptr来访问ss的三个成员变量?
答案:
ptr->a;
ptr->b;
ptr->c;
又请问怎样通过指针pstr来访问ss的三个成员变量?
答案:
*pstr;//访问了ss的成员a。
*(pstr+1);//访问了ss的成员b。
*(pstr+2)//访问了ss的成员c。
虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元:
例十二:
intarray[3]={35,56,37};
int*pa=array;
通过指针pa访问数组array的三个单元的方法是:
*pa;//访问了第0号单元
*(pa+1);//访问了第1号单元
*(pa+2);//访问了第2号单元
从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。
所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。
所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员 b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。
过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。
指针和函数的关系
可以把一个指针声明成为一个指向函数的指针。intfun1(char*,int);
int(*pfun1)(char*,int);
pfun1=fun1;
....
....
inta=(*pfun1)("abcdefg",7);//通过函数指针调用函数。
可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
例十三:
intfun(char*);
inta;
charstr[]="abcdefghijklmn";
a=fun(str);
...
...
intfun(char*s)
{
intnum=0;
for(inti=0;i{
num+=*s;s++;
}
returnnum;
}
这个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s后,实际是把str的值传递给了s,s所指向的地址就和str所指向的地址一致,但是str和s各自占用各自的存储空间。在函数体内对s进行自加1运算,并不意味着同时对str进行了自加1运算。
指针类型转换
当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。
例十四:
1、floatf=12.3;
2、float*fptr=&f;
3、int*p;
在上面的例子中,假如我们想让指针p指向实数f,应该怎么搞?是用下面的语句吗?
p=&f;
不对。因为指针p的类型是int*,它指向的类型是int。表达式&f的结果是一个指针,指针的类型是 float*,它指向的类型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":
p=(int*)&f;
如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP*TYPE, 那么语法格式是:
(TYPE*)p;
这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。而原来的指针p的一切属性都没有被修改。
一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,也会发生指针类型的转换。
例十五:
voidfun(char*);
inta=125,b;
fun((char*)&a);
...
...
voidfun(char*s)
{
charc;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
注意这是一个32位程序,故int类型占了四个字节,char类型占一个字节。函数fun的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a的结果是一个指针,它的类型是int*,它指向的类型是int。形参这个指针的类型是char*,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int*类型到char*类型的转换。结合这个例子,我们可以这样来想象编译器进行转换的过程:编译器先构造一个临时指针char*temp,然后执行temp=(char*)&a,最后再把temp的值传递给s。所以最后的结果是:s的类型是char*,它指向的类型是char,它指向的地址就是a的首地址。
我们已经知道,指针的值就是指针指向的地址,在32位程序中,指针的值其实是一个32位整数。那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:
unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。
...
...
a=20345686;
ptr=20345686;//我们的目的是要使指针ptr指向地址20345686(十进制
)
ptr=a;//我们的目的是要使指针ptr指向地址20345686(十进制)
编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:
unsignedinta;
TYPE*ptr;//TYPE是int,char或结构类型等等类型。
...
...
a=某个数,这个数必须代表一个合法的地址;
ptr=(TYPE*)a;//呵呵,这就可以了。
严格说来这里的(TYPE*)和指针类型转换中的(TYPE*)还不一样。这里的(TYPE*)的意思是把无符号整数a的值当作一个地址来看待。上面强调了a的值必须代表一个合法的地址,否则的话,在你使用ptr的时候,就会出现非法操作错误。
想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完 全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:
例十六:
inta=123,b;
int*ptr=&a;
char*str;
b=(int)ptr;//把指针ptr的值当作一个整数取出来。
str=(char*)b;//把这个整数的值当作一个地址赋给指针str。
现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。
指针的安全问题
看下面的例子:
例十七:
chars='a';
int*ptr;
ptr=(int*)&s;
*ptr=1298;
指针ptr是一个int*类型的指针,它指向的类型是int。它指向的地址就是s的首地址。在32位程序中,s占一个字节,int类型占四个字节。最后一条语句不但改变了s所占的一个字节,还把和s相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。
让我们再来看一例:
例十八:
1、chara;
2、int*ptr=&a;
...
...
3、ptr++;
4、*ptr=115;
该例子完全可以通过编译,并能执行。但是看到没有?第3句对指针ptr进行自加1运算后,ptr指向了和整形变量a相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。而第4句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。
在指针的强制类型转换:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的类型)大于sizeof(ptr1的类型),那么在使用指针ptr1来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2的类型)小于sizeof(ptr1的类型),那么在使用指针 ptr1来访问ptr2所指向的存储区时是不安全的。至于为什么,读者结合例十七来想一想,应该会明白的。
Smarty是一款PHP官方支持的模板,有点小复杂。
重要的是LIBS这个目录,以及在数据文件中,对“templates”,“templates_c”,“configs”以及“cashe”四个目录的设定;什么地方可以放模板,什么地方可以放数据,确实让开始学习的我头痛了一番。
比如,我要写一个 HelloWorld 程序,步骤如下:
1. 到 SMARTY 下载稳定的SMARTY包。
2. 在APACHE相应目录下面,新建一个文件夹:“helloworld”。
3. 将下载下来的smarty包内的“libs”文件夹完完全全的拷贝到“helloworld”文件夹。
4. 接下来就是最最恶心的一步了,你需要在“helloworld”手工新建四个文件夹:“cashe”、“configs”、“templates”,“templates_c”(不要修改文件夹名称),这四个文件将与“libs”文件夹并行。
5. 我们接着写一个模板文件,是的,它必须安放在“templates”文件夹下面(不要问我为什么,其实我也不知道~),你可以将它命名为index.tpl,或者index.html,推荐前者(smarty的模板格式,虽然我觉得其内容和html没有什么区别),输入如下代码:
2. <html>
3. <head>
4. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
5. <title>Smarty</title>
6. </head>
7. <body >
8. {$hello_templates}
9. </body>
10. </html>
6. 接着,我们需要写一个数据文件,这个才是重头。在“helloworld”根目录下面,新建一个index.php文件,写入如下代码:
2. //引用类文件
3. require '/libs/Smarty.class.php';
4.
5. $smarty = new Smarty;
6.
7. //设置各个目录的路径,这里是安装的重点
8.
9. $smarty->template_dir = "templates";
10.
11. $smarty->compile_dir = "templates_c";
12. $smarty->config_dir = "config";
13. $smarty->cache_dir = "cache";
14.
15. //smarty模板有高速缓存的功能,如果这里是true的话即打开caching,但是会造成网页不立即更新的问题,当然也可以通过其他的办法解决
16. $smarty->caching = false;
17.
18. //赋值
19. $smarty->assign("hello_templates","Hello World!");
20.
21. //引用模板文件,注意这里的index.tpl路径,不是真实的路径,不用写成/templates/index.tpl样式
22. $smarty->display('index.tpl');
23.
24. ?>
如果出现找不到文件:可能是如下变量设置或者默认设置有问题:
$tpl->template_dir = "./aaa";//修改到aaa目录,默认是templates,这个aaa和templates是一个层级的,然后放入模板文件即可!
echo $tpl->template_dir;
echo $tpl->config_dir ;
echo $tpl->compile_dir;
echo $tpl->cache_dir;