2009年3月29日星期日

天秤座大剖析

1. 偏好低调,不爱出风头,喜欢安静

2.
想借钱找天平成功的机率比较高,如果你和平子交情不错,他们又有足够的钱借你。因为平子不懂怎么拒绝别人。借出去的钱还8好意思催别人还。

3. 宽容 平子只会对别人宽容却不会对自己宽容 对自己要求挺严格的
经常和自己过不去别问理由,平子自己也不知道
  
4. 天平不擅表达
如果你能感觉平子对你三分的喜爱,事实上会有五分;如果你能感觉到五分,事实上会有七分。天平是不坦白的,会淡化感情的表达,会压抑自己的情绪。
  
5. 审美观超强 爱一切美好的事物。比如帅哥美女,但只是欣赏,当作艺术品。
  
6. 能忍
比如明明想见一个人,却不会见面。比如明明想知道谁的消息,却什么都不问。除非不想忍。
  
7. 不爱发短信,不爱打电话 懒蛋一个。对特别的人会例外。
  
8. 自尊心很强! 强过金钱,强过事业,也强过爱情。
9.
天平需要慢慢相处,因为平子是个被动的星座,慢热的星座,放不开的星座,认识时间越久对你越好。如果只是你喜欢平子,平子却不喜欢,频频接触的结果则是会对你越来越冷淡。
  
10.
一见钟情很难发生在天平身上,平子的爱需要时间。会喜欢很多人,却很难爱上一个人。喜欢细水长流的感情,暖暖细流,长长久久。
  
11. 平子知道什么是感恩图报。你对他们仁,他们就对你义。
  
12. 平子的喜欢不等于爱。多情而不滥情。
  
13.
除了感性,天平也可以理性的可怕。会很现实的考虑两个人的将来,如果觉得没有未来,就放开。
  
14. 对感情有点迟钝。
  
15. 配合 平子喜欢配合别人,迁就别人。自我意识比较薄弱。
  
16. 其实天平不擅交际的,一般情况下不喜欢说太多。
小老实,小保守。虽然嘴巴小笨却知道什么话该说,什么话不该说。不过在人多的场合也不会扭扭捏捏小家子气,说话会大大方方。这也是平子气质出众人缘好的一个原因。
  
17. 天平重视内在多于外在,重视精神多于物质。
  
18. 天平对朋友没什么要求 人好就行
就算心直口快老得罪人也无所谓因为天平不爱生气 不会记仇
对谁都会宽容就算伤害过自己的人。只记得别人的好。
  
19. 讨厌谁不会表现出来,只是不爱和你说话,或者干脆不睬你。
  
20. 有了心爱的人后就会变得很没安全感,怕寂寞
  
21.
不要献媚的乱夸奖,平子没你想象的那么虚荣,也没你想象的那么白痴。他们能清楚的分出哪句是虚情,哪句是假意。
  
22. 天平的头脑中性冷静 不论男女分析对待问题不会因为性别问题而偏激
处变不惊天平做事情处理事情好像一直都会不紧不慢
就算在一种很混乱的情况下也能冷静的思考不知道为啥 归天性吧
  
23. 不喜欢做决定,小事情随便怎么样都行,没所谓。 大事情很喜欢听朋友的意见
如果你是天平可以信赖的人 他们会问你的意见你需要做的只是分析
决定天平会自己来做如果他们觉得你分析的有道理会在心里琢磨 可行就采纳
如果天平有了自己的想法一旦下了决定就算全天下反对 几乎没有改变的可能
一意孤行其实多多少少有些靠直觉行事,不过天平的直觉大多时候还蛮准的
  
24. 温柔 温柔是天平的本性 就算外表看来是阿飞或者小太妹里头绝对有温柔的一面
  
25. 天平喜欢和自己喜欢的人斗斗嘴 却不会大声吵架 或者说根本不会吵架
你想吵可以天平只会转头走人
  
26.平子有善辩的口才,被别人误会的时候却不爱解释。表问为什么,就是不爱解释。
  
27.
不喜欢伤害任何人,宁愿伤害自己。前景不乐观时会对喜欢的人冷静的说恨话,会口是心非。为的是不伤害任何人,结果反到是既伤了别人,伤得更多却也正是平子自己。
  
28.
吃软不吃硬的一族。你软,平子就对你没脾气。你硬,平子就比你还横。有项调查,十二星座谋杀记录最少的就是善良的天平,不过发现大多死亡记录里都是和对方同归于尽的。。。哈哈哈平子脾气倔,惹怒了绝对敢和你同归于尽。
  
29. 让天平消气很容易 不管男平子还是女平子也不管对异性或者同性 只要你撒撒娇
说两句软话保准天平不会再有脾气
  
30. 天平对朋友绝对够义气 不过很多时候天平往往会低估自己这一点
只有碰到事情才能发现偶原来有把钱借给朋友交学费 搞到最后自己的学费米钱交
  
31. 不管对朋友还是恋人 在外边天平绝对袒护的是你 就算错的是你
没道理的是你也会和你一起对付外敌 不过事后你们单独相处的时候
才会告诉你说他们觉得哪里哪里似乎是你的不妥
  
32.
不要因为在平子面前丢了人而自卑或在意,因为平子绝对不会在意。平子觉得有时候丢人也是可爱的一种表现。
  
33. 不会打击别人,开玩笑时候除外。
  
34.
一般人都觉得天平会做人,不会得罪人。那是因为天平觉得没有和他们认真的必要。其实天平说话会很直,只有对好朋友说话才会一根肠子通到底,老说大实话。太直接偶尔会小伤人,但都是为了朋友好。比如知道好朋友的男朋友和其他女生在一起,一定不会怕好朋友伤心而因此隐瞒,天平会直言不讳的帮好朋友看清事实,面对事实,减少以后的伤害。对此,平子当作身为朋友义不容辞的责任。
  
35.
天平不喜欢向别人提要求。对朋友一点要求都不会提。如果向你提要求,就代表对你的喜欢和信赖非同一般,不过就算再信赖一个人,天平也很少开口要求什么。因为天平是随遇而安的,心态很平静悠哉悠哉的
而且不喜欢欠任何人的人情
  
36.
不喜欢打探朋友的太多私事,因为觉得不礼貌。也不喜欢逼喜欢的人的讲不愿讲的,平子认为这是一份尊重。  
37.
对于恋人的过去,过去就是过去,天平在乎的是现在的坦诚。同样,平子不会把上一份感情留在现在生活里,不允许心里死的人和眼前喜欢的人相提并论,因为会告诉自己,现在爱的才是最好的。同样,天平死去的爱情,是绝对没有可能爱火重燃的。
  
38. 平很好骗,因为单纯。平子不会没事就想着别人是不是在骗自己。
  
39. 失恋的时候喜欢听悲伤地情歌。既然伤就伤个够吧.
还会删除对方的电话号码联系方式,以及丢掉有回忆的东西。
  
40.
天平对理财没什么概念。经常不知道自己帐户里有多少钱,也不知道花了多少钱。对父母挺舍得花钱。对好朋友和喜欢的人也不会把帐算的很清。
  
41.
你可以欠天平的钱或者什么,但是天平却不喜欢欠别人的。天平欠别人钱的时候会天天惦记着,老觉得不爽,比别人欠自己钱还不爽。
理性的天平更感性,有颗不切实际单纯美好的心灵。比如看到蓝蓝的天会感动,会觉得生活真美好。。。比如看到乞讨的老人,会觉得可怜,然后想着以后有能力要帮助好多好多可怜的人...
所以,当一个天平对你说他的愿望是"世界和平,人人幸福"的时候,不要怀疑它的真实性,因为那颗美好稚气的心绝对是真的
42.
天平是脆弱的一族,却不会让别人看到自己的weak,甚至最好的朋友。心里有什么憋闷,不喜欢找好朋友倾诉,有时候宁愿拉着一个陌生人倾诉。因为陌生人听完就不会再记得那谁的故事。平子会继续撑起他们所谓的坚强的壳子。
  
43.
有条不紊的平子很少很难会情绪失控,也很少有人能让天平情绪失控,如果有就是看的很重的事或人。失控时间不会很久,短些就是几秒钟,长些就是几分钟。然后很快又会恢复先前的平静和冷静。自我调节能力蛮强。
  
44. 心情低落或受伤的平子不需要任何人的安慰,更加不会自暴自弃一蹶不振,no
way!
需要的只是空间时间以及那颗乐观的心。会自我安慰,自己劝自己看开,自己为自己疗伤,自己把自己武装。这个时候最好让平子自己呆着,因为朋友的出现不会对平子有什么帮助,而且,平子更不希望朋友看到自己低落受伤的样子。
45.
天平是乐观的,不喜欢对人抱怨,更不喜欢听别人找自己诉苦。所以你最好别在平子面前抱怨,就算你是他们最好的朋友。当你抱怨第一句时平子会安慰你,抱怨第二句是会附和你,抱怨第三句的时候,保证天平已经失去了继续听下去的耐心,虽然不会表现出来,但你会发现平子已经开始不说话了,只是"恩恩恩""是吖""哦"。天平可以帮你出点子,做分析,但绝对受不了唠唠叨叨抱怨诉苦。
  
46.
天平吃醋时不爱说话,也不会明讲。只是你会发现平子有点气嘟嘟的一个人生闷气,说话语气有点阴阳怪调,自然就8会给你好脸色看了。
  
47.
少说承诺,一旦说出就会履行;很少说"我爱你",说了出来就是真的爱你。喜欢用行动来证明一切。责任心重。
  
48.
平子不爱怀疑别人,所以也别怀疑平子诚实度。怀疑的结果是让平子伤,生气,也会变得不再相信你。
  
49. 喜欢创造小惊喜,喜欢制作小浪漫。
  
50.
如果两个互相相爱的人,天平觉得自己的存在阻碍对方的前途或发展,会选择自动离开。很爱很爱你,所以愿意,舍得让你,往更多幸福的地方飞去.很爱很爱你,所以愿意不牵绊你,飞向幸福的地方去.
  
51.
甩掉天平很容易,只要给一个理由,就算是你编出来的。不管理由是真是假,但请你亲口讲出来,天平都会离开,因为这是平子拿来替你说服平子自己的理由。平子不会纠缠,也不会乞求爱,更不会要施舍的爱。爱里的平子异常要强。
52.
接受能力很强,什么都可以理解,什么都可以接受,但请你亲口说出来。因为即使平子猜得到,从别人那里听的到,也希望亲耳听你说出来。因为关于你,平子在乎的只是你
  
53.
如果你不能给平子什么,不要招惹他们,他们比你想象的单纯,也没你想象的坚强
  
54.
平子相信分手的恋人还可以做朋友,只要对方愿意,希望大家都好。如果曾经那些叫**,就不要带着恨吧。
  
55.
如果你表白后,一个平子对你说"不喜欢你"或者类似的话,就等于判了你死刑。这个时候不要想着继续努力来感动平子。没用的。平子就像弹簧,你施力越大,他们就把你弹的越远。平子对自己不喜欢的人可以很残忍,就算心里会内疚也会继续残忍,好像这也是平子最冷血的时候。如果还想做朋友,就什么都不要做。
  
56. 被平子爱的人是幸福的,他们会为你而活,请好好珍惜。
  
57.
爱上平子的人是痛苦的,如果平子爱的不是你。结果往往是对平子又爱又恨又不愿放弃。
  
58.
小事糊涂,大事聪明。有些平子看起来是正常人,可经过交谈后会发现有些很简单的东西都不知道。不要以为那是在装,平子就是这样,出人意料的单纯,大家都懂的事情会不懂。发觉了单纯之后也不要以为平子就蠢的什么都不懂,大事情照样可以比你更有智慧,更有主见,更有见地。平子没你想象的精明,他们本不是精打细算的人;平子没你想象的愚蠢,他们有的是大智慧。

at命令的用法

at命令可以在规定时刻执行指定的任务。
crontab命令可以周期性的执行一些人物。
区别:
at: 这个工作公执行一次就从Linux系统的流程中取消
crontab:这个工作将持续例行性的做一去.

用法一:
命令行输入:
at 17:20 tomorrow 回车会出现:
warning: commands will be executed using /bin/sh
at> 这时候输入到时间后要执行的命令,可以输多行,输完了ctrl+d.
就可以了

用法二:
将命令保存成文件再执行:
at -f 文件名 17:20 tomorrow 回车.
这个文件最好加个可执行权限.
另外,如果你要重启的话,还要考虑root权限的问题.

说明:你运行的程序到时会执行,但并不显示出来。要想显示出来,需要指定显示位置。如
at now + 1 minutes
warning: commands will be executed using /bin/sh
at>export DISPLAY=:0 && firefox
at><EOT>
一分钟后,就会看到firefox显示在第0个桌面。

或者你可以测试一下at命令,让它执行:
touch ~/amai

在x下,也可以用at命令作提醒用。
执行命令:
at 12:00
MAIL=/dev/null DISPLAY=:0 zenity --info --text "time to lunch"
at> [ Ctrl + d ]
ps. 在 at 提示下设置 MAIL=/dev/null 可让提醒信息结束后不发送 e-mail
执行结果:

图文并茂教你学游泳

本贴的目标对象是那些希望学会游泳,但身边又没有人教的,身材比较适中的人。
比较适中,指身材不是极度的胖,也不是极度的瘦。否则,我所讲述的内容可能不适合与你。需要说明的是,我所讲述的游泳方法主要目的是让你能在水池里自主的动起来,不会被淹死,并不是让你能像那些高手一样飞快的向前游起来,要达到这个程度,请报名参加游泳培训班。但是,如果掌握了基本的游泳方法,获得更高的提升也就不是空谈了。
我知道山上游泳高手一把一把的,如果你有更适合新手学会游泳的方法和建议,请不要吝啬。
首先说一下我自己的游泳状况,我打小在乡下长大,没有正规的学过游泳,我的一切游泳都起始于经典的狗刨式。样子难看,却安全牢靠。我的第一次游泳是5、6岁的时候被我老爸套上了救生圈往河里一推,然后在水里哇哇大哭...
两周以后因为不肯从河里出来,而被我爸爸拉上岸,一顿"桑活"然后照样哇哇大哭...
现在你已经知道了,我的游泳是野路子出身,但是那又怎么样呢?各位没有人来教会游泳的可不都是准备走野路子这条路吗?

因为是野路子,所以你别拿来跟标准游泳比较了,但是可能要比教科书上那些更好懂。(我没看过游泳教科书,不知道它写得是不是浅显)

器材准备:游泳装、游泳帽、游泳眼镜、鼻夹、耳塞
耳塞看你自己乐意,不要也无所谓,要买么也很便宜
鼻夹对新手有帮助,可以避免慌乱 因为它便宜,就要一个吧
游泳眼镜一定要有,也就几十块钱的东西,你少几包烟,少涂几次洗面奶就回来了。不用特别好,300、400的那是装B用的。防水性好,戴着舒服就成。一般新的泳镜内层都有防雾
涂层,尽量别用手去摸。低于40块的也别考虑了,橡胶边的很不可靠,还是要买硅胶边的。
有的游泳池不带泳帽不给下水的 买一个吧,硅胶的也就12块 布的才4块、5块
泳衣,建议买色彩鲜艳的。这样新手比较容易让救生员注意到你。
其实所有人都会游泳,只是有的人还不知道自己会游泳而已。


各位应该都能知道这一点:那些不会水的人,掉进了平静的河水里(进了奔腾的黄河,菲尔普斯、庄泳、北岛...下去了也一样死路一条),并不是直接就沉下去淹死了,而是先扑腾。扑腾到最后没有力气了,就淹死了。可见,只要你能动,就可以帮助自己浮起来。而会游泳的人就知道如何花较少的力气保持自己浮在水面上的状态。会不会游泳的差别仅此而已。

慌乱,是导致在水里出事的重要原因。所以:
第一步:请先克服自己在水里的慌乱。
方法不难,下到浅水区,戴上泳镜,吸一口气,憋住它,用优雅的动作把脑袋慢慢埋到水里,看看水下的世界是多么美妙。美女的大腿、帅哥的屁股...
当然,更要看看清楚壁砖... 熟悉了周围的环境,就不容易慌乱。
20秒钟以后,也许你已经觉得有些缺氧了,不要慌张,还用优雅的动作慢慢的抬起头,出了水面不要立即张嘴呼吸,继续憋气一秒或半秒。让你脑袋上的水流下来,不要灌进嘴巴里。(如果你连20秒也憋不住,去医院查心肺功能吧,别下水了)
注意,新手会因为觉得有些缺氧,非常着急的把脑袋顶出水面,紧急的吸气,是呛水的重要原因。一旦呛水,就会慌乱。所以,吸气这个动作,请尽量让自己保持优雅。
刚出水面,被你脑袋顶起来的水花还没掉下来,这个时候你猛吸气,它很容易就会进你的嘴。所以,注意这个细节,出水以后,再忍一秒钟。或者,出水的速度尽量的慢,让水花不要跟起来。
在你出水的时候,戴着眼镜的人也更容易知道自己脑袋处于什么状况了,闭着眼你都不知道周围水花状况如何。有些新手因为觉得反正还没学会游泳,泳镜以后买好了,我觉得其实这个是错的思路。
当你可以从容的把自己浸没在水里,又可以从容的出水,并且换气的时候不会被呛到,那么你已经离学会游泳不远了。容易吧?

第二步:原地沉浮换气
因为我假设的条件是没有人教你,所以这一步要优先学会。学会之后就淹不死了。
现在走到水大约齐胸的深度,靠近池壁,(其实这只是给你自己一个心理安全的暗示)要满足这个条件:慢慢一伸脚,可以轻松的站出水面。
还是跟刚才那样,吸气,屏住。然后,稍微弯曲一下腿。让自己整个人沉下去,但头不要低下去。这个时候你会发现,原来你不会一下子沉到底的。而是会随着缓慢的节奏,一起一伏的在水面上"藤"。好玩吧?
你有没有注意到,当你的嘴巴高出水面的时候,大致是多少时间?而你吸气的时候,你才用了多少时间?
其实,嘴巴浮出水面的时间,远远大于你吸气所需要的时间。你需要注意的是保持冷静,然后在脑袋呈上升相的时候,呼气出去,并吸气进来。然后又屏气,然后你会随着下一个沉浮节奏,脑袋又来到了水面以下。
如此循环往复。你会不会纳闷,那些人是怎么淹死的?原来在平静的水里,人手脚不动都不会死掉。
注意,有的人吸气量不大,浮动的时候嘴巴不完全露出水面,这个时候只要手往下轻轻一压,就会明显的出水了。(这个时候你还是保持腿轻微弯曲,暂时我们不需要腿的帮忙好了)
怎么样?容易吧?
在这个环节中,还是要注意的是,在嘴巴出水面之后,不要让自己呛到水,一呛水,新手立即就紧张了,后面的就全乱套了。
好了,现在假设你觉得嘴巴浮出水面的高度不够,你想浮得更高一些,很容易,再加上两条腿,往下轻轻的推蹬就可以。注意,是轻轻的。剧烈的蹬水会让你抬起来很高,却再沉下去的幅度也大。没必要。

我们回忆一下奥运会游泳比赛里那些高手,其实他们也是脑袋在水里一进一出的。虽然他们可以做到完全把脑袋始终保持在水面以上,但是没那个必要。出水那一点点时间足够我们换气了。

第三步:让自己有方向性的动起来。
既然你已经可以让自己在水里沉浮了,那么这个也就很容易了。只需要两手往后轻轻划动就可以了。
双手往前直伸 然后打开,往后推 收进来 再往前伸 如此循环
注意:这个时候我们还没有配合到脚的动作 蛙泳是泳池里很常见的泳姿
看看别人就知道了 在双手往前伸的时候,双腿往后推 轻轻的推
目前我们所有的动作都轻轻的来动的速度如何不重要,总之你在动了。
电视里,那些游泳高手人几乎是横在水中的,这样阻力最小。但我们目前没达到这个水准,就倾斜着前进吧。这样抬头换气也更容易。我们就仰着头往前划水好了,样子难看一些就难看一些好了。大家都是这么过来的。
有的人觉得自己怎么老在原地划水,那是因为你往后的划水动作跟你的腿部动作不协调。于是你前进的动力被自己的腿部动作给打乱了。这是没办法立即改正的,要在水里多练习。时间一长自己会纠正过来。
第四步:让自己能在向前滑动的过程中换气

如果在做这些动作的时候你的嘴巴都是在水面以上的,那么不存在这个问题,但事实上大部分情况下,我们游泳的时候总是嘴巴在水面上下起伏的。请结合第一第二部分,在嘴巴浮出水面的时候换气。轻轻的 这不难。
还有一种状况是,游的时候头是往下压的,低在水中,然后该换气的时候就抬不起来。为避免新手的这种状况,你尽量不要让自己身体横向的前进,就斜向的前进好了。虽然阻力大了点,速度慢了点,累也累了点,但是这容易让你换气。作为新手,只要能有所进展,就可以建立起信心,不会半途而废,学起来会更快。


请注意蛙泳高手在水里的姿势,他们几乎是平的
而你注意一下新手在水里的表现

新手在水里头往往是朝下的,背是弓的,这样的姿势,你换成北岛康建来,他也会觉得抬头很费力的

所以,我给新手的建议是:
不要让自己在水里呈水平位置,刚开始,大家没必要全力去模仿高手。
高手要唤气,也要把背仰起,然后抬头的。所以,新手可以一开始就始终让自己头向上拔高,手脚在下面折腾好了。
人在水里,需要让自己憋气成为一种很自然的感觉,就是感觉那种只要嘴唇边有水了,就立即闭气的感觉,不需要动脑子去指挥嘴。再加上在水里不会慌乱了,游泳这事情基本也就成了。

踩水的时候,请结合我说的第2步,在原地沉浮时候一起用 效果很好 可以很轻易的帮助嘴巴浮到水面以上
其实踩水是非常没有技术含量的,仅仅是你两腿往下轻轻的推动
但是,你可以这样尝试:在张开的两腿 在往下蹬的时候,有意识的让膝盖先并拢 然后才是两脚并到一起
注意,动作要轻轻的
否则你猛的浮起来了,必然紧接着再猛的沉下去 只要嘴巴略高出水面就可以了
也请注意这幅图片里,我故意把人的半个脑袋没在水里,也就是说,在水里,这样的嘴巴在水面偏下的位置是很正常的,不用惊慌。手或脚稍微加点力就上去了
不会水的人由于紧张,会猛的蹬脚划手,这样一瞬间他是起了水面了,可是并没有利用好这段时间换气。接着他又沉下水面了。于是呛水了,必然的导致了惊慌,于是全乱套了,动作更剧烈了。剧烈的动作必然跟随巨大的氧气消耗,越是缺氧越不容易保持冷静,最后全乱套了,形成一个恶性循环... 直到最后淹死。惨剧就是这么来的。
我一般在水里不踩水,只靠手 也不用往下,前后轻微摆动就可以让人浮起来,这样更省力 我是个懒惰胚,用脚太累了


C语言中可变参数的用法

一、什么是可变参数
我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点"…"做参数占位符),实际调用时可以有以下的形式:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
以上这些东西已为大家所熟悉。但是究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实现,这个问题却一直困扰了我好久。本文就这个问题进行一些探讨,希望能对大家有些帮助.

二、写一个简单的可变参数的C函数
先看例子程序。该函数至少有一个整数参数,其后占位符…,表示后面参数的个数不定.
在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的值.
函数代码如下:
//示例代码1:可变参数函数的使用
#include "stdio.h"
#include "stdarg.h"
void simple_va_fun(int start, ...)
{
va_list arg_ptr;
int nArgValue =start;
int nArgCout=0; //可变参数的数目
va_start(arg_ptr,start);
//以固定参数的地址为起点确定变参的内存起始地址。
do
{
++nArgCout;
printf("the %d th arg: %d\n",nArgCout,nArgValue);
//输出各参数的值
nArgValue = va_arg(arg_ptr,int);
//得到下一个可变参数的值
} while(nArgValue != -1);
return;
}
int main(int argc, char* argv[])
{
simple_va_fun(100,-1);
simple_va_fun(100,200,-1);
return 0;
}
下面解释一下这些代码
从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
⑴由于在程序中将用到以下这些宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思.
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件.
⑵函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变
量是存储参数地址的指针.因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。
⑶然后用va_start宏初始化⑵中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数.
⑷然后依次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型,就可以得到参数的值。
⑸设定结束条件,这里的条件就是判断参数值是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目,读者在看完这几个宏的内部实现机制后,自然就会明白。

(二)可变参数在编译器中的处理
我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,
由于1)硬件平台的不同
2)编译器的不同,所以定义的宏也有所不同,下面看一下VC++6.0中stdarg.h里的代码(文件的路径为VC安装目录下的\vc98\include\stdarg.h)
typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
下面我们解释这些代码的含义:
1、首先把va_list被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的
2、定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大小。在我的机器上直接用sizeof运算符来代替,对程序的运行结构也没有影响。(后文将看到我自己的实现)。
3、va_start的定义为 &v+_INTSIZEOF(v)
,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap,
v)以后,ap指向第一个可变参数在的内存地址,有了这个地址,以后的事情就简单了。
这里要知道两个事情:
⑴在intel+windows的机器上,函数栈的方向是向下的,栈顶指针的内存地址低于栈底指针,所以先进栈的数据是存放在内存的高地址处。
(2)在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。
|——————————————————————————|
| 最后一个可变参数 | ->高内存地址处
|——————————————————————————|
...................
|——————————————————————————|
| 第N个可变参数 |
->va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N个可变参数的地址。
|——————————————— |
………………………….
|——————————————————————————|
| 第一个可变参数 |
->va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一个可变参数的地址
|——————————————— |
|———————————————————————— ——|
| |
| 最后一个固定参数 | -> start的起始地址
|—————————————— —| .................
|—————————————————————————— |
| |
|——————————————— | -> 低内存地址处

(4)
va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到下一个参数的起始地址。
因此,现在再来看va_arg()的实现就应该心中有数了:
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
这个宏做了两个事情,
①用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值
②计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。
(5)va_end宏的解释:x86平台定义为ap=(char*)0;使ap不再
指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.
在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型.
关于va_start, va_arg, va_end的描述就是这些了,我们要注意的
是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

(三)可变参数在编程中要注意的问题
因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,
可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能
地识别不同参数的个数和类型.
有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数
printf是从固定参数format字符串来分析出参数的类型,再调用va_arg
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的.
例如,在C的经典教材《the c programming
language》的7.3节中就给出了一个printf的可能实现方式,由于篇幅原因这里不再叙述。
(四)小结:
1、标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址,编译器是不知道参数的实际数目的。
2、在实际应用的代码中,程序员必须自己考虑确定参数数目的办法,如
⑴在固定参数中设标志—— printf函数就是用这个办法。后面也有例子。
⑵在预先设定一个特殊的结束标记,就是说多输入一个可变参数,调用时要将最后一个可变参数的值设置成这个特殊的值,在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.
无论采用哪种办法,程序员都应该在文档中告诉调用者自己的约定。
3、实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:
①函数栈的生长方向
②参数的入栈顺序
③CPU的对齐方式
④内存地址的表达方式
结合源代码,我们可以看出va_list的实现是由④决定的,_INTSIZEOF(n)的引入则是由③决定的,他和①②又一起决定了va_start的实现,最后va_end的存在则是良好编程风格的体现,将不再使用的指针设为NULL,这样可以防止以后的误操作。
4、取得地址后,再结合参数的类型,程序员就可以正确的处理参数了。理解了以上要点,相信稍有经验的读者就可以写出适合于自己机器的实现来。下面就是一个例子
(五)扩展——自己实现简单的可变参数的函数。
下面是一个简单的printf函数的实现,参考了<The C Programming
Language>中的156页的例子,读者可以结合书上的代码与本文参照。
#include "stdio.h"
#include "stdlib.h"
void myprintf(char* fmt, ...)
//一个简单的类似于printf的实现,//参数必须都是int 类型
{
char* pArg=NULL; //等价于原来的va_list
char c;

pArg = (char*) &fmt; //注意不要写成p = fmt
!!因为这里要对//参数取址,而不是取值
pArg += sizeof(fmt); //等价于原来的va_start

do
{
c =*fmt;
if (c != '%')
{
putchar(c); //照原样输出字符
}
else
{
//按格式字符输出数据
switch(*++fmt)
{
case 'd':
printf("%d",*((int*)pArg));
break;
case 'x':
printf("%#x",*((int*)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等价于原来的va_arg
}
++fmt;
}while (*fmt != '\0');
pArg = NULL; //等价于va_end
return;
}
int main(int argc, char* argv[])
{
int i = 1234;
int j = 5678;

myprintf("the first test:i=%d\n",i,j);
myprintf("the secend test:i=%d; %x;j=%d;\n",i,0xabcd,j);
system("pause");
return 0;
}
在intel+win2k+vc6的机器执行结果如下:
the first test:i=1234
the secend test:i=1234; 0xabcd;j=5678;

物理内存与线性地址的关系

如果你不懂什么是逻辑地址、线性地址、物理地址和虚拟地址,请先看:
http://linux.chinaunix.net/bbs/viewthread.php?tid=919019&extra=page%3D1%26amp%3Bfilter%3Ddigest

在硬件工程师和普通用户看来,内存就是插在或固化在主板上的内存条,它们有一定的容量——比如64
MB。但在应用程序员眼中,并不过度关心插在主板上的内存容量,而是他们可以使用的内存空间——他们可以开发一个需要占用1
GB内存的程序,并让其在OS平台上运行,哪怕这台运行主机上只有128
MB的物理内存条。而对于OS开发者而言,则是介于二者之间,他们既需要知道物理内存的细节,也需要提供一套机制,为应用程序员提供另一个内存空间,这个
内存空间的大小可以和实际的物理内存大小之间没有任何关系。
我们将主板上的物理内存条所提供的内存空间定义为物理内存空间;将
应用程序员看到的内存空间定义为线性空间。物理内存空间大小在不同的主机上可以是不一样的,随着主板上所插的物理内存条的容量不同而不同;但为应用程序员
提供的线性空间却是固定的,不会随物理内存的变化而变化,这样才能保证应用程序的可移植性。尽管物理内存的大小可以影响应用程序运行的性能,并且很多情况
下对物理内存的大小有一个最低要求,但这些因素只是为了让一个OS可以正常的运行。
线性空间的大小在32-bit平台上为4
GB的固定大小,对于每个进程都是这样(一个应用可以是多进程的,在OS眼中,是以进程为单位的)。也就是说线性空间不是进程共享的,而是进程隔离的,每
个进程都有相同大小的4
GB线性空间。一个进程对于某一个内存地址的访问,与其它进程对于同一内存地址的访问绝不冲突。比如,一个进程读取线性空间地址1234ABCDh可以读
出整数8,而另外一个进程读取线性空间地址1234ABCDh可以读出整数20,这取决于进程自身的逻辑。
在任意一个时刻,在
一个CPU上只有一个进程在运行。所以对于此CPU来讲,在这一时刻,整个系统只存在一个线性空间,这个线性空间是面向此进程的。当进程发生切换的时候,
线性空间也随着切换。所以结论就是每个进程都有自己的线性空间,只有此进程运行的时候,其线性空间才被运行它的CPU所知。在其它时刻,其线性空间对于
CPU来说,是不可知的。所以尽管每个进程都可以有4
GB的线性空间,但在CPU眼中,只有一个线性空间的存在。线性空间的变化,随着进程切换而变化。
尽管线性空间的大小和物理内存的大小之间没有任何关系,但使用线性空间的应用程序最终还是要运行在物理内存中。应用所给出的任何线性地址最终必须被转化
为物理地址,才能够真正的访问物理内存。所以,线性内存空间必须被映射到物理内存空间中,这个映射关系需要通过使用硬件体系结构所规定的数据结构来建立。
我们不妨先称其为映射表。一个映射表的内容就是某个线性内存空间和物理内存空间之间的映射关系。OS
Kernel一旦告诉某个CPU一个映射表的位置,那么这个CPU需要去访问一个线性空间地址时,就根据这张映射表的内容,将这个线性空间地址转化为物理
空间地址,并将此物理地址送到地址线,毕竟地址线只知道物理地址。
所以,我们很容易得出一个结论,如果我们给出不同的映射表,
那么CPU将某一线性空间地址转化的物理地址也会不同。所以我们为每一个进程都建立一张映射表,将每个进程的线性空间根据自己的需要映射到物理空间上。既
然某一时刻在某一CPU上只能有一个应用在运行,那么当任务发生切换的时候,将映射表也更换为响应的映射表就可以实现每个进程都有自己的线性空间而互不影
响。所以,在任意时刻,对于一个CPU来说,也只需要有一张映射表,以实现当前进程的线性空间到物理空间的转化。
--------------------------------------------------------------------------------
2. OS Kernel Space & Process Space
由于OS
Kernel在任意时刻都必须存在于内存中,而进程却可以切换,所以在任意时刻,内存中都存在两部分,OS
Kernel和用户进程。而在任意时刻,对于一个CPU来说只存在一个线性空间,所以这个线性空间必须被分成两部分,一部分供OS
Kernel使用,另一部分供用户进程使用。既然OS
Kernel在任何时候都占用线性空间中的一部分,那么对于所有进程的线性空间而言,它们为OS
Kernel所留出的线性空间可以是完全相同的,也就是说,它们各自的映射表中,也分为两部分,一部分是进程私有映射部分,对于OS
Kernel映射部分的内容则完全相同。
从这个意义上来说,我们可以认为,对于所有的进程而言,它们共享OS
Kernel所占用的线性空间部分,而每个进程又各自有自己私有的线性空间部分。假如,我们将任意一个4
GB线性空间分割为1 GB的OS Kernel空间部分和3
GB的进程空间部分,那么所有进程的4 GB线性空间中1 GB的OS
Kernel空间是共享的,而剩余的3
GB进程空间部分则是各个进程私有的。Linux就是这么做的,而Windows NT则是让OS
Kernel和进程各使用2 GB线性空间。
--------------------------------------------------------------------------------
3. Segment Mapping & Page Mapping
所有的线性空间的内容只有被放置到物理内存中才能够被真正的运行和操作。所以,尽管OS
Kernel和进程都被放在线性空间中,但它们最终必须被放置到物理内存中。所以OS
Kernel和所有的进程都最终共享物理内存。在现阶段,物理内存远没有线性空间那么大——线性空间是4
GB,而物理内存空间往往只有几百兆,甚至更小。另外即使物理内存有4
GB,但由于每个进程都可以有3 GB线性空间(假如进程私有线性空间是3
GB的话),如果把所有进程的线性空间内容都放在物理内存中,明显是不现实的。所以OS
Kernel必须将某些进程暂时用不到的数据或代码放在物理内存之外,将有限的内存提供给当前最需要的进程。另外,由于OS
Kernel在任何时候都有可能运行,所以OS
Kernel最好被永远放在物理内存中。我们仅仅将进程数据进行换入换出。

线性空间到物理空间的映射需要映射表,映射表的内容是将某段线性空间映射到相同大小的物理内存空间上。从理论上,我们可以使用两种映射方法:变长映射,和
定长映射。变长映射指的是根据不同的需要,将一个一个变长段映射到物理内存上,其格式可以如下(线性空间段起始地址,物理空间段起始地址,段长度)。假如
一个进程有3个段:10M的数据段,5M的代码段,和8K的堆栈段,那么就可以在映射表中建立3项内容,每一项针对一个段。这看起来没有问题。但假如现在
我们的实际的内存只有32M,其中10M被内核占用,留给进程的物理空间只有22M,那么此进程在运行时,就占据了10M+5M+8K的内存空间。随后当
进程发生切换时,假如另一个进程和其有相同的内存要求,那么剩余的22M-(10M+5M+8K)明显就不够用了,这时只能将原进程的某些段换出,并且必
须是整段的换出。这就意味着我们必须至少换出一个10M的数据段,而换出的成本很高,因为我们必须将这10M的内容拷贝到磁盘上,磁盘I/O是很慢的。
所以,使用变长的段映射的结果就是一个段要么被全部换入,要么被全部换出。但在现实中,一个程序中并非所有的代码和数据都能够被经常访问,往往被经常访
问的只占全部代码数据的一部分,甚至是一小部分。所以更有效的策略是我们最好只换出那些并不经常使用的部分,而保留那些经常被使用的部分。而不是整个段的
换入换出。这样可以避免大块的慢速磁盘操作。
这就是定长映射策略,我们将内存空间分割为一个个定长块,每个定长块被称为一个
页。映射表的基本格式为(物理空间页起始地址),由于页是定长的,所以不需要指出它的长度,另外,我们不需要在映射表中指定线性地址,我们可以将线性地址
作为索引,到映射表中检索出相应的物理地址。当使用页时,其策略为:当换出的时候,我们只将那些不活跃的,也就是不经常使用的页换出,而保留那些活跃的
页。在换入的时候,只有被请求访问的页才被换入,没有被请求访问的页将永远不会被换入到物理内存。这就是请求页(Demand
Page)算法的核心思想。
这就引出一个页大小的问题:首先我们不可能以字节为单位,这样映射表的大小和线性空间大小相同——
假如整个线性空间都被映射的话——我们不可能将全部线性空间用作存放这个映射表。由此,我们也可以得知,页越小,则映射表的容量越大。而我们不能让映射表
占用太多的空间。但如果页太大,则面临着和不定长段映射同样的问题,每次换出一个页,都需要大量的磁盘操作。另外,由于为一个进程分配内存的最小单位是
页,假如我们的页大小为4 MB,那么即使一个进程只需要使用4
KB的内存,也不得不占用整个4
MB页,这明显是一种很大的浪费。所以我们必须在两者之间进行折衷,一般平台所规定的页大小为1
KB到8 KB,IA-32所规定的页大小为4 KB。(IA-32也支持4
MB页,你可以根据你的OS的用途进行选择,一般都是使用4 KB页)。
--------------------------------------------------------------------------------
4. Page Table
假如使用4 KB的页,那么对于4
GB的线性空间,则需要1,048,576个页表实体,每个表项占用4个字节,则需要4,194,304个字节。仅仅页表就占用4
MB空间,这是一个很大的需求。但如果确确实实一个进程需要使用全部线性空间的话,那么这4
MB的页表空间投入也是必要的。
但在现实中,很少有那个程序需要使用这么大空间,一般的程序往往很小,从几KB到几MB,再使用这么大的页表就纯粹是一种浪费。那我们该怎么办?
一种策略是建立变长页表——我们只建立所需长度的页表。但这种策略带来很大的限制,并且仍然会造成比较大的空间浪费。由于页表机制是使用线性地址作为索
引,到页表中进行检索。那么如果我们想让OS
Kernel使用C0000000h-FFFFFFFFh,也就是3 G
http://www.xrss.cn/Dev/PC/2007111117550.Html

二进制与文本文件的区别

http://topic.csdn.net/t/20050518/17/4017296.html
文本文件也称ASCII文件,在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
ASC码:  00110101 00110110 00110111 00111000
↓     ↓    ↓    ↓
十进制码: 5     6    7    8
共占用4个字节。ASCII码文件可在屏幕上按字符显示,
例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。
由于是按字符显示,因此能读懂文件内容。
二进制文件是按二进制的编码方式来存放文件的。
例如,
数5678的存储形式为:
00010110 00101110只占二个字节。
二进制文件虽然也可在屏幕上显示,但其内容无法读懂。
C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。
因此也把这种文件称作"流式文件"。
------------
数据就是数据,关键看你怎么使用了,如果说区别,就是文本文件载入的时候,会把回车换行2个符号转换成回车符号,而吃掉换行符,存盘的时候把这个吃掉的换行符吐回去...
至于也可以把文本文件当初数据文件来读,纯粹是看应用了。
就好比有人问10进制,16进制的区别,纯粹就是一回事 !!!
都是数据,只不过看问题的角度不同而已,只是你观察角度不同而已。他们都是一样的。
好比'1'
你用文本文件看是'1',你用16进制工具看是0x31
实际上都是0x31,或者是0x0011 0001
-------------
没有区别,从文件系统的角度,所有的文件都是一个顺序的比特集合,区别在于程序如何解读这些数据,一段数据可以看成是文本,图像或者声音
------------------
说到底计算机存储的文件都是以二进制形式存储的,但是区别是,习惯上认为:
1)、文本文件
文本文件是包含用户可读信息的文件。这些文件以ASCII码方式存储,可显示和打印。文本文件的行不能包括空字符(即码中的NULL),行的最大
长度(包括换行符在内)也不能超过(LINE_MAX)所定义的字节数。不过文本文件中并不限制使用除空字符以外的控制字符或其它不可打印字符。
(2)、二进制文件(实际上这种说法不准确,因为文本文件也是用二进制代码存放的,称为非文本文件比较好)
二进制文件是包含计算机可读信息的文件。二进制文件可以是可执行的文件,使系统根据其中的指令完成某项工作。命令和程序都是以可执行的而进制文件方式存储。二进制文件没有行的长度限制,也可包含空字符。
--------------
http://www.net0791.com/article/45480.htm
看个例子:
#include<stdio.h>
int main()
{
FILE *fp;
int i = 12;
int j = 12;
fp = fopen("01.txt","wb");
fprintf(fp,"%d",i);
fputc('\n',fp);
fwrite(&j,sizeof(int),1,fp);
fclose(fp);
return 0;
}
看输出结果。
linux:~/test # od -x 01.txt
0000000 3231 0c0a 0000 0000
即使是用二进制打开,但如果你用fputc,fputs,fprintf这些函数,其实还是和用文本文件打开一样。
只有用到fwrite/fread函数,才会看到一个整形占4个字节。
按二进制写文件指的是直接按照数据在内存中的表现形式写入文件。例如,如果int型数据在内存中用
4 个字节表示,则写这个int数据的时候直接把对应的内存中 4
个字节的内容写入文件。在此过程中数据不需要做任何转换,所以效率较高。

据有字符型和非字符型(数)两种。按文本方式写文件指的是将数据转换为对应的字符型数据之后再写入文件。对于字符型数据,由于其本身就是ASCII码字
符,一般不必转换,直接写入文件。但是,由于不同的系统对于换行符('\n')有不同的处理(转换)方式,在有的系统(如Windows)下也会对
'\ n' 作适当的转换。
对于非字符型数据,都要进行转换处理。例如:int m = 12; 以及 double f =
2.3;,分别按照 "%d"、"%lf" 方式将 m 和 f 写入文件的时候,写入的分别是
'1'、'2' 两个字符以及 '2'、'.'、 '3'
等三个字符的ASCII码值。显然,如果按照二进制方式写的话,在文件中一般 m 要占
4 个字节、f 要占 8 个字节
------------------
http://www.80diy.com/home/20020504/20/698376.html
PS:
binary file中有许多是不可见字符(它本来就不是用来看的),
而text file都是可见字符(这些字符可能属于不同的字符集)Top

你自己慢慢会明白其实人们做的这种区分本身就很模糊的。
比如说word文件,他应该算是是二进制文件,但它里面却又一大块unicode的数据区。
所有文件都能以二进制方式打开,也都能以文本方式打开。
只不过人们通常把仅由可见字符构成的文件称为"文本文件。
二进制与文本文件的区别
http://topic.csdn.net/t/20050518/17/4017296.html
文本文件也称ASCII文件,在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
ASC码:  00110101 00110110 00110111 00111000
↓     ↓    ↓    ↓
十进制码: 5     6    7    8
共占用4个字节。ASCII码文件可在屏幕上按字符显示,
例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。
由于是按字符显示,因此能读懂文件内容。
二进制文件是按二进制的编码方式来存放文件的。
例如,
数5678的存储形式为:
00010110 00101110只占二个字节。
二进制文件虽然也可在屏幕上显示,但其内容无法读懂。
C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。
因此也把这种文件称作"流式文件"。
------------
数据就是数据,关键看你怎么使用了,如果说区别,就是文本文件载入的时候,会把回车换行2个符号转换成回车符号,而吃掉换行符,存盘的时候把这个吃掉的换行符吐回去...
至于也可以把文本文件当初数据文件来读,纯粹是看应用了。
就好比有人问10进制,16进制的区别,纯粹就是一回事 !!!
都是数据,只不过看问题的角度不同而已,只是你观察角度不同而已。他们都是一样的。
好比'1'
你用文本文件看是'1',你用16进制工具看是0x31
实际上都是0x31,或者是0x0011 0001
-------------
没有区别,从文件系统的角度,所有的文件都是一个顺序的比特集合,区别在于程序如何解读这些数据,一段数据可以看成是文本,图像或者声音
------------------
说到底计算机存储的文件都是以二进制形式存储的,但是区别是,习惯上认为:
1)、文本文件
文本文件是包含用户可读信息的文件。这些文件以ASCII码方式存储,可显示和打印。文本文件的行不能包括空字符(即码中的NULL),行的最大
长度(包括换行符在内)也不能超过(LINE_MAX)所定义的字节数。不过文本文件中并不限制使用除空字符以外的控制字符或其它不可打印字符。
(2)、二进制文件(实际上这种说法不准确,因为文本文件也是用二进制代码存放的,称为非文本文件比较好)
二进制文件是包含计算机可读信息的文件。二进制文件可以是可执行的文件,使系统根据其中的指令完成某项工作。命令和程序都是以可执行的而进制文件方式存储。二进制文件没有行的长度限制,也可包含空字符。
--------------
http://www.net0791.com/article/45480.htm
看个例子:
#include<stdio.h>
int main()
{
FILE *fp;
int i = 12;
int j = 12;
fp = fopen("01.txt","wb");
fprintf(fp,"%d",i);
fputc('\n',fp);
fwrite(&j,sizeof(int),1,fp);
fclose(fp);
return 0;
}
看输出结果。
linux:~/test # od -x 01.txt
0000000 3231 0c0a 0000 0000
即使是用二进制打开,但如果你用fputc,fputs,fprintf这些函数,其实还是和用文本文件打开一样。
只有用到fwrite/fread函数,才会看到一个整形占4个字节。
按二进制写文件指的是直接按照数据在内存中的表现形式写入文件。例如,如果int型数据在内存中用
4 个字节表示,则写这个int数据的时候直接把对应的内存中 4
个字节的内容写入文件。在此过程中数据不需要做任何转换,所以效率较高。

据有字符型和非字符型(数)两种。按文本方式写文件指的是将数据转换为对应的字符型数据之后再写入文件。对于字符型数据,由于其本身就是ASCII码字
符,一般不必转换,直接写入文件。但是,由于不同的系统对于换行符('\n')有不同的处理(转换)方式,在有的系统(如Windows)下也会对
'\ n' 作适当的转换。
对于非字符型数据,都要进行转换处理。例如:int m = 12; 以及 double f =
2.3;,分别按照 "%d"、"%lf" 方式将 m 和 f 写入文件的时候,写入的分别是
'1'、'2' 两个字符以及 '2'、'.'、 '3'
等三个字符的ASCII码值。显然,如果按照二进制方式写的话,在文件中一般 m 要占
4 个字节、f 要占 8 个字节
------------------
http://www.80diy.com/home/20020504/20/698376.html
PS:
binary file中有许多是不可见字符(它本来就不是用来看的),
而text file都是可见字符(这些字符可能属于不同的字符集)Top

你自己慢慢会明白其实人们做的这种区分本身就很模糊的。
比如说word文件,他应该算是是二进制文件,但它里面却又一大块unicode的数据区。
所有文件都能以二进制方式打开,也都能以文本方式打开。
只不过人们通常把仅由可见字符构成的文件称为"文本文件。
二进制与文本文件的区别
http://topic.csdn.net/t/20050518/17/4017296.html
文本文件也称ASCII文件,在磁盘中存放时每个字符对应一个字节,用于存放对应的ASCII码。例如,数5678的存储形式为:
ASC码:  00110101 00110110 00110111 00111000
↓     ↓    ↓    ↓
十进制码: 5     6    7    8
共占用4个字节。ASCII码文件可在屏幕上按字符显示,
例如源程序文件就是ASCII文件,用DOS命令TYPE可显示文件的内容。
由于是按字符显示,因此能读懂文件内容。
二进制文件是按二进制的编码方式来存放文件的。
例如,
数5678的存储形式为:
00010110 00101110只占二个字节。
二进制文件虽然也可在屏幕上显示,但其内容无法读懂。
C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。
因此也把这种文件称作"流式文件"。
------------
数据就是数据,关键看你怎么使用了,如果说区别,就是文本文件载入的时候,会把回车换行2个符号转换成回车符号,而吃掉换行符,存盘的时候把这个吃掉的换行符吐回去...
至于也可以把文本文件当初数据文件来读,纯粹是看应用了。
就好比有人问10进制,16进制的区别,纯粹就是一回事 !!!
都是数据,只不过看问题的角度不同而已,只是你观察角度不同而已。他们都是一样的。
好比'1'
你用文本文件看是'1',你用16进制工具看是0x31
实际上都是0x31,或者是0x0011 0001
-------------
没有区别,从文件系统的角度,所有的文件都是一个顺序的比特集合,区别在于程序如何解读这些数据,一段数据可以看成是文本,图像或者声音
------------------
说到底计算机存储的文件都是以二进制形式存储的,但是区别是,习惯上认为:
1)、文本文件
文本文件是包含用户可读信息的文件。这些文件以ASCII码方式存储,可显示和打印。文本文件的行不能包括空字符(即码中的NULL),行的最大
长度(包括换行符在内)也不能超过(LINE_MAX)所定义的字节数。不过文本文件中并不限制使用除空字符以外的控制字符或其它不可打印字符。
(2)、二进制文件(实际上这种说法不准确,因为文本文件也是用二进制代码存放的,称为非文本文件比较好)
二进制文件是包含计算机可读信息的文件。二进制文件可以是可执行的文件,使系统根据其中的指令完成某项工作。命令和程序都是以可执行的而进制文件方式存储。二进制文件没有行的长度限制,也可包含空字符。
--------------
http://www.net0791.com/article/45480.htm
看个例子:
#include<stdio.h>
int main()
{
FILE *fp;
int i = 12;
int j = 12;
fp = fopen("01.txt","wb");
fprintf(fp,"%d",i);
fputc('\n',fp);
fwrite(&j,sizeof(int),1,fp);
fclose(fp);
return 0;
}
看输出结果。
linux:~/test # od -x 01.txt
0000000 3231 0c0a 0000 0000
即使是用二进制打开,但如果你用fputc,fputs,fprintf这些函数,其实还是和用文本文件打开一样。
只有用到fwrite/fread函数,才会看到一个整形占4个字节。
按二进制写文件指的是直接按照数据在内存中的表现形式写入文件。例如,如果int型数据在内存中用
4 个字节表示,则写这个int数据的时候直接把对应的内存中 4
个字节的内容写入文件。在此过程中数据不需要做任何转换,所以效率较高。

据有字符型和非字符型(数)两种。按文本方式写文件指的是将数据转换为对应的字符型数据之后再写入文件。对于字符型数据,由于其本身就是ASCII码字
符,一般不必转换,直接写入文件。但是,由于不同的系统对于换行符('\n')有不同的处理(转换)方式,在有的系统(如Windows)下也会对
'\ n' 作适当的转换。
对于非字符型数据,都要进行转换处理。例如:int m = 12; 以及 double f =
2.3;,分别按照 "%d"、"%lf" 方式将 m 和 f 写入文件的时候,写入的分别是
'1'、'2' 两个字符以及 '2'、'.'、 '3'
等三个字符的ASCII码值。显然,如果按照二进制方式写的话,在文件中一般 m 要占
4 个字节、f 要占 8 个字节
------------------
http://www.80diy.com/home/20020504/20/698376.html
PS:
binary file中有许多是不可见字符(它本来就不是用来看的),
而text file都是可见字符(这些字符可能属于不同的字符集)Top

你自己慢慢会明白其实人们做的这种区分本身就很模糊的。
比如说word文件,他应该算是是二进制文件,但它里面却又一大块unicode的数据区。
所有文件都能以二进制方式打开,也都能以文本方式打开。
只不过人们通常把仅由可见字符构成的文件称为"文本文件。

10个方法提高你的编程生产力

1. 一天最多阅读两次新闻
信息爆炸的年代新闻数量多不胜数,不要阅读过多的新闻,我一般一天阅读两次新闻,早晨和下午各打开一次google
reader,更多的新闻阅读将会严重降低工作效率。
2。给自己精心准备一个工作开始的起点
写程序一旦进入状态,毫无疑问效率是非常高的,脑子里面每个细胞似乎都在奋斗。但是如何快速进入完美的工作状态?我的经验是,每当我离开工作的时候,比如
中午午餐时间前或者一天工作结束时候,我会故意遗留一个未完成小任务在我的程序里面。当我回来工作的时候,我能够迅速知道从哪里开始,专心致志解决完这个
小任务,我的大脑差不多已经完成热身了,马上能够进入真正的工作。
3。用笔画出来,做好预先研究工作
怎么说呢,就是脑子的想法尽可能的用笔画出来,形象化的图形能够很好的帮助你思考总结。
对于复杂的工作,预先做好研究工作,比如一个难度很大的算法程序,我会先搞懂最难的技术问题才开始写代码。
4。建立一个完美的工作环境
大多数我们都是在公司工作,工作环境不能由我们决定,但是至少我们可以在自己家里弄一个完美的工作环境。我心目中好的工作环境包括:
a)一个大电脑显示器,一张大桌子
b)一个舒服的电脑椅
c)有益工作的背景音乐,这个因人而异
d)一套好的音响
e)阳光充足的窗口
f)大的开放的空间
g)安静,很少有人在旁边走动
h)和外界通风良好
i)房间是现代装饰风格
5。工作时间关掉IM工具
不管什么理由,都要坚决关掉所有的IM工具
6。工作时间只回复和处理紧急邮件
不要让邮件打断你的工作节奏,工作时间只回复和处理紧急邮件
7。减少开会,一周一次会议或者更少
保持沟通效率高效,但不是更多的会议。减少开会,一周一次会议或者更少。
8。每两周参加一次社交活动
程序员的生活是比较单调的,我所说的社交活动并不是和同事,工作伙伴之间的交流,也不是你依然坐在电脑桌前玩游戏。而是走出你的办公室,和你工作以外的朋友在一起交流,量身打造你自己的情感需求。
9。放松的夜晚
没有比7×24小时连续工作更糟了。长时间处于兴奋和焦虑状态会极大影响你长期的工作效率。每天休息一段时间,散步,阅读,享受生活,你会发现你的创造力提高了。
10。每周3次,每次20分钟的体育运动
体育运动并不是浪费时间,定期的体育活动会让你精力更加充沛,头脑反映更加灵活。保持至少每周3次,每次20分钟的体育运动。

GCC使用入门

一、GCC简介
通 常所说的GCC是GUN Compiler
Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进制代
码。GCC是Linux平台下最常用的编译程序,它是Linux平台编译器的事实标准。同时,在Linux平台下的嵌入式开发领域,GCC也是用得最普遍
的一种编译器。GCC之所以被广泛采用,是因为它能支持各种不同的目标体系结构。例如,它既支持基于宿主的开发(简单讲就是要为某平台编译程序,就在该平
台上编译),也支持交叉编译(即在A平台上编译的程序是供平台B使用的)。目前,GCC支持的体系结构有四十余种,常见的有X86系列、Arm、
PowerPC等。同时,GCC还能运行在不同的操作系统上,如Linux、Solaris、Windows等。
除了上面讲的之外,GCC除了支持C语言外,还支持多种其他语言,例如C++、Ada、Java、Objective-C、FORTRAN、Pascal等。
本系列文章中,我们不仅介绍GCC的基本功能,还涉及到一些诸如优化之类的高级功能。另外,我们还考察GCC的一些映像操作工具,如size和objcopy等,这将在后续的文章中加以介绍。

二、程序的编译过程
对于GUN编译器来说,程序的编译要经历预处理、编译、汇编、连接四个阶段。从功能上分,预处理、编译、汇编是三个不同的阶段,但GCC的实际操作上,它可以把这三个步骤合并为一个步骤来执行。下面我们以C语言为例来谈一下不同阶段的输入和输出情况。

预处理阶段,输入的是C语言的源文件,通常为*.c。它们通常带有.h之类头文件的包含文件。这个阶段主要处理源文件中的#ifdef、
#include和#define命令。该阶段会生成一个中间文件*.i,但实际工作中通常不用专门生成这种文件,因为基本上用不到;若非要生成这种文件
不可,可以利用下面的示例命令:
gcc -E test.c -o test.i
在编译阶段,输入的是中间文件*.i,编译后生成汇编语言文件*.s
。这个阶段对应的GCC命令如下所示:
GCC -S test.i -o test.s
在汇编阶段,将输入的汇编文件*.s转换成机器语言*.o。这个阶段对应的GCC命令如下所示:
GCC -c test.s -o test.o
最后,在连接阶段将输入的机器代码文件*.s(与其它的机器代码文件和库文件)汇集成一个可执行的二进制代码文件。这一步骤,可以利用下面的示例命令完成:
GCC test.o -o test
上面介绍了GCC编译过程的四个阶段以及相应的命令。下面我们进一步介绍常用的GCC的模式。
三、GCC常用模式
这里介绍GCC追常用的两种模式:编译模式和编译连接模式。下面以一个例子来说明各种模式的使用方法。为简单起见,假设我们全部的源代码都在一个文件test.c中,要想把这个源文件直接编译成可执行程序,可以使用以下命令:
$ GCC -o test
这里test.c是源文件,生成的可执行代码存放在一个名为test
的文件中(该文件是机器代码并且可执行)。-o
是生成可执行文件的输出选项。如果我们只想让源文件生成目标文件(给文件虽然也是机器代码但不可执行),可以使用标记-c
,详细命令如下所示:
$ GCC -c test.c
默认情况下,生成的目标文件被命名为test.o,但我们也可以为输出文件指定名称,如下所示:
$ GCC -c test.c -o
上面这条命令将编译后的目标文件命名为mytest.o,而不是默认的test.o。
迄今为止,我们谈论的程序仅涉及到一个源文件;现实中,一个程序的源代码通常包含在多个源文件之中,这该怎么办?没关系,即使这样,用GCC处理起来也并不复杂,见下例:
$ GCC -o test first.c second.c third.c

要注意的是,要生成可执行程序时,一个程序无论有有一个源文件还是多个源文件,所有被编译和连接的源文件中必须有且仅有一个main函数,因为main函
数是该程序的入口点(换句话说,当系统调用该程序时,首先将控制权授予程序的main函数)。但如果仅仅是把源文件编译成目标文件的时候,因为不会进行连
接,所以main函数不是必需的。
四、常用选项
许多情况下,头文件和源文件会单独存放在不同的目录中。例如,假设存放源文件的子目录名为./src,而包含文件则放在层次的其他目录下,如./inc。当我们在./src
目录下进行编译工作时,如何告诉GCC到哪里找头文件呢?方法如下所示:
$ gcc test.c –I../inc -o test
上面的命令告诉GCC包含文件存放在./inc
目录下,在当前目录的上一级。如果在编译时需要的包含文件存放在多个目录下,可以使用多个-I
来指定各个目录:
$ gcc test.c –I../inc –I../../inc2 -o test
这里指出了另一个包含子目录inc2,较之前目录它还要在再上两级才能找到。
另外,我们还可以在编译命令行中定义符号常量。为此,我们可以简单的在命令行中使用-D选项即可,如下例所示:
$ gcc -DTEST_CONFIGURATION test.c -o test
上面的命令与在源文件中加入下列命令是等效的:
#define TEST_CONFIGURATION
在编译命令行中定义符号常量的好处是,不必修改源文件就能改变由符号常量控制的行为。

五、警告功能

GCC在编译过程中检查出错误的话,它就会中止编译;但检测到警告时却能继续编译生成可执行程序,因为警告只是针对程序结构的诊断信息,它不能说明程序一
定有错误,而是存在风险,或者可能存在错误。虽然GCC提供了非常丰富的警告,但前提是你已经启用了它们,否则它不会报告这些检测到的警告。
在众多的警告选项之中,最常用的就是-Wall选项。该选项能发现程序中一系列的常见错误警告,该选项用法举例如下:
$ gcc -Wall test.c -o test
该选项相当于同时使用了下列所有的选项:
◆unused-function:遇到仅声明过但尚未定义的静态函数时发出警告。
◆unused-label:遇到声明过但不使用的标号的警告。
◆unused-parameter:从未用过的函数参数的警告。
◆unused-variable:在本地声明但从未用过的变量的警告。
◆unused-value:仅计算但从未用过的值得警告。
◆Format:检查对printf和scanf等函数的调用,确认各个参数类型和格式串中的一致。
◆implicit-int:警告没有规定类型的声明。
◆implicit-function-:在函数在未经声明就使用时给予警告。
◆char-subscripts:警告把char类型作为数组下标。这是常见错误,程序员经常忘记在某些机器上char有符号。
◆missing-braces:聚合初始化两边缺少大括号。
◆Parentheses:在某些情况下如果忽略了括号,编译器就发出警告。
◆return-type:如果函数定义了返回类型,而默认类型是int型,编译器就发出警告。同时警告那些不带返回值的
return语句,如果他们所属的函数并非void类型。
◆sequence-point:出现可疑的代码元素时,发出报警。
◆Switch:如果某条switch语句的参数属于枚举类型,但是没有对应的case语句使用枚举元素,编译器就发出警告(在switch语句中使用default分支能够防止这个警告)。超出枚举范围的case语句同样会导致这个警告。
◆strict-aliasing:对变量别名进行最严格的检查。
◆unknown-pragmas:使用了不允许的#pragma。
◆Uninitialized:在初始化之前就使用自动变量。
需要注意的是,各警告选项既然能使之生效,当然也能使之关闭。比如假设我们想要使用-Wall来启用个选项,同时又要关闭unused警告,利益通过下面的命令来达到目的:
$ gcc -Wall -Wno-unused test.c -o test
下面是使用-Wall选项的时候没有生效的一些警告项:
◆cast-align:一旦某个指针类型强制转换时,会导致目标所需的地址对齐边界扩展,编译器就发出警告。例如,某些机器上只能在2或4字节边界上访问整数,如果在这种机型上把char
*强制转换成int *类型, 编译器就发出警告。
◆sign-compare:将有符号类型和无符号类型数据进行比较时发出警告。
◆missing-prototypes
:如果没有预先声明函数原形就定义了全局函数,编译器就发出警告。即使函数定义自身提供了函数原形也会产生这个警告。这样做的目的是检查没有在头文件中声明的全局函数。
◆Packed:当结构体带有packed属性但实际并没有出现紧缩式给出警告。
◆Padded:如果结构体通过充填进行对齐则给出警告。
◆unreachable-code:如果发现从未执行的代码时给出警告。
◆Inline:如果某函数不能内嵌(inline),无论是声明为inline或者是指定了-finline-functions
选项,编译器都将发出警告。
◆disabled-optimization:当需要太长时间或过多资源而导致不能完成某项优化时给出警告。
上面是使用-Wall选项时没有生效,但又比较常用的一些警告选项。本文中要介绍的最后一个常用警告选项是-Werror。使用该选项后,GCC发现可疑之处时不会简单的发出警告就算完事,而是将警告作为一个错误而中断编译过程。该选项在希望得到高质量代码时非常有用。

六、小结
本文介绍了GCC的基本编译过程和编译模式,并详细阐述了GCC的一些常用选项以及警告功能。这些是在利用GCC进行应用编程时最基本也最常用的一些内容,我们会在后续文章中继续介绍GCC的调试和优化技术。
该命令将同时编译三个源文件,即first.c、second.c和
third.c,然后将它们连接成一个可执行程序,名为test。
Linux联盟收集整理 ,转贴请标明原始链接,如有任何疑问欢迎来本站Linux论坛讨论
http://www.xxlinux.com/linux/article/development/soft/20061029/5551.html

单字节大小枚举及按位对齐

在linux下定义一个枚举,我们用gcc编译时,得到的大小默认为4个字节,但如何让这个枚举大小为1个字节呢?下面我们看看一个例子test.c:
#include <stdio.h>
enum he {
AGE = 0x01,
NAME = 0x02
};
enum she {
AGE1 = 0x03,
NAME2 = 0x04
};
struct bear {
char x;
char y;
enum he he;
enum she she;
int z;
};
int main(int argc, char **argv)
{
int size = 0;
struct bear bear;
size = sizeof(struct bear);
printf("size is:%d\n", size);
return 0;
}
用gcc
test.c时,得出来的结构体的大小为16个字节,两个char型为2个字节,由于第二个char型后面为4个字节的enum型,所以按默认的4个字节对齐,前两个char型为4个字节。再加上两个enum,一个int型,所以总共16个字节。
然而在"#include <stdio.h>"下行加上#pragma pack(1)
编译运行后得到得结构体的大小为14个字节。因为这是按1个字节对齐,两个char型为两个字节,加两个enum,一个int共14个字节。
上面说的是按位对齐,而要把enum变为单个字节,则只需在上面的代码编译时,再加上-fshort-enums参数就行了:
gcc test.c -fshort-enums
运行后得到此结构体的大小只有8个字节了。上面的两个enum型就成就单字节的枚举了。
下面对-fshort-enums在gcc手册中的解释:
-fshort-enums
Allocate to an "enum" type only as many bytes as it needs for the
declared range of possible values. Specifically, the "enum" type will be
equivalent to the smallest integer type which has enough room.
Warning: the -fshort-enums switch causes GCC to generate code that is
not binary compatible with
code generated without that switch. Use it to conform to a non-default
application binary interface.
http://linux0818.bokee.com/viewdiary.179592916.html

[转贴]一个老程序员的心里话说到人的心坎

诸位,咱当电子工程师也是十余年了,不算有出息,环顾四周,也没有看见几个有出息的!回顾工程师生涯,感慨万千,愿意讲几句掏心窝子的话,也算给咱们师弟师妹们提个醒,希望他们比咱们强!
[1]
好好规划自己的路,不要跟着感觉走!根据个人的理想决策安排,绝大部分人并不指望成为什么院士或教授,而是希望活得滋润一些,爽一些。那么,就需要慎重安
排自己的轨迹。从哪个行业入手,逐渐对该行业深入了解,不要频繁跳槽,特别是不要为了一点工资而转移阵地,从长远看,这点钱根本不算什么,当你对一个行业
有那么几年的体会,以后钱根本不是问题。频繁地动荡不是上策,最后你对哪个行业都没有摸透,永远是新手! 
  
[2]可以做技术,切不可沉湎于技术。千万不可一门心思钻研技术!给自己很大压力,如果你的心思全部放在这上面,那么注定你将成为孔乙己一类的人物!适可而止为之,因为技术只不过是你今后前途的支柱之一,而且还不是最大的支柱,除非你只愿意到老还是个工程师!   
[3]
不要去做技术高手,只去做综合素质高手!在企业里混,我们时常瞧不起某人,说他"什么都不懂,凭啥拿那么多钱,凭啥升官!"这是普遍的典型的工程师的迂腐
之言。8051很牛吗?人家能上去必然有他的本事,而且是你没有的本事。你想想,老板搞经营那么多年,难道见识不如你这个新兵?人家或许善于管理,善于领
会老板意图,善于部门协调等等。因此务必培养自己多方面的能力,包括管理,亲和力,察言观色能力,攻关能力等,要成为综合素质的高手,则前途无量,否则只
能躲在角落看示波器!技术以外的技能才是更重要的本事!!从古到今,美国***,一律如此!   
[4]多交社会三教九流的朋友!不要
只和工程师交往,认为有共同语言,其实更重要的是和其他类人物交往,如果你希望有朝一日当老板或高层管理,那么你整日面对的就是这些人。了解他们的经历,
思维习惯,爱好,学习他们处理问题的模式,了解社会各个角落的现象和问题,这是以后发展的巨大的本钱,没有这些以后就会笨手笨脚,跌跌撞撞,遇到重重困
难,交不少学费,成功的概率大大降低!
[5]知识涉猎不一定专,但一定要广!多看看其他方面的书,金融,财会,进出口,税务,法律等等,为以后做一些积累,以后的用处会更大!会少交许多学费!! 
[6]
抓住时机向技术管理或市场销售方面的转变!要想有前途就不能一直搞开发,适当时候要转变为管理或销售,前途会更大,以前搞技术也没有白搞,以后还用得着。
搞管理可以培养自己的领导能力,搞销售可以培养自己的市场概念和思维,同时为自己以后发展积累庞大的人脉!应该说这才是前途的真正支柱!!!   
[7]
逐渐克服自己的心里弱点和性格缺陷!多疑,敏感,天真(贬义,并不可爱),犹豫不决,胆怯,多虑,脸皮太薄,心不够黑,教条式思维。。。这些工程师普遍存
在的性格弱点必须改变!很难吗?只在床上想一想当然不可能,去帮朋友守一个月地摊,包准有效果,去实践,而不要只想!不克服这些缺点,一切不可能,甚至连
项目经理都当不好--尽管你可能技术不错!   
[8]工作的同时要为以后做准备!建立自己的工作环境!及早为自己配置一个工作环境,
装备电脑,示波器(可以买个二手的),仿真器,编程器等,业余可以接点活,一方面接触市场,培养市场感觉,同时也积累资金,更重要的是准备自己的产品,咱
搞技术的没有钱,只有技术,技术的代表不是学历和证书,而是产品,拿出象样的产品,就可技术转让或与人合作搞企业!先把东西准备好,等待机会,否则,有了
机会也抓不住!   
[9]要学会善于推销自己!不仅要能干,还要能说,能写,善于利用一切机会推销自己,树立自己的品牌形象,很必
要!要创造条件让别人了解自己,不然老板怎么知道你能干?外面的投资人怎么相信你?提早把自己推销出去,机会自然会来找你!搞个个人主页是个好注意!!特
别是培养自己在行业的名气,有了名气,高薪机会自不在话下,更重要的是有合作的机会...   
[10]该出手时便出手!永远不可能有100%把握!!!条件差不多就要大胆去干,去闯出自己的事业,不要犹豫,不要彷徨,干了不一定成功,但至少为下一次冲击积累了经验,不干永远没出息,而且要干成必然要经历失败。不经历风雨,怎么见彩虹,没有人能随随便便成功!

Linux下的段错误的原因及调试

http://www.yuanma.org/data/2008/0818/article_3139.htm
简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址.
一般来说,
段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gdtr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的
gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向的gdt是由以64位为一个单位的表,
在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。一旦一个程序发生了越界
访问,cpu就会产生相应的异常保护,于是segmentation fault就出现了.
在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的
1)访问系统数据区,尤其是往 系统保护的内存地址写数据
最常见就是给一个指针以0地址
2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域
解决方法

们在用C/C++语言写程序的时侯,内存管理的绝大部分工作都是需要我们来做的。实际上,内存管理是一个比较繁琐的工作,无论你多高明,经验多丰富,难免
会在此处犯些小错误,而通常这些错误又是那么的浅显而易于消除。但是手工"除虫"(debug),往往是效率低下且让人厌烦的,本文将就"段错误"这个内
存访问越界的错误谈谈如何快速定位这些"段错误"的语句。
下面将就以下的一个存在段错误的程序介绍几种调试方法: 1 dummy_function
(void)
2 {
3 unsigned char *ptr = 0x00;
4 *ptr = 0x00;
5 }
6
7 int main (void)
8 {
9 dummy_function ();
10
11 return 0;
12 }
作为一个熟练的C/C++程序员,以上代码的bug应该是很清楚的,因为它尝试操作地址为0的内存区域,而这个内存区域通常是不可访问的禁区,当然就会出错了。我们尝试编译运行它:xiaosuo@gentux
test $ ./a.out
段错误
果然不出所料,它出错并退出了。
1.利用gdb逐步查找段错误:
这种方法也是被大众所熟知并广泛采用的方法,首先我们需要一个带有调试信息的可执行程序,所以我们加上"-g
-rdynamic"的参数进行编译,然后用gdb调试运行这个新编译的程序,具体步骤如下:xiaosuo@gentux
test $ gcc -g -rdynamic d.c
xiaosuo@gentux test $ gdb ./a.out
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db
library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /home/xiaosuo/test/a.out
Program received signal SIGSEGV, Segmentation fault.
0x08048524 in dummy_function () at d.c:4
4 *ptr = 0x00;
(gdb)
哦?!好像不用一步步调试我们就找到了出错位置d.c文件的第4行,其实就是如此的简单。
从这里我们还发现进程是由于收到了SIGSEGV信号而结束的。通过进一步的查阅文档(man
7
signal),我们知道SIGSEGV默认handler的动作是打印"段错误"的出错信息,并产生Core文件,由此我们又产生了方法二。
2.分析Core文件:
Core文件是什么呢?The default action of certain signals is to cause a
process to terminate and produce a core dump file, a disk file containing
an image of the process's memory at the time of termination. A list of the
signals which cause a process to dump core can be found in signal(7).
以 上资料摘自man page(man 5
core)。不过奇怪了,我的系统上并没有找到core文件。后来,忆起为了渐少系统上的拉圾文件的数量(本人有些洁癖,这也是我喜欢Gentoo的原因
之一),禁止了core文件的生成,查看了以下果真如此,将系统的core文件的大小限制在512K大小,再试:xiaosuo@gentux
test $ ulimit -c
0
xiaosuo@gentux test $ ulimit -c 1000
xiaosuo@gentux test $ ulimit -c
1000
xiaosuo@gentux test $ ./a.out
段错误 (core dumped)
xiaosuo@gentux test $ ls
a.out core d.c f.c g.c pango.c test_iconv.c test_regex.c
core文件终于产生了,用gdb调试一下看看吧:xiaosuo@gentux test $ gdb ./a.out
core
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db
library "/lib/libthread_db.so.1".
warning: Can't read pathname for load map: 输入/输出错误.
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0 0x08048524 in dummy_function () at d.c:4
4 *ptr = 0x00;
哇,好历害,还是一步就定位到了错误所在地,佩服一下Linux/Unix系统的此类设计。
接着考虑下去,以前用windows系统下的ie的时侯,有时打开某些网页,会出现"运行时错误",这个时侯如果恰好你的机器上又装有windows的编译器的话,他会弹出来一个对话框,问你是否进行调试,如果你选择是,编译器将被打开,并进入调试状态,开始调试。
Linux下如何做到这些呢?我的大脑飞速地旋转着,有了,让它在SIGSEGV的handler中调用gdb,于是第三个方法又诞生了:
3.段错误时启动调试:#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
void dump(int signo)
{
char buf[1024];
char cmd[1024];
FILE *fh;
snprintf(buf, sizeof(buf), "/proc/%d/cmdline", getpid());
if(!(fh = fopen(buf, "r")))
exit(0);
if(!fgets(buf, sizeof(buf), fh))
exit(0);
fclose(fh);
if(buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
snprintf(cmd, sizeof(cmd), "gdb %s %d", buf, getpid());
system(cmd);
exit(0);
}
void
dummy_function (void)
{
unsigned char *ptr = 0x00;
*ptr = 0x00;
}
int
main (void)
{
signal(SIGSEGV, &dump);
dummy_function ();
return 0;
}
编译运行效果如下:xiaosuo@gentux test $ gcc -g -rdynamic f.c
xiaosuo@gentux test $ ./a.out
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...Using host libthread_db
library "/lib/libthread_db.so.1".
Attaching to program: /home/xiaosuo/test/a.out, process 9563
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xffffe410 in __kernel_vsyscall ()
(gdb) bt
#0 0xffffe410 in __kernel_vsyscall ()
#1 0xb7ee4b53 in waitpid () from /lib/libc.so.6
#2 0xb7e925c9 in strtold_l () from /lib/libc.so.6
#3 0x08048830 in dump (signo=11) at f.c:22
#4 <signal handler called>
#5 0x0804884c in dummy_function () at f.c:31
#6 0x08048886 in main () at f.c:38
怎么样?是不是依旧很酷?
以上方法都是在系统上有gdb的前提下进行的,如果没有呢?其实glibc为我们提供了此类能够dump栈内容的函数簇,详见/usr/include/execinfo.h(这些函数都没有提供man
page,难怪我们找不到),另外你也可以通过gnu的手册进行学习。
4.利用backtrace和objdump进行分析:
重写的代码如下:#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
/* A dummy function to make the backtrace more interesting. */
void
dummy_function (void)
{
unsigned char *ptr = 0x00;
*ptr = 0x00;
}
void dump(int signo)
{
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 10);
strings = backtrace_symbols (array, size);
printf ("Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
printf ("%s\n", strings[i]);
free (strings);
exit(0);
}
int
main (void)
{
signal(SIGSEGV, &dump);
dummy_function ();
return 0;
}
编译运行结果如下:xiaosuo@gentux test $ gcc -g -rdynamic g.c
xiaosuo@gentux test $ ./a.out
Obtained 5 stack frames.
./a.out(dump+0x19) [0x80486c2]
[0xffffe420]
./a.out(main+0x35) [0x804876f]
/lib/libc.so.6(__libc_start_main+0xe6) [0xb7e02866]
./a.out [0x8048601]
这次你可能有些失望,似乎没能给出足够的信息来标示错误,不急,先看看能分析出来什么吧,用objdump反汇编程序,找到地址0x804876f对应的代码位置:xiaosuo@gentux
test $ objdump -d a.out
8048765: e8 02 fe ff ff call 804856c <signal@plt>
804876a: e8 25 ff ff ff call 8048694 <dummy_function>
804876f: b8 00 00 00 00 mov $0x0,%eax
8048774: c9 leave
我们还是找到了在哪个函数(dummy_function)中出错的,信息已然不是很完整,不过有总比没有好的啊!
后记:
本文给出了分析"段错误"的几种方法,不要认为这是与孔乙己先生的"回"字四种写法一样的哦,因为每种方法都有其自身的适用范围和适用环境,请酌情使用,或遵医嘱。

EMCV:可在DSP上运行的OpenCV

EMCV项目主页: http://sf.net/projects/emcv
EMCV全称为Embedded Computer Vision Library,是一个可在TI
DM64x系列DSP上运行的计算机视觉库。EMCV提供了跟OpenCV完全一致的函数接口,通过EMCV,你可以轻松的将你的OpenCV算法移植到DSP,甚至不用改一行代码。
目前EMCV已经支持IplImage, CvMat,CvSeq等基本数据结构,
可使用cvCreateImage等创建和释放图像,以及contour检测等。
EMCV刚刚启动,希望得到您的代码贡献:
目前您可以通过subversion客户端获取源代码,源代码位于
https://emcv.svn.sourceforge.net/svnroot/emcv

在C6000系列DSP中使用EMCV,
请参考:http://www.opencv.org.cn/forum/viewtopic.php?f=9&t=4638

如何测试程序在DSP上的运行时间?

如何测试程序在DSP上的运行时间?
1、用CLOCK()准确吗?
2、用CCS上的工具,但如何使用?

在你要测试时间的程序的两端,用Project Toolbar上的Toggle profile
point按键,加上profile
print,像断点一样,不过是绿色的。然后在profiler菜单中选择Enable Clock和view
clock,在view clock窗口中可以看到程序运行的时间/周期(时钟)数。在view
clock窗口中第2行显示的时间就是程序从第1行所在位置到第2行位置的时间,也就是你要测定的时间。这是ti推荐的方法,但我发现数据不准确,比实际的要大。如果用profiler得到的数据比你的要求好要少的话,那就说明已经达到了要求,如果比你的大则比一定达不到要求。

周期测定可以利用DSP的定时器来实现!在你要测定开始的地方设定定时器并启动,在你结束的地方关闭定时器即可.不过要关闭中断哟?

用clock函数是可行的,尤其是优化以后的代码很难确定PROFILE的位置。你看一下.asm的输出就知道了,clock没有这个问题。有一点要注意,clock函数如果直接在DSP上跑时间是不准的,必须用load6x命令在DOS或命令行状态下加载,这样结果就准的多。当然,调用函数本身的开销也是要考虑的。

数汇编代码测程序运行时间的方法是不行的:)因为指令所耗费周期数是不一样的,并不是都是一个周期啊,你数指令条数判断运行时间不准确啊,而且小程序你还可以用数的方法,大的怎么办呢:)看看指令耗费周期数的大致规律啊
指令所耗费周期数规律:主要跟操作多少及有关系,简单的加减乘和逻辑运算一般是单周期,延迟跳转指令也是1周期,复杂的并行运算则因操作数据所在存储区(片内、外)而分成一周期和两周期,改变程序计数器的指令一般如跳转,返回等耗费4周期,软件陷阱则因操作多而需要5个周期。

什么是字节对齐,为什么要对齐?

现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐.其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。显然在读取效率上下降很多。

二.字节对齐对程序的影响:
先让我们看几个例子吧(32bit,x86环境,gcc编译器):
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};

4Byte
|---------|
| a |
|----|----|
| b -| c -|
|---------|


struct B
{
char b;
int a;
short c;
};

|--------|
|b |
|--------|
| a |
|--------|
| c |
|--------|

现在已知32位机器上各种数据类型的长度如下:
char:1 (有符号无符号同)
short:2 (有符号无符号同)
int:4 (有符号无符号同)
long:4 (有符号无符号同)
float:4 double:8
那么上面两个结构大小如何呢?
结果是:
sizeof(strcut A)值为8
sizeof(struct B)的值却是12

结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个,B也一样;按理说A,B大小应该都是7字节。之所以出现上面的结果是因为编译器要对数据成员在空间上进行对齐。上面是按照编译器的默认设置进行对齐的结果,那么我们是不是可以改变编译器的这种默认对齐设置呢,当然可以.例如:

#pragma pack (2) /*指定按2字节对齐*/
struct B
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct B)值是8。


修改对齐值为1:
#pragma pack (1) /*指定按1字节对齐*/
struct B
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct B)值为7。

后面我们再讲解#pragma pack()的作用.

三.编译器是按照什么样的原则进行对齐的?

先让我们看四个重要的基本概念:
1.数据类型自身的对齐值:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
2.结构体的自身对齐值:其成员中自身对齐值最大的那个值。
3.指定对齐值:#pragma pack (value)时的指定对齐值value。
4.数据成员和结构体的有效对齐值:数据成员(数据类型)和数据结构的自身对齐值和指定对齐值中小的那个值。(数据成员对齐了数据结构自然也就对齐了)

有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示"对齐在N上",也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解)。这样就不难理解上面的几个例子的值了。

例子分析:
分析例子B;
struct B
{
char b;
int a;
short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为
2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,
0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B
共有12个字节,sizeof(struct
B)=12;其实如果就这一个就来说它已将满足字节对齐了,
因为它的起始地址是0,因此肯定是对齐的,之所以在后面补充2个字节,是因为编译器为了实现结构数组的存取效率,试想如果我们定义了一个结构B的数组,那么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都是紧挨着的,如果我们不把结构的大小补充为4的整数倍,那么下一个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐了,因此我们要把结构补充成有效对齐大小的整数倍.其实诸如:对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,这些已有类型的自身对齐值也是基于数组考虑的,只是因为这些类型的长度已知了,所以他们的自身对齐值也就已知了.
同理,分析上面例子C:
#pragma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定对齐,恢复缺省对齐*/
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=
0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
在0x0006、0x0007中,符合
0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C
只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.


四.如何修改编译器的默认对齐值?
1.在VC IDE中,可以这样修改:[Project]|[Settings],c/c++选项卡Category的Code
Generation选项的Struct Member Alignment中修改,默认是8字节。
2.在编码时,可以这样动态修改:#pragma pack .注意:是pragma而不是progma.

---------------------------------------------------------
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。

---------------------------------------------------------
· __attribute((aligned
(n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
· __attribute__
((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。


五.针对字节对齐,我们在编程中如何考虑?
如果在编程的时候要考虑节约空间的话,那么我们只需要假定结构的首地址是0,然后各个变量按照上面的原则进行排列即可,基本的原则就是把结构中的变量按照类型大小从小到大声明,尽量减少中间的填补空间.还有一种就是为了以空间换取时间的效率,我们显示的进行填补空间进行对齐,比如:有一种使用空间换时间做法是显式的插入reserved成员:
struct A{
char a;
char reserved[3];//使用空间换时间
int b;
}

reserved成员对我们的程序没有什么意义,它只是起到填补空间以达到字节对齐的目的,当然即使不加这个成员通常编译器也会给我们自动填补对齐,我们自己加上它只是起到显式的提醒作用.


六.字节对齐可能带来的隐患:
代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;

p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最后两句代码,从奇数边界去访问unsigned
short型变量,显然不符合对齐的规定。在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.

七.如何查找与字节对齐方面的问题:
如果出现对齐或者赋值问题首先查看
1. 编译器的big little端设置
2. 看这种体系本身是否支持非对齐访问
3.
如果支持看设置了对齐与否,如果没有则看访问时需要加某些特殊的修饰来标志其特殊访问操作。

DSP代码移植

基于DSP系统开发的视频编解码系统,国内几乎都是走的移植,优化的路线,并且移植的代码,都是开源的。毕竟花费大量的人力,物力去开发一套自己的代码,并不见得比一些成熟的开源代码效率更高,健壮性更好。更何况开发速度对于一个产品的发展而言,更是重要。
目前对于H.264而言,移植的代码主要有JM,x264和T264。移植的时候,就需要对各个代码进行测试,以确定要移植的代码。相对而言,JM
的移植更容易,但效率比较差,如果基于科学研究,移植JM的比较多,多见于各高校的研究人员。对企业而言,考虑到实时性的要求,移植以X264和T264
居多。
将视频编解码移植到DSP的时候,考虑到DSP系统资源的宝贵,主要考虑的因素是系统空间,包括程序空间和数据空间,所以需要对原始的C代码,进行评估,这就需要对于所移植的代码有一个比较详细的了解。代码空间一般可以通过map文件进行估算。数据空间的估计,需要计算程序中内存的使用情况,除了
malloc申请的空间,还包括静态数组,主要是H.264标准中的各种表格数组以及一些全局变量等等。
准备好了这些,就可以开始移植了,移植,也是一个考验你的过程。
做好了移植的准备工作,就进入了开发过程的第一个重要阶段---移植。
移植开发的时候,最好准备两个版本,一个纯C代码,在VC下编译,运行,另一个是VDSP下的版本(ccs同理),VC版本主要是验证代码运行是否正确,
VDSP版本就是移植以后的版本,两个版本同步更新,即尽量保持两个版本的一致性,但能够同时在VC和VDSP下运行。在移植过程中,一般会遇到的问题如下:
1.头文件的不同,一般问题都是linux下的头文件,在VDSP中没有存在。最典型的就是inttypes.h

stdint.h,这种头的作用主要是定义了8字符,16字符,32字符,64字符的数据类型,移植的时候,可以自己建一个头文件或者直接在其他的头文件中把这些数据类型的定义加进去,这样的话,就不会出现问题。其他的类似
,要么找相应的头文件替换,要么干脆自己定义。
2.Int64_t和Uint64_t 的问题, 在第一步中,其实也存在这个问题,
不过我最初是用long和Unsigned long
来代替,不过这样的话,编译是可以通过,但仔细分析,其实是有问题的。一般来讲,64位数的用途有两个,第一种是这个数字可能比较大,当累积到一定的程度,可能超过32位,这种情况下,可以用32位代替,不过最好加上注释,告诉自己这个数可能越界,在后面调试的时候,要提示自己注意一下。另一种用途,是开发者为了速度的要求,对一些变量复制的时候,使用了强制性的指针赋值,这种情况下,就不能直接该成32位数据了,那样的话,虽然编译通过,后面运行,肯定有错误的。这种情况下,可以使用32位数据类型,分两条语句对变量赋值,当然,这是个时候要千万注意,不要把地址搞错了。
3.
Inline的问题,移植以后,编译的时候Inline经常会报错。虽然有编译选项可以去掉错误,不过你如果和我一样不熟悉的话,直接去掉
Inline关键字,到后面随着对VDSP熟悉以后,如果有优化的需要,再按照VDS
P的语法,为自己想要嵌入的函数增加Inline关键字。
经过上面的修改,一般情况下,编译就没有问题了,当然,这只是移植的第一步。距离成功,还很远!
1.
配置LDF文件。因为刚移植的代码,往往数据和程序都非常大,所以,SRAM里面肯定是放不下的,这个时候,链接就会有问题。刚开始的时候,最好把所有的程序和数据都放在SDRAM里面去,这样的,链接就不会有问题了。Stack和heap情况类似,开始的时候,都先放到SDRAM。开始的时候,你需要的是一个可以运行正确的程序,速度倒在其次。
2.Malloc的问题。DSP下的开发,malloc都是一个需要解决的问题。动态申请内存,就算可以运行,结果往往也是不对的。所以,最好进行静态分配,用数组的形式分配,这样做的好处是可以方便自己管理,那些数组多大,放在那里,自己都很清楚,因为优化的时候,有一些是要放在SRAM中,另外一些特别大的才放在SDRAM中,这样才能取的比较好的效果,另外,静态数组也稳定性一些,不需要记着去释放。
3.文件操作。在VDSP的SETTING下,有一个STDIO的开关,其实可以支持文件操作,但是我调试的时候发现,有些情况下是有问题的。比如我在一个循环中使用fread,但是他只有第一次的读取是有效的,但有些时候,它好像又可以。所以,你调试的时候,如果发现结果和VC下运行的不同,可以重点看看,是不是这里出了问题。
4.调试跟踪。经过上面的准备,程序已经可以运行了。你可以在Simulator下仿真,或者板子上直接仿真。在SI下,速度会很慢,不过
Sesion里面,有一个blackfin
family那个sision,速度还可以,当然,有板子会更好。我们开发的时候,我使用板子的时间总共不到两个月,所以浪费了很多时间,现在回头看看,好心痛。
调试结果OK了的话,说明移植已经成功了。就可以进入下一个最主要的阶段---优化了

DSP CACHE操作选项说明

直写式(write
through),也叫写透。即CPU在向Cache写入数据的同时,也把数据写入主存以保证Cache和主存中相应单元数据的一致性,其特点是简单可靠,但由于CPU每次更新时都要对主存写入,速度必然受影响。    
回写式(write
back)即CPU只向Cache写入,并用标记加以注明,直到Cache中被写过的块要被进入的信息块取代时,才一次写入主存。这种方式考虑到写入的往往是中间结果,每次写入主存速度慢而且不必要。其特点是速度快,避免了不必要的冗余写操作,但结构上较复杂。 
当CPU要进行写入操作时,只把数据写入Cache,而不直接写入主内存。这时,Cache与主内存之间会出现暂时不一致的数据块。当Cache中的不一致数据块将要被替代时,再把数据写回主内存,从而使Cache中的数据与主内存中的数据又再保持一致。在此方式下,需要在Cache中加入一个控制位(Dirty
bit),若Cache中的某数据块是由CPU写入的,则控制位=1,否则控制位=0。发生块替代时,Cache先检查被替代块的控制位,若控制位=0,则无需把内容写回主内存,若控制位=1,则执行写回操作。这样做的好处是当CPU多次刷新同一数据块时,只需把最后的结果写回主内存即可,从而避免了重复写入,因而具有较高的效率。在早期版本的BIOS中,用户还可以对采用写通方式还是写回方式进行设置,但在新版本的BIOS中已取消了这一设置。
可以认为Dirty表示cache中的数据被更改了
invalidate()则将内存中数据写入高速缓冲存储器。
因此,可以简单认为:在写之前需要invaliate,(invalidate里包含了writeback操作)
若一个cache是dirty的,则若需要对这块cache进行写操作时,需要将它writeback。

CCS中各个项目文件的作用

1.include头文件(.h)的主要作用
头文件,一般用于定义程序中的函数、参数、变量和一些宏单元,同库函数配合使用。因此,在使用库时,必须用相应的头文件说明。

2.DSP/BIOS CONFIG FILES
开发基于DSP/BIOS的程序保存BIOS配置之后自动产生的文件。在保存BIOS配置时候会产生program.cbd/programcfg.h54/programcfg.s54/programcfg.cmd/programcfg.h/programcfg_c.c(C5000的example,program为你的项目名),这些文件被分配在project不同目录。

3.Source Files
源程序,实现DSP系统指定功能的主要代码部分

4.Program.cmd
链接文件,在源文件经过汇编器(Assembler)输出的OBJ Files(目标文件)需要通过Linker(链接器)才能得到OUT files,在链接阶段Linker根据.cmd里面存储区、Section分配以及lib来链接rst.lib、csl.lib、DSP/BIOS library跟对段的重定位

5.Library Filers的作用
Rst.lib:C语言实时运行支持库,建立C运行环境,由_c_int00主要完成设置堆栈指针、初始化全局变量、调用main()
Csl.lib:芯片支持库,实现片内外设的操作
DSPLIB/IMGLIB:信号处理库,利用针对不同DSP的优化的函数进行数学运算
通用的视/音LIB:例如263、264、jpeg、G.7XX、mp3、wmv。。。
其他自己封装的LIB:例如bsl.lib(board source library),对DSP系统板上资源的操作。

其他文件:
.opt工程关于开发环境的参数文件。如工具条位置等信息;
.aps (AppStudio File),资源辅助文件,二进制格式,一般不用去管他.
.clw ClassWizard信息文件,实际上是INI文件的格式,有兴趣可以研究一下.有时候ClassWizard出问题,手工修改CLW文件可以解决.如果此文件不存在的话,每次用ClassWizard的时候绘提示你是否重建.
.dsp (DeveloperStudio Project):项目文件,文本格式,不过不熟悉的话不要手工修改.DSW(DeveloperStudio
Workspace)是工作区文件,其他特点和DSP差不多.
.plg 是编译信息文件,编译时的error和warning信息文件(实际上是一个html文件),一般用处不大.在Tools->Options里面有个选项可以控制这个文件的生成.
.hpj (Help Project)是生成帮助文件的工程,用microsfot Help Compiler可以处理.
.mdp (Microsoft DevStudio
Project)是旧版本的项目文件,如果要打开此文件的话,会提示你是否转换成新的DSP格式.
.bsc 是用于浏览项目信息的,如果用Source Brower的话就必须有这个文件.如果不用这个功能的话,可以在Project Options里面去掉Generate Browse Info File,可以加快编译速度.
.map 是执行文件的映像信息纪录文件,除非对系统底层非常熟悉,这个文件一般用不着.
.pch (Pre-Compiled File)是预编译文件,可以加快编译速度,但是文件非常大.
.pdb (Program Database)记录了程序有关的一些数据和调试信息,在调试的时候可能有用.
.exp 只有在编译DLL的时候才会生成,记录了DLL文件中的一些信息.一般也没什么用.
.ncb 无编译浏览文件(no compile browser)。当自动完成功能出问题时可以删除此文件。build后会自动生成。

转一个哥们儿的简历

http://www.soyudesign.com/zblog/post/209.html

求职意向: 嵌入式 or DSP开发工程师

教育背景: 2002年9月-2006 年7月:西安电子科技大学

项目经验:

1.
智能视频监控产品:基于DM642平台,负责软硬件及相关算法开发工作(以及相关的技术文档编写):

1) 硬件方面:

电路图PCB改版设计(使用PROTEL),硬件电路板焊接调试,

CPLD(ALTERA MAX II EMP240)程序编写,实现看门狗功能等(Verilog语言);

2) 软件方面:

DM642主程序框架的搭建,基于DSP/BIOS,采用RF5框架,

DM642驱动程序编写(EDMA,串口及云台控制,I2C,网络,FLASH,BOOT烧写启动,音频视频输入输出mini驱动),

PC端网络接收程序编写(基于TI及合众达demo程序修改);

3) 算法方面:

MPEG4算法:编码算法在DM642平台的移植及优化(程序结构调整,已经汇编优化等),

音频编解码算法在DM642平台的移植(G.711、G.726等算法),

智能视频算法:参与技术讨论(区域入侵、阴影去除、目标跟踪、人脸识别等算法),DM642平台部分算法的验证及移植(验证基于模板匹配的跟踪算法,移植mean
shift跟踪算法)。

2.
智能交通项目:基于DM642平台(硬件由大恒图像公司负责提供),负责软件及算法开发工作(以及相关的技术文档编写):

1) 软件方面:

DM642 主程序框架的搭建,基于DSP/BIOS,

DM642 FLASH烧写及boot实现,利用flashburn工具烧写;

2) 算法方面:

参与技术讨论(车牌定位分隔识别,车流密度检查等算法),

车牌字符识别算法在PC上实现及在DM642平台的移植(主要利用特征提取模板匹配算法)。

3.
DAVINCI开发项目:是与北京维德利公司智能视频监控产品的合作项目,基于DM6441
DAVINCI平台(硬件及相应linux驱动由上海通力公司负责提供),负责智能视频算法的移植整合以及linux下应用软件的开发工作:

1) 算法方面:

智能视频算法(区域入侵、摄像头诊断、丢包检测算法)在DAVINCI平台的移植,按照TI
xDM算法标准对这些算法进行打包;

2) 软件方面:

对应上面的算法编写相应的ARM端嵌入式应用程序,实现从视频驱动获取视频图像传给DSP算法端以及将算法处理结果输出等。

4. ADI BF561开发项目:基于ADI BlackFin BF561平台,负责软件及算法开发工作:

1) 软件方面:

主程序框架的搭建,

视频输入输出驱动的开发;

2) 算法方面:

摄像头消抖算法的移植验证。

5. PC上MPEG4编解码开发项目:

1) 编码方面:

算法优化,涉及到程序结构的调整,汇编优化(mmx、sse、sse2等),

2) 解码方面:

算法的实现以及优化,使用了intel的IPP库,解码速度为axis公司解码器速度的2/3。

6. 基于硬件压缩的视频监控产品:

1) Z1510 MPEG1视频监控产品:电路图设计
PCB,DOS应用程序(嵌入式)及驱动开发(cf卡、视频输入)。

2) SAA6752
MPEG2视频监控产品:电路图设计、PCB、DOS应用程序(嵌入式)及驱动开发(cf卡、视频输入)。

3) FIC8120 MPEG4 IP camera产品:电路图设计,PCB,linux
(arm9)应用程序及驱动开发(按键、 LED、 Flash、
boot、视频输入(SAA7113)输出(SAA7121))。

7. 其他的一些项目及产品开发等工作:

1) DSP5402的USB项目:处理器为TI
TMS320VC5402,usb接口芯片为PDIUSBD12,负责dsp端usb驱动程序以及PC端测试应用程序的编写(驱动采用D12通用驱动);

2) 使用过的其他器件:

ARM:S3C2410、S3C2440(驱动及应用程序),

单片机:c51、c8051F、MSP430、AVR、68HC等系列(软件硬件都做),

FPGA:EP1C3T144、EP1C6Q240(硬件以及Verilog程序,熟悉NIOS硬核),

DSP:TMS320F2407(驱动程序);

3) 使用的开发工具:

PC软件开发:

VC、C++builder、Matlab,

嵌入式软件开发:

Keil、IAR430、CodeWarrior、ICC、ADS、CCS、Visualdsp、VMware,

嵌入式硬件开发:

PROTEL、OrCad、Quartus、

熟练使用示波器外用表等硬件调试工具,熟练器件焊接。

4) 维护公司服务器(server 2003)以及网站(论坛后台)等。

自我评价:

知识结构比较完善,自学能力强,富有创造性思维,敢于做尝试性挑战性工作。
附录

大学期间的项目经验:
2004.9 -2004.12《声音定向系统》

实现功能:探测10米范围内声源方位。负责系统方案制定及硬件电路设计工作,该项目获2004年"首届全国大学生
IC设计大赛" 本科组一等奖。
在此期间还参加了"中国集成电路设计产业发展十周年高层论坛暨中国半导体行业协会集成电路设计分会'2004年会"。
成员:3人;
2004.12 《墙体探测器》

实现功能:探测墙内7厘米范围是否有异物,作品以"构思新颖,独特创新"获2004年校"星火杯"特等奖。
2005.1 -2005.5 《智能媒体播放器》

负责脉搏采集部分方案制定和模拟电路设计,该项目获2005
"DSP西部高校邀请赛"团体入围奖。 成员:6人;
2005.6 《3D显示器》

参加全国挑战杯项目,负责系统分析及硬件电路设计,并参与机械部分设计
成员:3人;
2005.9 《简易频谱分析仪》

此为参加全国电子设计竞赛所选题目,基本完成题目要求,获陕西赛区二等奖。主要负责主控芯片的编程工作,采用MSP430F149单片机作为主控芯片,
EP1C6Q240 FPGA实现外围控制和部分高速处理。
成员:3人;
2004.8 《基于DSP5416的脉象采集及分析系统》

项目方案来自《智能媒体播放器》,根据其心情识别部分所做改动,负责其系统硬件设计及编程,(算法来自《智能媒体播放器》心情识别部分)
成员:2人;
2003.12 《USB串口并口转换器》

使用PDIUSBD12,完成USB1.1接口转换功能;
2003.12 《USB2.0接口应用》

使用ISP1581 ,采用51单片机作为主控芯片,完成 USB2.0数据收发;
2004.3 《三角法激光测距仪》

负责硬件电路部分设计,该项目是与西安某公司合作;
2005.10 《nRF905无线收发模块》

功能:采用TI5402
DSK板,结合本模块实现语音信号的采集传输及回放,参与电路测试及软件调试,该项目以被列为TI-XD
DSP 实验室学生试验题目。成员:3人。
2006.1 -2006.6《TPMS汽车轮胎压力监测系统》

这是毕业设计所选题目,采用FREESCALE方案,所用主要芯片为:MC68hc908RF2,MC33594,MPXY8020A
,完成系统样机。

Linux/Unix环境下的Make和Makefile语法和详解

无论是在Linux还是在Unix环境中,make都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到
make或make
install。利用make工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用make和
makefile工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。而且如此多的源文件,如果每次都要键入gcc命令进行编译的话,那对程序员
来说简直就是一场灾难。而make工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用make和
makefile工具可以大大提高项目开发的效率。同时掌握make和makefile之后,您也不会再面对着Linux下的应用软件手足无措了。
但令人遗憾的是,在许多讲述Linux应用的书籍上都没有详细介绍这个功能强大但又非常复杂的编译工具。在这里我就向大家详细介绍一下make及其描述文件makefile。
Makefile文件
Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile
文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile
文件是许多编译器–包括 Windows NT
下的编译器–维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改
makefile 文件而已。
在 UNIX 系统中,习惯使用 Makefile 作为 makfile
文件。如果要使用其他文件作为 makefile,则可利用类似下面的 make
命令选项指定 makefile 文件:
$ make -f Makefile.debug
例如,一个名为prog的程序由三个C源文件filea.c、fileb.c和filec.c以及库文件LS编译生成,这三个文件还分别包含自己的
头文件a.h
、b.h和c.h。通常情况下,C编译器将会输出三个目标文件filea.o、fileb.o和filec.o。假设filea.c和fileb.c都要
声明用到一个名为defs的文件,但filec.c不用。即在filea.c和fileb.c里都有这样的声明:
#include "defs"
那么下面的文档就描述了这些文件之间的相互联系:
———————————————————
#It is a example for describing makefile
prog : filea.o fileb.o filec.o
cc filea.o fileb.o filec.o -LS -o prog
filea.o : filea.c a.h defs
cc -c filea.c
fileb.o : fileb.c b.h defs
cc -c fileb.c
filec.o : filec.c c.h
cc -c filec.c
———————————————————-
这个描述文档就是一个简单的makefile文件。
从上面的例子注意到,第一个字符为 #
的行为注释行。第一个非注释行指定prog由三个目标文件filea.o、fileb.o和filec.o链接生成。第三行描述了如何从prog所依赖的
文件建立可执行文件。接下来的4、6、8行分别指定三个目标文件,以及它们所依赖的.c和.h文件以及defs文件。而5、7、9行则指定了如何从目标所
依赖的文件建立目标。
当filea.c或a.h文件在编译之后又被修改,则 make
工具可自动重新编译filea.o,如果在前后两次编译之间,filea.C 和a.h
均没有被修改,而且test.o还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义,make
工具可避免许多不必要的编译工作。当然,利用Shell脚本也可以达到自动编译的效果,但是,Shell
脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make
工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
Makefile文件作为一种描述文档一般需要包含以下内容:
◆ 宏定义
◆ 源文件之间的相互依赖关系
◆ 可执行的命令
Makefile中允许使用简单的宏指代源文件及其相关编译信息,在Linux中也称宏为变量。在引用宏时只需在变量前加$符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。
下面都是有效的宏引用:
$(CFLAGS)
$2
$Z
$(Z)
其中最后两个引用是完全一致的。
需要注意的是一些宏的预定义变量,在Unix系统中,$*、$@、$?和$<四个特殊宏的值在执行命令的过程中会发生相应的变化,而在GNU
make中则定义了更多的预定义变量。关于预定义变量的详细内容,宏定义的使用可以使我们脱离那些冗长乏味的编译选项,为编写makefile文件带来很
大的方便。
———————————————————
# Define a macro for the object files
OBJECTS= filea.o fileb.o filec.o
# Define a macro for the library file
LIBES= -LS
# use macros rewrite makefile
prog: $(OBJECTS)
cc $(OBJECTS) $(LIBES) -o prog
……
———————————————————
此时如果执行不带参数的make命令,将连接三个目标文件和库文件LS;但是如果在make命令后带有新的宏定义:
make "LIBES= -LL -LS"
则命令行后面的宏定义将覆盖makefile文件中的宏定义。若LL也是库文件,此时make命令将连接三个目标文件以及两个库文件LS和LL。
在Unix系统中没有对常量NULL作出明确的定义,因此我们要定义NULL字符串时要使用下述宏定义:
STRINGNAME=
Make命令
在make命令后不仅可以出现宏定义,还可以跟其他命令行参数,这些参数指定了需要编译的目标文件。其标准形式为:
target1 [target2 …]:[:][dependent1 …][;commands][#…]
[(tab) commands][#…]
方括号中间的部分表示可选项。Targets和dependents当中可以包含字符、数字、句点和"/"符号。除了引用,commands中不能含有"#",也不允许换行。
在通常的情况下命令行参数中只含有一个":",此时command序列通常和makefile文件中某些定义文件间依赖关系的描述行有关。如果与目
标相关连的那些描述行指定了相关的command序列,那么就执行这些相关的command命令,即使在分号和(tab)后面的aommand字段甚至有
可能是NULL。如果那些与目标相关连的行没有指定command,那么将调用系统默认的目标文件生成规则。
如果命令行参数中含有两个冒号"::",则此时的command序列也许会和makefile中所有描述文件依赖关系的行有关。此时将执行那些与目标相关连的描述行所指向的相关命令。同时还将执行build-in规则。
如果在执行command命令时返回了一个非"0″的出错信号,例如makefile文件中出现了错误的目标文件名或者出现了以连字符打头的命令字符串,make操作一般会就此终止,但如果make后带有"-i"参数,则make将忽略此类出错信号。
Make命本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。其标准形式为:
Make [flags] [macro definitions] [targets]
Unix系统下标志位flags选项及其含义为:
-f
file  指定file文件为描述文件,如果file参数为"-"符,那么描述文件指向标准输入。如果没有"-f"参数,则系统将默认当前目录下名为
makefile或者名为Makefile的文件为描述文件。在Linux中, GNU make
工具在当前工作目录中按照GNUmakefile、makefile、Makefile的顺序搜索
makefile文件。
-i   忽略命令执行返回的出错信息。
-s   沉默模式,在执行之前不输出相应的命令行信息。
-r   禁止使用build-in规则。
-n   非执行模式,输出所有执行命令,但并不执行。
-t   更新目标文件。
-q   make操作将根据目标文件是否已经更新返回"0″或非"0″的状态信息。
-p   输出所有宏定义和目标文件描述。
-d   Debug模式,输出有关文件和检测时间的详细信息。
Linux下make标志位的常用选项与Unix系统中稍有不同,下面我们只列出了不同部分:
-c dir   在读取 makefile 之前改变到指定的目录dir。
-I dir   当包含其他 makefile文件时,利用该选项指定搜索目录。
-h   help文挡,显示所有的make选项。
-w   在处理 makefile 之前和之后,都显示工作目录。
通过命令行参数中的target
,可指定make要编译的目标,并且允许同时定义编译多个目标,操作时按照从左向右的顺序依次编译target选项中指定的目标文件。如果命令行中没有指定目标,则系统默认target指向描述文件中第一个目标文件。
通常,makefile 中还定义有 clean
目标,可用来清除编译过程中的中间文件,例如:
clean:
rm -f *.o
运行 make clean 时,将执行 rm -f *.o
命令,最终删除所有编译过程中产生的所有中间文件。
隐含规则
在make
工具中包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。Unix系统通常支持一种基于文件扩展名即文件名后缀的隐
含规则。这种后缀规则定义了如何将一个具有特定文件名后缀的文件(例如.c文件),转换成为具有另一种文件名后缀的文件(例如.o文件):
.c:.o
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
系统中默认的常用文件扩展名及其含义为:
.o  目标文件
.c  C源文件
.f  FORTRAN源文件
.s  汇编源文件
.y  Yacc-C源语法
.l  Lex源语法
在早期的Unix系统系统中还支持Yacc-C源语法和Lex源语法。在编译过程中,系统会首先在makefile文件中寻找与目标文件相关的.C
文件,如果还有与之相依赖的.y和.l文件,则首先将其转换为.c文件后再编译生成相应的.o文件;如果没有与目标相关的.c文件而只有相关的.y文件,
则系统将直接编译.y文件。
而GNU make
除了支持后缀规则外还支持另一种类型的隐含规则–模式规则。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。模式规则看起来非常类似于
正则规则,但在目标名称的前面多了一个 %
号,同时可用来定义目标和依赖文件之间的关系,例如下面的模式规则定义了如何将任意一个
file.c 文件转换为 file.o 文件:
%.c:%.o
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
#EXAMPLE#
下面将给出一个较为全面的示例来对makefile文件和make命令的执行进行进一步的说明,其中make命令不仅涉及到了C源文件还包括了
Yacc语法。本例选自"Unix Programmer's Manual 7th Edition, Volume 2A" Page
283-284
下面是描述文件的具体内容:
———————————————————
#Description file for the Make command
#Send to print
P=und -3 | opr -r2
#The source files that are needed by object files
FILES= Makefile version.c defs main.c donamc.c misc.c file.c \
dosys.c gram.y lex.c gcos.c
#The definitions of object files
OBJECTS= vesion.o main.o donamc.o misc.o file.o dosys.o gram.o
LIBES= -LS
LINT= lnit -p
CFLAGS= -O
make: $(OBJECTS)
cc $(CFLAGS) $(OBJECTS) $(LIBES) -o make
size make
$(OBJECTS): defs
gram.o: lex.c
cleanup:
-rm *.o gram.c
install:
@size make /usr/bin/make
cp make /usr/bin/make ; rm make
#print recently changed files
print: $(FILES)
pr $? | $P
touch print
test:
make -dp | grep -v TIME>1zap
/usr/bin/make -dp | grep -v TIME>2zap
diff 1zap 2zap
rm 1zap 2zap
lint: dosys.c donamc.c file.c main.c misc.c version.c gram.c
$(LINT) dosys.c donamc.c file.c main.c misc.c version.c \
gram.c
rm gram.c
arch:
ar uv /sys/source/s2/make.a $(FILES)
———————————————————-
通常在描述文件中应象上面一样定义要求输出将要执行的命令。在执行了make命令之后,输出结果为:
$ make
cc -c version.c
cc -c main.c
cc -c donamc.c
cc -c misc.c
cc -c file.c
cc -c dosys.c
yacc gram.y
mv y.tab.c gram.c
cc -c gram.c
cc version.o main.o donamc.o misc.o file.o dosys.o gram.o \
-LS -o make
13188+3348+3044=19580b=046174b
最后的数字信息是执行"@size
make"命令的输出结果。之所以只有输出结果而没有相应的命令行,是因为"@size
make"命令以"@"起始,这个符号禁止打印输出它所在的命令行。
描述文件中的最后几条命令行在维护编译信息方面非常有用。其中"print"命令行的作用是打印输出在执行过上次"make
print"命令后所有改动过的文件名称。系统使用一个名为print的0字节文件来确定执行print命令的具体时间,而宏$?则指向那些在print
文件改动过之后进行修改的文件的文件名。如果想要指定执行print命令后,将输出结果送入某个指定的文件,那么就可修改P的宏定义:
make print "P= cat>zap"
在Linux中大多数软件提供的是源代码,而不是现成的可执行文件,这就要求用户根据自己系统的实际情况和自身的需要来配置、编译源程序后,软件才能使用。只有掌握了make工具,才能让我们真正享受到到Linux这个自由软件世界的带给我们无穷乐趣。

quickfix提高vim编程编译调试效率

以前使用vim编程时,在终端下面至少2个tab,一个编辑一个编译,编译出错时,切换到编辑tab页进行差错纠正。今天又学习了vim的quickfix模式,quickfix是vim自带的,无需插件。

vim由一个程序员开发,而且为更多的程序员所使用,所以在vim中加强了对软件开发的支持,quickfix模式的引入就是一个例子。所谓quickfix模式,它和Normal模式、Insert模式没什么关系,它只是一种加快你开发速度的工作方式。
Quickfix模式的主要思想是保存一个位置列表,然后提供一系列命令,实现在这个位置列表中跳转。
位置列表的产生可以从编译器的编译输出信息中获得,也可以由grep命令的输出信息中获得。

[编译]
通常,我们在开发过程中,经常要写代码,编译,修改编译错误,这个过程会数十遍上百遍的重复。如果你是根据编译器输出的错误信息,打开出错的文件,找到出错的行,然后再开始修改,那效率未免太低下了。
利用vim的quickfix模式,可以大大加快这一过程,你可以在vim启动编译,然后vim会根据编译器输出的错误信息,自动跳到第一个出错的地方,让你进行修改;修改完后,使用一个快捷键,跳到下一个错误处,再进行修改,方便的很。
为了做到这一点,你首先要定义编译时所使用的程序,对大多数使用Makefile的项目来说,vim的缺省设置"make"已经可以满足要求了。如果你的项目需要用一个特殊的程序进行编译,就需要修改'makeprg'选项的值。
大家在学编程时大概都读过"hello world"程序,我们就以这个简单的例子为例,讲一下quickfix模式的用法。

该程序的内容如下,里面包含了三个小小的错误:
/* hello world demo */
#include <stdio.h"
int main(int argc, char **argv)
{
int i;
print("hello world\n");
return 0;
}

输入编译命令了:
:make
在使用":make"时,vim会自动编译,并把编译输出重定向到一个临时文件中,当编译出现错误时,vim会从上述临时文件中读出错误信息,根据这些信息形成quickfix列表,并跳转到第一个错误出现的地方。
对于我们上面的程序来说,光标会停在第三行,也就是第一个出错的位置,vim同时会提示出错信息。如果你没看清出错信息,可以输入":cc"命令,vim会更次显示此信息,或者干脆使用":cw"命令,打开一个quickfix窗口,把所有的出错信息显示出来,见下图:


现在我们知道错在哪儿了,修正一下,然后使用":cn"命令(或者在Quickfix List对应行上输入回车)跳到下一个出错的地方,以此类推,直到修正全部错误。
好了,千辛万苦,我们的hello world终于工作了。乍一看这个例子,似乎Quickfix并没有提高什么效率,但如果你的错误出现在多个不同目录的不同文件里,它可以帮你省很多时间,使你可以集中精力在修正bug上。
vim可以同时记住最新的10个错误列表,也就是说你最近10次使用":make"命令编译所遇到的错误都保存着,可以使用":colder"和":cnewer"命令,回到旧的错误列表,或者到更新的错误列表。

在quickfix模式里经常用到的命令有:
:cc 显示详细错误信息 ( :help :cc )
:cp 跳到上一个错误 ( :help :cp )
:cn 跳到下一个错误 ( :help :cn )
:cl 列出所有错误 ( :help :cl )
:cw 如果有错误列表,则打开quickfix窗口 ( :help :cw )
:col 到前一个旧的错误列表 ( :help :col )
:cnew 到后一个较新的错误列表 ( :help :cnew )

更多的命令,以及这些命令更详细的解释,请参见手册。
对于经常用到的命令,最好提供更方便的使用方法,在我的vimrc中的定义:
autocmd FileType c,cpp map <buffer> <leader><space> :w<cr>:make<cr>
nmap <leader>cn :cn<cr>
nmap <leader>cp :cp<cr>
nmap <leader>cw :cw 10<cr>

现在使用",<space>"(先按,再按空格)就可以编译,使用",cp"和",cn"跳到上一个和下一个错误,使用",cw"来打开一个quickfix窗口。这下顺手多了!
如果你希望跳转到出错的文件时,使用一个分隔的窗口打开,请参阅'switchbuf'选项的值。
在vim7中,每个窗口都可以拥有自己的位置列表,这样,你就能够同时打开多个位置列表了,而quickfix列表在整个vim中只有一个。你可以使用位置列表来显示编译错误信息,具体命令参阅手册:":help location-list"以及":help :lmake"。

结合文章http://dongpingli.blogspot.com/2009/03/makefile_27.html编写的通用makefile,这下在写新程序时,直接copy makefile到程序目录,在vim里面写完程序后直接make,然后查错修改在一个tab下,省得来回tab切换,效率会提高不少。