2009年4月30日星期四

[转]Facebook 如何管理150亿张照片(值得学习)

Facebook 的照片分享很受欢迎,迄今,Facebook 用户已经上传了150亿张照片,加上缩略图,总容量超过1.5PB,而每周新增的照片为2亿2000万张,约25TB,高峰期,Facebook 每秒处理55万张照片,这些数字让如何管理这些数据成为一个巨大的挑战。本文由 Facebook 工程师撰写,讲述了他们是如何管理这些照片的。

旧的 NFS 照片架构
老的照片系统架构分以下几个层:
# 上传层接收用户上传的照片并保存在 NFS 存储层。
# 照片服务层接收 HTTP 请求并从 NFS 存储层输出照片。
# NFS存储层建立在商业存储系统之上。

因为每张照片都以文件形式单独存储,这样庞大的照片量导致非常庞大的元数据规模,超过了 NFS 存储层的缓存上限,导致每次招聘请求会上传都包含多次I/O操作。庞大的元数据成为整个照片架构的瓶颈。这就是为什么 Facebook 主要依赖 CDN 的原因。为了解决这些问题,他们做了两项优化:
# Cachr: 一个缓存服务器,缓存 Facebook 的小尺寸用户资料照片。
# NFS文件句柄缓存:部署在照片输出层,以降低 NFS 存储层的元数据开销。

新的 Haystack 照片架构
新的照片架构将输出层和存储层合并为一个物理层,建立在一个基于 HTTP 的照片服务器上,照片存储在一个叫做 haystack 的对象库,以消除照片读取操作中不必要的元数据开销。新架构中,I/O 操作只针对真正的照片数据(而不是文件系统元数据)。haystack 可以细分为以下几个功能层:
# HTTP 服务器
# 照片存储
# Haystack 对象存储
# 文件系统
# 存储空间

存储
Haystack 部署在商业存储刀片服务器上,典型配置为一个2U的服务器,包含:
# 两个4核CPU
# 16GB – 32GB 内存
# 硬件 RAID,含256-512M NVRAM 高速缓存
# 超过12个1TB SATA 硬盘

每个刀片服务器提供大约10TB的存储能力,使用了硬件 RAID-6, RAID 6在保持低成本的基础上实现了很好的性能和冗余。不佳的写性能可以通过高速缓存解决,硬盘缓存被禁用以防止断电损失。
文件系统
Haystack 对象库是建立在10TB容量的单一文件系统之上。文件系统中的每个文件都在一张区块表中对应具体的物理位置,目前使用的文件系统为 XFS。
Haystack 对象库
Haystack 是一个简单的日志结构,存储着其内部数据对象的指针。一个 Haystack 包括两个文件,包括指针和索引文件:



Haystack 对象存储结构



指针和索引文件结构




Haystack 写操作
Haystack 写操作同步将指针追加到 haystack 存储文件,当指针积累到一定程度,就会生成索引写到索引文件。为了降低硬件故障带来的损失,索引文件还会定期写道存储空间中。

Haystack 读操作
传到 haystack 读操作的参数包括指针的偏移量,key,代用Key,Cookie 以及数据尺寸。Haystack 于是根据数据尺寸从文件中读取整个指针。

Haystack 删除操作
删除比较简单,只是在 Haystack 存储的指针上设置一个已删除标志。已经删除的指针和索引的空间并不回收。

照片存储服务器
照片存储服务器负责接受 HTTP 请求,并转换成相应的 Haystack 操作。为了降低I/O操作,该服务器维护着全部 Haystack 中文件索引的缓存。服务器启动时,系统就会将这些索引读到缓存中。由于每个节点都有数百万张照片,必须保证索引的容量不会超过服务器的物理内存。

对于用户上传的图片,系统分配一个64位的独立ID,照片接着被缩放成4种不同尺寸,每种尺寸的图拥有相同的随机 Cookie 和 ID,图片尺寸描述(大,中,小,缩略图)被存在代用key 中。接着上传服务器通知照片存储服务器将这些资料联通图片存储到 haystack 中。

每张图片的索引缓存包含以下数据

Haystack 使用 Google 的开源 sparse hash data 结构以保证内存中的索引缓存尽可能小。
照片存储的写/修改操作
写操作将照片数据写到 Haystack 存储并更新内存中的索引。如果索引中已经包含相同的 Key,说明是修改操作。

照片存储的读操作
传递到 Haystack 的参数包括 Haystack ID,照片的 Key, 尺寸以及 Cookie,服务器从缓存中查找并到 Haystack 中读取真正的数据。

照片存储的删除操作
通知 Haystack 执行删除操作之后,内存中的索引缓存会被更新,将便宜量设置为0,表示照片已被删除。

重新捆扎
重新捆扎会复制并建立新的 Haystack,期间,略过那些已经删除的照片的数据,并重新建立内存中的索引缓存。

HTTP 服务器
Http 框架使用的是简单的 evhttp 服务器。使用多线程,每个线程都可以单独处理一个 HTTP 请求。

结束语
Haystack 是一个基于 HTTP 的对象存储,包含指向实体数据的指针,该架构消除了文件系统元数据的开销,并实现将全部索引直接存储到缓存,以最小的 I/O 操作实现对照片的存储和读取。

本文国际来源:http://www.facebook.com/FacebookEngineering#/note.php?note_id=76191543919&ref=mf
中文翻译来源:COMSHARP CMS 官方网站

编译安装Vala Toys for GEdit

今天终于编译安装成功了Vala Toys for GEdit 。它是一个不错的vala语言的gedit插件。
按照:http://code.google.com/p/vtg/wiki/Compile的步骤编译安装。
不过需要注意的是它的依赖(我选的svn里的版本,vala语言的版本也是0.7.1):
需要安装的东西一般是:

sudo apt-get install libreadline5-dev gedit-dev

就可以了。如果不装全会出现错误:

gtksourcecompletion需要编译安装,这个容易./configure, make, make install就可以了。

就下来正式安装就是:

./autogen.sh --prefix=/home/zmm/.gnome2/gedit/plugins --datadir=/home/
zmm/.gnome2/gedit/plugins

make

make install

即可,最后完成了安装。不过如何启用这个插件呢?

很简单,将~/.gnome2/gedit/plugins/lib/gedit-2/plugins下的东西移到~/.gnome2/gedit/plugins/即可。在重新打开Gedit在首选项/插件中启用vala toy即可。

附录:目前插件还在开发之中,发现new project选项不是很好用。期待以后改正,目前可以用
/home/zmm/.gnome2/gedit/plugins/bin目录下的vala-gen-project生成工程打开。vsc-shell的功能还在琢磨中...

[转]西方教育让我儿子失去了“理想”

看了我写儿子在西方上学的文章,很多朋友写信给我,希望我就教育问题写一些文章,发表一些看法,提出一些建议,推动一些改革。我又何尝不想呢?我一直以来都认为教育是头等的大事。如果想一个国家发生变化,最平和又快捷的办法就是对成人加强启蒙教育或者宣传洗脑,可如果想让一个国家彻底发生变化,则非得从孩子的教育开始不可。

对成年人的思想控制,可以用一句话来概括:控制了现在,就可以改变过去;改变了过去,也就掌握了未来。说的是篡改历史和控制舆论的重要性。可如果在和平年代,则我认为下面的话更加具有普适意义:谁控制了孩子的教育,谁就掌握了未来。

当然,我这里说的教育,强调的是教育的内容,是我们用什么来教育我们的孩子,而不是多少人读书,多少人失学的问题。

我对一个国家教育的重要性的认识有一个过程,和我出国之后的经历有一定的关系。那时我很想知道中西文化的区别到底在哪里,以及为什么只要在大陆读过小学和中学的年轻人出国后就永远无法真正融入西方社会,而在西方受到小学和中学教育的移民第二代却再也无法融入中国大陆的社会——你能告诉我有一个海外出生的华人华侨能够从精神上回流到中国大陆的例子吗?

十几年前出国后,我除了老老实实潜伏下来,研究美国等西方国家的政治和军事外,有段时间我一度把注意力转向教育,因为我隐约地意识到,无论政治还是军事,甚至是经济,都不能最终决定我们国家和民族的未来,我们国家的未来最终将由我们施于孩子身上的教育来决定。

于是,我先后在美国和澳洲大学(美国的是华盛顿的一所大学,澳洲的是悉尼大学教育学院)里申请了教育学硕士等课程,但都因为看不到显著效果而半途而废。可我却得出了一个结论,教育形式和方式不重要,重要的是教育的内容。因此,我一度悲观地认为,与其任凭学校把那些垃圾和有害的东西灌输给我们的孩子,不如让他们当文盲。当文盲的话,他们总还有靠自己的天性和经历觉醒的一天,可一旦被灌输了那些所谓的“知识”和害人的价值观,他们就沦落为世界上最不可救药的“知识人”了。

没有拿到教育学的硕 士、博士学位,可我自认对教育还是有一定发言权的,我从中国农村小学读起,又到多个国家读书,在四十多的高龄才拿到博士学位,没有经验也有教训吧。可我不 得不承认,由于最终认定是教育的内容决定一个国家和民族命运,而我始终无法掌握外国小学和中学的教育内容——因为我不可能再靠我一直自鸣得意的“实践”的 方式到西方国家的小学、中学去“潜伏”,去取经。

可是,那句话怎么说 的,有心栽花花不发,无意插柳柳成荫,没有想到,我对中西教育最大的灵感和收获却来自我的两个儿子。大儿子在国内读幼儿园,到美国和澳洲读小学,小儿子出 生在国外。我对他们的教育一直很重视,那是父亲对儿子的本能关心。可后来,就在我对教育很迷茫的时候,我逐渐发现,两个分别在美国和澳洲从幼儿园一路读到 高中和小学的儿子,却给我另外一种启示。虽然我竭力在用自己认为是对的方式方法教育他们,然而,短短两年后,我就发现在教育上(包括世界观上)他们和我渐 行渐远。那时我最想知道的是,澳洲人使用什么样的教材,在短短几年里,把我的儿子从我身边“夺走”?我并不是没有说服力的人,可面对他们学校整天放羊似的 教育,我竟然显得无能为力!

(插一句,说到美国 和澳洲“放羊似的教育”,我要控诉:几乎就在儿子要考大学的前一年,他们竟然还是我行我素,根本没有延长教学时间,早上九点才上课,下午三点就放学了,中 间竟然还有那么多体育和课外活动。读小学的儿子就更不用说了,读到四年级,竟然没有给我带回一本让老子看看他在学什么的“课本”,到他教室去,竟然发现, 他大部分时间是爬在地上一边玩耍一边听老师讲课的,我郁闷啊!想当初老子在神州大地的时候,从小学到高中,早起晚归,忙得连偷看邻家女孩的时间都没有……)

从自己和儿子身上,我深深的感觉到,教育的重要性在于教育的内容,而我们长成什么样的人,是在小学和中学就被决定了的。从那以后,我发现自己在研究教育上的“潜伏”毫无疑义,反而是无意中以儿子做实验,让我感受良多,也最终让我明白,人是怎样被教育出来的?我为什么是我?我的儿子又和我有什么不同……以及,你为什么是你那些年轻人又是怎么变成今天这个样子的……

大家注意到,我并没有说哪种教育好哪种教育坏的意思,这不是此篇短文里要探讨的。实际上,中国现代的教育体系几乎都是从西方引进的,特别是理工科的设置和内容几乎全盘西化,就连社会科学,各科目的设置也是从欧美以及前苏联引进的。唯一有中国特色的是我们的政治、思想教育和语文课文内容的设置,让我们有了中国特色。

和很多朋友期待的我来攻击一番中国教育相反,今天我要“攻击”的正好是儿子所受的西方的教育。我的大儿子十六岁了,快要高考了。我对大儿子影响比较大,从幼儿园就灌输他要“有所作为”,他的成绩也一直很好。可是正如我所说的,他已经从我对他的中国式的灌输渐渐走开。

我曾经按照我的父亲对待我的方式多次和儿子讨论理想,也就是今后想干啥事、想从事什么职业,一开始,他还能听我忽悠,要做一个有意义的人,要当科学家,要当公务员,要当作家,要当……。可最近一次他的回答让我非常吃惊,他对我说,可以开一个卖东西的小店铺啊……

乖乖隆的冬!你不在 中国,我就不怪你不想当雷锋、王杰、黄继光,但你至少也应该有当一名厅长、局长或者师长、团长的志向吧,再不济,你也应该搞个科学家、不拿工资的市长、总 经理、作家什么的玩玩吧?没想到他的理想就是去开一个小商店,看到我横眉冷对怒发冲冠血脉膨胀浑身上下气不打一处来的样子,儿子心态平和地说,能养活自 己,干点自己想干的事,就很快乐了。

鉴于万恶的西方教育 把我的大儿子搞得一点“理想”都没有了,我就立即把关注焦点转移到刚上四年级的小儿子身上。我要多和他交流,多引导引导他。特别是利用他回国的机会,我得 让他从小心中有一些榜样。要知道,在中国的教育下,像小儿子这么大的孩子,早就知道这个世界上不但有那么好的主席(主席的睡衣)、那么好的将军(朱德的扁 担)、还有让孩子都能流出眼泪的“人民的好总理”恩来同志,而且,九岁的中国孩子谁不知道雷锋叔叔的故事?

可是,我郁闷地发现,我的那一套在小儿子身上完全不起作用,才多少年没有把他抱在怀里啊?竟然这么快就被澳洲的学校“洗脑”了?!

很显然,我已经无法 在儿子心中树立一些我认为是楷模的榜样,无论是什么“家”还是那些有权有钱的人,小儿子几乎没有任何概念,说到“当官的”,在他眼中,竟然和工人、农民没 有任何区别,甚至“当官的”还没有一个拥有几匹马的农民酷。在儿子眼里,当官是不实际的,又不开工厂,又不卖东西,靠什么赚钱呢?靠税收养活的人,总要看 纳税人的脸色,腰杆总是站不直的啊。(所以大家也应该注意到,西方最优秀的人才,总是集中在创造财富、钻研科技、从事教育等领域,至于公仆的行列里,大多 是碌碌无为的家伙)

在儿子们的学校,好 像没有任何政治思想教育课,也没理想培养之类的课程,我曾经和八十岁的老父亲讨论这个问题,他和我一样感觉问题很严重,我们当时一起哀叹,这种(西方)国 家,到时培养出来的人怎么会有干劲和理想?我们一致同意,关于理想、道德和中国式的思想教育这一课,就由我这个老爸来完成。

不幸的是,我失败了,更不幸的是,儿子并没有失败——不幸在哪里?不幸在我从中国带出来的那一套是失败的!更不幸在哪里?在于我那失败的一套正在教育全中国的孩子!

人家的孩子培养出来却能够让这个社会挺和谐的,甚至那些整天放羊似的教育却仍然培养出在科学技术和各方面都引导世界潮流的人才,而我们,一个最注重理想和思想品德教育的国家,却培养出——我不说别人,说我自己好不好?——培养出我这种到了四十岁还在迷茫和彷徨的人……

我再强调一句,区别不在教育的方式方法,甚至不在于有多少人不识字,是文盲,更不在于我们的孩子是否从小学就开始加班加点学“知识” ……问题的关键,也即是我们国家和民族的前途在于:我们用什么样的价值观和“知识”来教育我们的孩子!

杨恒均2009/4/30

vs2005打开项目后自动关闭问题

阔别多日打开了vs2005看看自己所写的一些代码,但是发现打开项目之后vs2005自动关闭了。google之后发现时vmware的问题。
找到了以下的回复:
我遇到过类似的问题,发现是VMware(我用的是6.0)搞的鬼(因为我把它的几个自动启动的服务 VMware Authorization/VMware DHCP Service/VMware NAT Service给设成手动并停止了)
visual Studio 可以正常使用.
前几天为了试装一下leopard装了一个vmware。现在没什么需要了,干脆卸了。

重启之后万事大吉了!

2009年4月29日星期三

编译vala过程

今天编译vala了,一直想这么干,今天终于实现了。当然首先下载vala的源码。然后安装flex和bison两个东西。使用典型的apt安装

sudo apt-get install flex bison

然后解压源码之后输入
./configure

make

sudo make install
本人使用./configure时加了参数--perfix=/usr/local所以导致了一个安装完后运行时的一个错误:
valac: error while loading shared libraries: libvala.so.0: cannot open shared object file: No such file or directory
应该是共享库的路径没有/usr/local/lib。
解决方法很简单就是修改/etc/ld.so.conf加入/usr/local/lib一行,然后运行sudo ldconfig
之后就可以了。

[Shell]set、env、export的区别

set 显示当前shell的变量,包括当前用户的变量
env 显示当前用户的变量
export 显示当前导出成用户变量的shell变量

每个shell有自己特有的变量(set)显示的变量,这个和用户变量是不同的,当前用户变量和你用什么shell无关,不管你用什么shell都在,比 如HOME,SHELL等这些变量,但shell自己的变量不同shell是不同的,比如BASH_ARGC, BASH等,这些变量只有set才会显示,是bash特有的,export不加参数的时候,显示哪些变量被导出成了用户变量,因为一个shell自己的变 量可以通过export “导出”变成一个用户变量。

2009年4月28日星期二

自己动手丰衣足食,hack了一个gnome主题

今天实在是对gnome look上的主题忍无可忍了,都不是很喜欢。趁着9.04刚装完,心情正好,故改了一个以前的一直较为喜欢的主题:Glow Leopard。

以下是截图:


下载就不方上来了,网盘什么的比较麻烦。有喜欢的,email我就可以了(zmmbreeze0825@gmail.com)。

2009年4月27日星期一

《时代》:世界上最有影响力的人

转自:solidot:世界上最有影响力的人是.....

Christopher Poole,听说过吗?《时代》杂志网站报道,Christopher Poole(aka Moot),21岁的大学生和在线社区4chan.org的创始人,在TIME的年度Top 100投票中,战胜了奥巴马总统、普京总理、电视主持人奥普拉温芙瑞、达赖喇嘛、教皇、乔治W布什、胡锦涛主席等大腕,成为世界上最有影响力的人,他得到 了16,794,368张投票,平均影响指数90。《时代》委婉的承认网络投票很不靠谱。 4chan.org创建于2003年,平均每天PV数850万,每月独立访问者330万,被认为是世界第4大公告栏。

本人仔细看了4chan.org的网站发现时隔不错的找动漫壁纸或图片的地方,当然貌似有些18禁的图片,好孩子不要看了,另外还是一个不错的找gif表情的地方。

2009年4月26日星期日

ubuntu9.04上体验gnome+kde core

今天早上终于升级到9.04了,看到了新的通知系统。很兴奋!的确很不错,但是除了播放音乐的时候出现的频率不高,对整个桌面的美感提升不高。
无奈gnome2.26目前还不够漂亮,但是一直是用惯了gnome,期待gnome3.0的同时发现kde4.2的稳定性以及美化程度都已经很高了。所以想体验下,为了不全部安装kde所以只选择安装了其核心部分。
输入如下命令:sudo apt-get install kde0-core
然后在启动项里面添加命令:plasma
然后重启之后就可以见到gnome与kde4.2共存的界面,可以的是kde-panel上的菜单中没有关机按钮,估计是因为没有权限吧!
不过无伤大雅!

2009年4月25日星期六

推荐两个Firefox插件:NewTabKing、SiteLauncher

打开常用网页的时候还需要书签栏的帮助,但是本人偏偏不是很喜欢书签栏的存在总是与隐藏着,为了更大的浏览空间和美观。所以找了两个插件,让我更快速的打开网页并且不用书签栏。

New Tab King:这个插件可以统计自己过去的浏览纪录,会把自己最常去的网站列出来。在打开一个新的标签页的时候,在新标签页上列出常去的网站,十分方便。

下载地址:https://addons.mozilla.org/en-US/firefox/addon/10828
缺点是不支持linux。
所以在使用liux的时候我使用另一个插件:SiteLauncher
这个插件不是很智能,但是外观不错,在linux下时用此代替New Tab king是个不错的选择。

下载地址:https://addons.mozilla.org/en-US/firefox/addon/10127

[转]朝鲜人民的幸福生活(组图)


鸭绿江畔女朝鲜哨兵


朝鲜哨兵枪指摄影师


朝鲜小孩在玩


赶牛车的人


放鹅的男孩


到冰上提水的女人


挑水回家的女人


鸭绿江畔女哨兵


在领袖头像下面劳动的女人


下班的士兵在打篮球


弹吉它的朝鲜士兵


上班路上的女工人


中朝边界线上的士兵

图片来自http://www.boston.com/bigpicture/2009/04/peering_into_north_korea.html

感想:话说最后一位哥们长的真像小兵张嘎的扮演着!

2009年4月24日星期五

新劳动法解读之劳动者解约

>>法条新规

第二十五条除本法第二十二条和第二十三条规定的情形外,用人单位不得与劳动者约定由劳动者承担违约金。

>>出台动机

北京律协劳动法律委员会委员马照辉表示:此前,劳动法律及一些地方法规大多规定,用人单位与职工可以约定违约金。劳动者提前解除劳动合同的,应该支付约定的违约金。“新法的一个重大变化就是:员工一般不承担违约金责任。”

>>权威解读

马照辉解释称,之所以有这个规定,目的是防止公司滥用违约金条款,事先约定高额违约金来限制员工流动。新法施行后,员工可以主动离职,并无需承担违约金责任。即使公司自行规定违约金,法院今后也不会支持。

但新法亦对此作了2个例外规定:一是在公司支付培训费用并约定了服务期限后,员工在约定的服务期内主动离职,应当赔偿违约金;二是,在违反竞业限制责任时,员工也应该承担违约金责任。

马照辉进一步解释称,这里的培训,不是普通的、必要的培训,而是专项技术培训。因培训产生的违约金数额,不得超过公司实际支出的培训费用。此外,公司要求员工支付的违约金,亦不得超过服务期尚未履行部分所应分摊的培训费用。

公司因竞业限制原因,约定员工需承担违约金,也应付出相应代价:在约定违约金的同时,必须同时约定在竞业限制期限内按月给予员工经济补偿。

北京市一中院法官黄彩相表示,在北京地区,单位和员工之间涉及户口问题而发生的违约金纠纷较多。以往的审判实践中,法院通常会根据案件的具体情况,对明显过高的违约金给予调整,但基本上都持支持态度。“劳动合同法实施后,今后此种违约金约定将因违反法律的禁止性规定而无效。”

黄彩相同时表示,对于培训费用,普通、必要的职业培训与专项技术培训也不能混为一谈,且应以实际为员工专项技术培训所支出的费用来约定违约金数额。

>>法条新规

第四十六条(节选)有下列情形之一的,用人单位应当向劳动者支付经济补偿:

(一)劳动者依照本法第三十八条规定解除劳动合同的[1];

(五)除用人单位维持或者提高劳动合同约定条件续订劳动合同,劳动者不同意续订的情形外,依照本法第四十四条第一项规定终止固定期限劳动合同的。

>>出台动机

北京律协劳动法律委员会委员马照辉表示,之前,除了公司以暴力、威胁或者非法限制人身自由的手段强迫员工劳动,员工因此解除合同,公司应当向员工支付经济补偿金外,员工主动提出解除劳动合同时,公司一般无须支付经济补偿金。劳动合同法在这方面做出了很大的突破,大大扩充了员工解除合同可获得经济补偿金的范围。

>>权威解读

马照辉表示:“本次劳动法修改,加大了对劳动者的保护,精髓就体现在这。”

他分析称:“现实生活中,个别公司寻找理由逼迫员工主动提出解除劳动合同,以此来规避支付经济补偿金的义务。员工遇到此类行为后,或根本无权主张补偿,或只能依照实际发生的损失要求赔偿。新法在这里做了很大的突破性规定。”

此外,新法还增加了一条特别规定:劳动合同期满,除用人单位维持或者提高劳动合同约定条件续订劳动合同,劳动者不同意续订的情形外,终止固定期限劳动合同的,公司仍需支付经济补偿金。

马照辉认为:补偿金范围扩大导致解除合同的成本加大,有利于引导公司与员工订立长期或无固定期限劳动合同。

试用期辞职应三日前通知

>>法条新规

第三十七条(节选)劳动者在试用期内提前三日通知用人单位,可以解除劳动合同。

>>出台动机

市一中院法官黄彩相称:“这个变化,是为了兼顾公司利益与员工利益。”他介绍称:此前施行的《劳动法》,在第三十二条曾规定:“在试用期间内,劳动者可以随时通知用人单位解除劳动合同。”现实生活中,员工在试用期内,随时撂挑子不干的行为,给用人单位的正常工作秩序带来了一些不利的影响。

>>权威解读

黄彩相法官说,之所以作此改变,主要是考虑:首先,现实生活中,公司对试用期员工的辞职也需要一个工作交接。其次,此时辞职并非用人单位的过错导致,应当给予用人单位适当的时间来安排其他人接替辞职员工的工作,这与劳动者提前三十日通知方可辞职的规定目的基本是一致的。

黄彩相认为,适用该条提出辞职的时候,员工应当注意两个问题:一是要确定是否在试用期内,双方约定的试用期若因违反规定而无效的,比如过长的试用期约定、多次约定试用期等等。在这些情况下,双方确定的“试用期”实际上就不属于该条规定中的试用期,此时辞职还是应当提前三十日通知用人单位;二是向用人单位发出解除劳动合同通知时,应当尽量保存相关的证据材料。

马照辉还认为,对于员工来说,这一条也相对公平,因为试用期内行使解除合同的权利,不同于因公司违法行为被迫离职的情形,无须紧迫地随时解除。

[1] 新劳动法第三十八条
[2] 新劳动合同法解释:第二十六条【劳动合同的无效】

2009年4月22日星期三

iPhone SDK教学视频在线观看

- iPhone SDK介绍

- iPhone开发工具介绍

- iPhone应用程序开发-起步

- iPhone应用程序框架-进阶

- iPhone图形和媒体概览

- 在程序中使用iPhone特性

- iPhone程序用户界面设计

- iPhone程序开发关键练习

- 获取iPhone定位、加速、方向及系统信息的方法

- 针对Web开发人员的iPhone SDK

如无法直接播放,请访问 这个地址 进行观看。如果您是ADC会员,也可以直接在iTunes中观看。

2009年4月21日星期二

2009年4月19日星期日

面向对象中类、对象、实例

今天对面向对象的一些概念重新了解了下,发现我以前了解的一些概念有些错误。

例如代码:
class Boy{
string name;
public Boy(String name){
this.name = name;
}

public static void main(String[] args) {
Boy b = new Boy();
}
}

上述代码中Boy是类,b是对象同时又是实例。

***类:是具有相同属性特征和行为规则的多个“对象”的一种统一描述。

***对象:是对现实世界中实体的一种模拟工具。

***实例:是某某的具体的实体。例如java中对象是类的实例。

有些情况下描述不一样,如GLib库实现的面向对象编程。
对象的结构分为三部分:
  1. 对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);
  2. 对象的类结构(唯一,结构型,由对象的所有实例共同拥有);
  3. 对象的实例(多个,结构型,对象的具体实现)。
以上中类是所有对象共有的属性或方法用结构表示,表示的是共有特性。对象的实例是对象的具体实现,它有自己私有的属性方法。也用结构型实现,此结构类型的变量就是真正使用的实例。

这种情况与java有所不同

面向对象三个特性封装、继承、多态

面向对象的三个基本特征是:封装、继承、多态。

o_OOBase.gif

封装

封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

继承

面向对象编程 (OOP) 语言的一个主要功能就是继承。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。

被继承的类称为基类父类超类

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过继承Inheritance)和组合Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式有三类:实现继承、接口继承和可视继承。

Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;

Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

Ø 可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。

在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是属于关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class

OO开发范式大致为:划分对象抽象类将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

其实,重载的概念并不属于面向对象编程,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_funcstr_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是覆盖。 当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法 确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对 象也无关!引用一句Bruce Eckel的话:不要犯傻,如果它不是晚邦定,它就不是多态。

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用家谱中任一类的实例的某一属性时的正确调用。

概念讲解

泛化(Generalization

o_Generalization.gif

图表 1 泛化

在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这种关系被称为泛化(Generalization)。Person()是基类,Teacher(教师)Student(学生)Guest(来宾)是子类。

若在逻辑上BA的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性。

例如,教师是人,Teacher Person的“一种”(a kind of )。那么类Teacher可以从类Person派生(继承)。

如果A是基类,BA的派生类,那么B将继承A的数据和函数。

如果类A和类B毫不相关,不可以为了使B的功能更多些而让B继承A的功能和属性。

若在逻辑上BA的“一种”(a kind of ),则允许B继承A的功能和属性。

聚合(组合)

o_aggregationBase.gif

图表 2 组合

若在逻辑上AB的“一部分”(a part of),则不允许BA派生,而是要用A和其它东西组合出B

例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类EyeNoseMouthEar组合而成,不是派生(继承)而成。

聚合的类型分为无、共享(聚合)、复合(组合)三类。

聚合(aggregation

o_aggregation.gif

图表 3 共享

上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不需要对被聚合的类A负责。

组合(composition

o_composition.gif

图表 4 复合

这幅图与上面的唯一区别是菱形为实心的,它代表了一种更为坚固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。

依赖(Dependency)

o_Dependency.gif

图表 5 依赖

这里BA的关系只是一种依赖(Dependency)关系,这种关系表明,如果类A被修改,那么类B会受到影响。

[GLib学习]GObject与GType

*****我本来想学习GLib,因为那是最棒的方法用C来实现OOP。但是开始很困难,对C语言了解的不是很深,所以最近一直在看C语言的基础部分。今天终于对GLib的面向对象是想了解了大概*****

***GLib简介:

GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据结构的定义、相关的处理函 数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。它能够在类UNIX的操作系统平台(如 LINUX, HP-UNIX等),WINDOWS,OS2和BeOS等操作系统台上运行。

GLib需要一个支持线程的操作系统和一个字符集间转换函数iconv的支持,事实上大多现代的操作系统都有以上两项功能。

GLib由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成的。

***GObject系统
GLib实现面向对象主要归功于GObject子系统。GObject系统主要依赖于GType系统,它是以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。

GType 是GLib 运行时类型认证和管理系统。GType API 是GObject的基础系统,所以理解GType是理解GObject的关键。Gtype提供了注册和管理所有基本数据类型、用户定义对象和界面类型的技 术实现。(注意:在运用任一GType和GObject函数之前必需运行g_type_init()函数来初始化类型系统。)

为实现类型定制和注册这一目的,所有类型必需是静态的或动态的这二者之一。静态的类型永远不能在运行时加载或卸载,而动态的类型则可以。静态类型由 g_type_register_static()创建,通过GTypeInfo结构来取得类型的特殊信息。动态类型则由 g_type_register_dynamic()创建,用GTypePlugin结构来取代GTypeInfo,并且还包括 g_type_plugin_*()系列API。这些注册函数通常只运行一次,目的是取得它们返回的专有类的类型标识。

还可以用g_type_register_fundamental来注册基础类型,它同时需要GTypeInfo和GTypeFundamentalInfo两个结构,事实上大多数情况下这是不必要的,因为系统预先定义的基础类型是优于用户自定义的。

(本文重点介绍创建和使用静态的类型。)

***面向对象实现
基于GObject的对象到底是什么样的呢?下面是基于GObject的简单对象 -- Boy的定义代码:
/* boy.h */
#ifndef __BOY_H__
#define __BOY_H__
#include
#define BOY_TYPE (boy_get_type())
#define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),BOY_TYPE,Boy))
typedef struct _Boy Boy;
typedef struct _BoyClass BoyClass;
struct _Boy {
GObject parent;
//
gint age;
gchar *name;
void (*cry)(void);
};
struct _BoyClass {
GObjectClass parent_class;
//
void (*boy_born)(void);
};
GType boy_get_type(void);
Boy* boy_new(void);
int boy_get_age(Boy *boy);
void boy_set_age(Boy *boy, int age);
char* boy_get_name(Boy *boy);
void boy_set_name(Boy *boy, char *name);
Boy* boy_new_with_name(gchar *name);
Boy* boy_new_with_age(gint age);
Boy* boy_new_with_name_and_age(gchar *name, gint age);
void boy_info(Boy *boy);
#endif /* __BOY_H__*/

结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。Boy对象中的parent表示此对象的父 类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;其它的成员可以是公共的,这里包括表示年龄的age,表示名字的 name和表示方法的函数指针cry,外部代码可以操作或引用它们。

结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。BoyClass中的 parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。在 BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的,所有的Boy实例都可以调用它;同 样,如果需要的话,你也可以在类结构中定义其它数据成员。

其余的函数定义包括三种,一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;另一种是创建Boy对象实例的函数 boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;第三种是设 定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。正常情况下这三种函数都是一个对象所必需的,另外一个函数 boy_info用来显示此对象的当前状态。

下面的代码实现了上面的Boy对象的定义:
/* boy.c */
#include "boy.h"
enum { BOY_BORN, LAST_SIGNAL };
static gint boy_signals[LAST_SIGNAL] = { 0 };
static void boy_cry (void);
static void boy_born(void);
static void boy_init(Boy *boy);
static void boy_class_init(BoyClass *boyclass);
GType boy_get_type(void)
{
static GType boy_type = 0;
if(!boy_type)
{
static const GTypeInfo boy_info = {
sizeof(BoyClass),
NULL,NULL,
(GClassInitFunc)boy_class_init,
NULL,NULL,
sizeof(Boy),
0,
(GInstanceInitFunc)boy_init
};
boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);
}
return boy_type;
}
static void boy_init(Boy *boy)
{
boy->age = 0;
boy->name = "none";
boy->cry = boy_cry;
}
static void boy_class_init(BoyClass *boyclass)
{
boyclass->boy_born = boy_born;
boy_signals[BOY_BORN] = g_signal_new("boy_born",
BOY_TYPE,
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET(BoyClass,boy_born),
NULL,NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, NULL);
}
Boy *boy_new(void)
{
Boy *boy;
boy = g_object_new(BOY_TYPE, NULL);
g_signal_emit(boy,boy_signals[BOY_BORN],0);
return boy;
}
int boy_get_age(Boy *boy)
{
return boy->age;
}
void boy_set_age(Boy *boy, int age)
{
boy->age = age;
}
char *boy_get_name(Boy *boy)
{
return boy->name;
}
void boy_set_name(Boy *boy, char *name)
{
boy->name = name;
}
Boy* boy_new_with_name(gchar *name)
{
Boy* boy;
boy = boy_new();
boy_set_name(boy, name);
return boy;
}
Boy* boy_new_with_age(gint age)
{
Boy* boy;
boy = boy_new();
boy_set_age(boy, age);
return boy;
}
Boy *boy_new_with_name_and_age(gchar *name, gint age)
{
Boy *boy;
boy = boy_new();
boy_set_name(boy,name);
boy_set_age(boy,age);
return boy;
}
static void boy_cry (void)
{
g_print("The Boy is crying ......\n");
}
static void boy_born(void)
{
g_print("Message : A boy was born .\n");
}
void boy_info(Boy *boy)
{
g_print("The Boy name is %s\n", boy->name);
g_print("The Boy age is %d\n", boy->age);
}

在这段代码中,出现了实现Boy对象的关键函数,这是在Boy对象的定义中未出现的,也是没必要出现的。就是两个初始化函数,boy_init和 boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用,关键是将其用宏转换为地址指针,然后赋值到 GTypeInfo结构中,然后由GType系统自行处理,同时将它们定义为静态的也是非常必要的。

GTypeInfo结构中定义了对象的类型信息,包括以下内容:

  1. 包括类结构的长度(必需,即我们定义的BoyClass结构的长度);
  2. 基础初始化函数(base initialization function,可选);
  3. 基础结束化函数(base finalization function,可选);

    (以上两个函数可以对对象使用的内存来做分配和释放操作,使用时要用GBaseInitFunc和GBaseFinalizeFunc来转换为指针,本例中均未用到,故设为NULL。)

  4. 类初始化函数(即我们这里的boy_class_init函数,用GclassInit宏来转换,可选,仅用于类和实例类型);
  5. 类结束函数(可选);
  6. 实例初始化函数(可选,即我们这里的boy_init函数);
  7. 最后一个成员是GType变量表(可选)。

定义好GTypeInfo结构后就可以用g_type_register_static函数来注册对象的类型了。

g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我 们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类;第二个参数表示此对象的名称,这里为"Boy";第三个参数是此对象的 GTypeInfo结构型指针,这里赋值为&boyinfo;第四个参数是对象注册成功后返回此对象的整型ID标识。

g_object_new函数,用来创建一个基于G_OBJECT的对象,它可以有多个参数,第一个参数是上面说到的已 注册的对象标识ID;第二个参数表示后面参数的数量,如果为0,则没有第三个参数;第三个参数开始类型都是GParameter类型,它也是一个结构型, 定义为:

struct GParameter{
const gchar* name;
GValue value;
};
关于GValue,它是变量类型的统一定义,它是基础的变量容器结构,用于封装变量的值和变量的类型,可以GOBJECT文档的GVALUE部分。

***对象的属性和方法

对象实例所有的属性和方法一般都定义在对象的实例结构中,属性定义为变量或变量指针,而方法则定义为函数指针,如此,我们一定要定义函数为static类型,当为函数指针赋值时,才能有效。

***对象的继承

以下为继承自Boy对象的Man对象的实现,Man对象在Boy对象的基础上又增加了一个属性job和一个方法bye。

#ifndef __MAN_H__
#define __MAN_H__
#include "boy.h"
#define MAN_TYPE (man_get_type())
#define MAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),MAN_TYPE,Man))
typedef struct _Man Man;
typedef struct _ManClass ManClass;
struct _Man {
Boy parent;
char *job;
void (*bye)(void);
};
struct _ManClass {
BoyClass parent_class;
};
GType man_get_type(void);
Man* man_new(void);
gchar* man_get_gob(Man *man);
void man_set_job(Man *man, gchar *job);
Man* man_new_with_name_age_and_job(gchar *name, gint age, gchar *job);
void man_info(Man *man);
#endif //__MAN_H__
/* man.c */
#include "man.h"
static void man_bye(void);
static void man_init(Man *man);
static void man_class_init(Man *man);
GType man_get_type(void)
{
static GType man_type = 0;
if(!man_type)
{
static const GTypeInfo man_info = {
sizeof(ManClass),
NULL, NULL,
(GClassInitFunc)man_class_init,
NULL, NULL,
sizeof(Man),
0,
(GInstanceInitFunc)man_init
};
man_type = g_type_register_static(BOY_TYPE, "Man", &man_info, 0);
}
return man_type;
}
static void man_init(Man *man)
{
man->job = "none";
man->bye = man_bye;
}
static void man_class_init(Man *man)
{
}
Man* man_new(void)
{
Man *man;
man = g_object_new(MAN_TYPE, 0);
return man;
}
gchar* man_get_gob(Man *man)
{
return man->job;
}
void man_set_job(Man *man, gchar *job)
{
man->job = job;
}
Man* man_new_with_name_age_and_job(gchar *name, gint age, gchar *job)
{
Man *man;
man = man_new();
boy_set_name(BOY(man), name);
boy_set_age(BOY(man), age);
man_set_job(man, job);
return man;
}
static void man_bye(void)
{
g_print("Goodbye everyone !\n");
}
void man_info(Man *man)
{
g_print("the man name is %s\n", BOY(man)->name);
g_print("the man age is %d\n", BOY(man)->age);
g_print("the man job is %s\n", man->job);
}
关键在于定义对象时将父对象实例定义为Boy,父类设定为BoyClass,在注册此对象时将其父对象
类型设为BOY_TYPE,在设定对象属性时如用到父对象的属性要强制转换下,如取得对象的name属性,就必须用BOY(obj)->name,因为Man本身没
有name属性,而其父对象Boy有,所以用BOY宏将其强制为Boy类型的对象。

***使用我们定义的对象
#include
#include "boy.h"
#include "man.h"
int main(int argc, char *argv[])
{
Boy *tom, *peter;
Man *green, *brown;
g_type_init();//注意,初始化类型系统,必需
tom = boy_new_with_name("Tom");
tom->cry();
boy_info(tom);
peter = boy_new_with_name_and_age("Peter", 10);
peter->cry();
boy_info(peter);
green = man_new();
boy_set_name(BOY(green), "Green");
//设定Man对象的name属性用到其父对象Boy的方法
boy_set_age(BOY(green), 28);
man_set_job(green, "Doctor");
green->bye();
man_info(green);
brown = man_new_with_name_age_and_job("Brown", 30, "Teacher");
brown->bye();
man_info(brown);
}
Makefile文件如下:
CC = gcc
all:
$(CC) -c boy.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -c man.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -c main.c `pkg-config --cflags glib-2.0 gobject-2.0`
$(CC) -o simple boy.o man.o main.o `pkg-config --libs glib-2.0 gobject-2.0`
执行make命令编译,编译结束后,执行./simple运行此测试程序,输出结果如下:
Message : A boy was born .
The Boy is crying ......
The Boy name is Tom
The Boy age is 0
Message : A boy was born .
The Boy is crying ......
The Boy name is Peter
The Boy age is 10
Goodbye everyone !
the man name is Green
the man age is 28
the man job is Doctor
Goodbye everyone !
the man name is Brown
the man age is 30
the man job is Teacher

Makefile中用到`pkg-config -cflags -libs gobject-2.0`,在GLIB中将线程(gthread),插件
(gmoudle)和对象系统(gobject)这三个子系统区别对待,编译时要注意加入相应的参数。

C语言中函数名与函数指针

函数名与函数指针

一 通常的函数调用
一个通常的函数调用的例子:
//自行包含头文件
void MyFun(int x); //此处的申明也可写成:void MyFun( int );

int main(int argc, char* argv[])
{
MyFun(10); //这里是调用MyFun(10);函数

return 0;
}

void MyFun(int x) //这里定义一个MyFun函数
{
printf(“%d\n”,x);
}
这个MyFun函数是一个无返回值的函数,它并不完成什么事情。这种调用函数的格式你应该是很熟悉的吧!看主函数中调用MyFun函数的书写格式:
MyFun(10);
我们一开始只是从功能上或者说从数学意义上理解MyFun这个函数,知道MyFun函数名代表的是一个功能(或是说一段代码)。
直到——
学习到函数指针概念时。我才不得不在思考:函数名到底又是什么东西呢?
(不要以为这是没有什么意义的事噢!呵呵,继续往下看你就知道了。)

二 函数指针变量的申明
就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的首地址也以存储在某个函数指针变量里的。这样,我就可以通过这个函数指针变量来调用所指向的函数了。
在C系列语言中,任何一个变量,总是要先申明,之后才能使用的。那么,函数指针变量也应该要先申明吧?那又是如何来申明呢?以上面的例子为例,我来申明一个可以指向MyFun函数的函数指针变量FunP。下面就是申明FunP变量的方法:
void (*FunP)(int) ; //也可写成void (*FunP)(int x);
你看,整个函数指针变量的申明格式如同函数MyFun的申明处一样,只不过——我们把MyFun改成(*FunP)而已,这样就有了一个能指向MyFun函数的指针FunP了。(当然,这个FunP指针变量也可以指向所有其它具有相同参数及返回值的函数了。)

三 通过函数指针变量调用函数
有了FunP指针变量后,我们就可以对它赋值指向MyFun,然后通过FunP来调用MyFun函数了。看我如何通过FunP指针变量来调用MyFun函数的:
//自行包含头文件
void MyFun(int x); //这个申明也可写成:void MyFun( int );
void (*FunP)(int ); //也可申明成void(*FunP)(int x),但习惯上一般不这样。

int main(int argc, char* argv[])
{
MyFun(10); //这是直接调用MyFun函数
FunP=&MyFun; //将MyFun函数的地址赋给FunP变量
(*FunP)(20); //这是通过函数指针变量FunP来调用MyFun函数的。
}

void MyFun(int x) //这里定义一个MyFun函数
{
printf(“%d\n”,x);
}
请看黑体字部分的代码及注释。
运行看看。嗯,不错,程序运行得很好。
哦,我的感觉是:MyFun与FunP的类型关系类似于int 与int *的关系。函数MyFun好像是一个如int的变量(或常量),而FunP则像一个如int *一样的指针变量。
int i,*pi;
pi=&i; //与FunP=&MyFun比较。
(你的感觉呢?)
呵呵,其实不然——

四 调用函数的其它书写格式
函数指针也可如下使用,来完成同样的事情:
//自行包含头文件
void MyFun(int x);
void (*FunP)(int ); //申明一个用以指向同样参数,返回值函数的指针变量。

int main(int argc, char* argv[])
{
MyFun(10); //这里是调用MyFun(10);函数
FunP=MyFun; //将MyFun函数的地址赋给FunP变量
FunP(20); //这是通过函数指针变量来调用MyFun函数的。

return 0;
}

void MyFun(int x) //这里定义一个MyFun函数
{
printf(“%d\n”,x);
}
我改了黑体字部分(请自行与之前的代码比较一下)。
运行试试,啊!一样地成功。
咦?
FunP=MyFun;
可以这样将MyFun值同赋值给FunP,难道MyFun与FunP是同一数据类型(即如同的int 与int的关系),而不是如同int 与int*的关系了?(有没有一点点的糊涂了?)
看来与之前的代码有点矛盾了,是吧!所以我说嘛!
请容许我暂不给你解释,继续看以下几种情况(这些可都是可以正确运行的代码哟!):
代码之三:
int main(int argc, char* argv[])
{
MyFun(10); //这里是调用MyFun(10);函数
FunP=&MyFun; //将MyFun函数的地址赋给FunP变量
FunP(20); //这是通过函数指针变量来调用MyFun函数的。

return 0;
}
代码之四:
int main(int argc, char* argv[])
{
MyFun(10); //这里是调用MyFun(10);函数
FunP=MyFun; //将MyFun函数的地址赋给FunP变量
(*FunP)(20); //这是通过函数指针变量来调用MyFun函数的。

return 0;
}
真的是可以这样的噢!
(哇!真是要晕倒了!)
还有呐!看——
int main(int argc, char* argv[])
{
(*MyFun)(10); //看,函数名MyFun也可以有这样的调用格式

return 0;
}
你也许第一次见到吧:函数名调用也可以是这样写的啊!(只不过我们平常没有这样书写罢了。)
那么,这些又说明了什么呢?
呵呵!假使我是“福尔摩斯”,依据以往的知识和经验来推理本篇的“新发现”,必定会由此分析并推断出以下的结论:
1. 其实,MyFun的函数名与FunP函数指针都是一样的,即都是函数指针。MyFun函数名是一个函数指针常量,而FunP是一个函数数指针变量,这是它们的关系。
2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许MyFun(10);这种形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3. 为统一起见,FunP函数指针变量也可以FunP(10)的形式来调用。
4. 赋值时,即可FunP=&MyFun形式,也可FunP=MyFun。
上述代码的写法,随便你爱怎么着!
请这样理解吧!这可是有助于你对函数指针的应用喽!
最后——
补充说明一点:在函数的申明处:
void MyFun(int ); //不能写成void (*MyFun)(int )。
void (*FunP)(int ); //不能写成void FunP(int )。
(请看注释)这一点是要注意的。

五 定义某一函数的指针类型:
就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数指针变量。
我先给你一个自定义数据类型的例子。
typedef int* PINT; //为int* 类型定义了一个PINT的别名
int main()
{
int x;
PINT px=&x; //与int * px=&x;是等价的。PINT类型其实就是int * 类型
*px=10; //px就是int*类型的变量
return 0;
}
根据注释,应该不难看懂吧!(虽然你可能很少这样定义使用,但以后学习Win32编程时会经常见到的。)
下面我们来看一下函数指针类型的定义及使用:(请与上对照!)
//自行包含头文件
void MyFun(int x); //此处的申明也可写成:void MyFun( int );
typedef void (*FunType)(int ); //这样只是定义一个函数指针类型
FunType FunP; //然后用FunType类型来申明全局FunP变量

int main(int argc, char* argv[])
{
//FunType FunP; //函数指针变量当然也是可以是局部的 ,那就请在这里申明了。
MyFun(10);
FunP=&MyFun;
(*FunP)(20);

return 0;
}

void MyFun(int x)
{
printf(“%d\n”,x);
}

看黑体部分:
首先,在void (*FunType)(int ); 前加了一个typedef 。这样只是定义一个名为FunType函数指针类型,而不是一个FunType变量。
然后,FunType FunP; 这句就如PINT px;一样地申明一个FunP变量。
其它相同。整个程序完成了相同的事。
这样做法的好处是:
有了FunType类型后,我们就可以同样地、很方便地用FunType类型来申明多个同类型的函数指针变量了。如下:
FunType FunP2;
FunType FunP3;
//……

六 函数指针作为某个函数的参数
既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。所以,你还应知道函数指针是如何作为某个函数的参数来传递使用的。
给你一个实例:
要求:我要设计一个CallMyFun函数,这个函数可以通过参数中的函数指针值不同来分别调用MyFun1、MyFun2、MyFun3这三个函数(注:这三个函数的定义格式应相同)。
实现:代码如下:
//自行包含头文件
void MyFun1(int x);
void MyFun2(int x);
void MyFun3(int x);
typedef void (*FunType)(int ); //②. 定义一个函数指针类型FunType,与①函数类型一至
void CallMyFun(FunType fp,int x);

int main(int argc, char* argv[])
{
CallMyFun(MyFun1,10); //⑤. 通过CallMyFun函数分别调用三个不同的函数
CallMyFun(MyFun2,20);
CallMyFun(MyFun3,30);
}
void CallMyFun(FunType fp,int x) //③. 参数fp的类型是FunType。
{
fp(x);//④. 通过fp的指针执行传递进来的函数,注意fp所指的函数是有一个参数的
}
void MyFun1(int x) // ①. 这是个有一个参数的函数,以下两个函数也相同
{
printf(“函数MyFun1中输出:%d\n”,x);
}
void MyFun2(int x)
{
printf(“函数MyFun2中输出:%d\n”,x);
}
void MyFun3(int x)
{
printf(“函数MyFun3中输出:%d\n”,x);
}
输出结果:略

C语言中enum、struct、union

enmu:

在C语言中有一种对软件工程来讲,可以大大提高程序可读性的数据类型,那就是枚举(enum)。一般的定义方式如下:
enum enum_type_name{
ENUM_CONST_1,
ENUM_CONST_2,
...
ENUM_CONST_n
} enum_variable_name;
enum_type_name 是自定义的一种数据数据类型,而enum_variable_name为enum_type_name类型的一个变量。实际上 enum_type_name类型是对一个变量取值范围的限定,而花括号内是它的取值范围,即enum_type_name 类型的变量 enum_variable_name只能取值为花括号内的任何一个值,如果赋给该类型变量的值不在列表中,则会报错或者警告。

实际上在此定义了一组常量:ENUM_CONST_1、ENUM_CONST_2、...、ENUM_CONST_n,这些常量可以用来给任何一种类型的数据赋值,在这种意义下与#define定义的常量几乎没有区别。
enum变量类型还可以给其中的常量符号赋值,如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,它们的值从0开始依次递增1。如分别用一个常数表示不同颜色
typedef enum{
GREEN = 1,
RED,
BLUE,
GREEN_RED = 10,
GREEN_BLUE
}Color.
其中各常量名代表的数值分别为:
GREEN = 1
RED = 2
BLUE = 3
GREEN_RED = 10
GREEN_BLUE = 11

struct and union:

union共用体
构造数据类型,也叫联合体
用途:使几个不同类型的变量共占一段内存(相互覆盖)

struct结构体是一种构造数据类型
用途:把不同类型的数据组合成一个整体-------自定义数据类型

---------------------------------------------------------------


结构体变量所占内存长度是各成员占的内存长度的总和。


共同体变量所占内存长度是各最长的成员占的内存长度。


共同体每次只能存放哪个的一种!!

共同体变量中起作用的成员是尊后一次存放的成员,
在存入新的成员后原有的成员失去了作用!

---------------------------------------------------------------


Structure 与 Union主要有以下区别:

1. struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而 struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。 在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。

2. 对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。

2009年4月18日星期六

glibc 2.10的新特性

GNU C 函数库(GNU C Library,简称glibc)是开源的C编译程序,最新版是2.9版。glibc的维护者Ulrich Dreeper的博客上透露了2.10版的主要特性
POSIX 2008,C++兼容,支持C++ 201x,DNS NSS改进,libcrypt中使用网络安全服务包(Network Security Services),printf hooks,更好的malloc可扩展性,Automatic使用优化函数等等。

C语言静态变量与静态函数

C语言程序可以看成由一系列外部对象构成,这些外部对象可能是变量或函数。而内部变量是指定义在函数内部的函数参数及变量。外部变量定义在函数之外,因此可以在许多函数中使用。由于C语言不允许在一个函数中定义其它函数,因此函数本身只能是“外部的”。

由于C语言代码是以文件为单位来组织的,在一个源程序所有源文件中,一个外部变量或函数只能在某个文件中定义一次,而其它文件可以通过extern声明来访问它(定义外部变量或函数的源文件中也可以包含对该外部变量的extern声明)。
而static则可以限定变量或函数为静态存储。如果用static限定外部变量与函数,则可以将该对象的作用域限定为被编译源文件的剩余部分。通过 static限定外部对象,可以达到隐藏外部对象的目的。因而,static限定的变量或函数不会和同一程序中其它文件中同名的相冲突。如果用 static限定内部变量,则该变量从程序一开始就拥有内存,不会随其所在函数的调用和退出而分配和消失。
C语言中使用静态函数的好处

  1. 静态函数会被自动分配在一个一直使用的存储区,直到退出应用程序实例,避免了调用函数时压栈出栈,速度快很多。
  2. 关键字“static”,译成中文就是“静态的”,所以内部函数又称静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限 于本文件。 使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名,因为同名也没有关系。

c语言中static的语义
1.static变量:
1).局部
a.静态局部变量在函数内定义,生存期为整个源程序,但作用域与自动变量相同,只能在定义该变量的函数内使用。退出该函数后, 尽管该变量还继续存在,但不能使用它。
b.对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。
2).全局
全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。但是他们的作用域,非静态全局 变量的作用域是整个源程序(多个源文件可以共同使用); 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。
2.static函数(也叫内部函数)
只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。区别于一般的非静态函数(外部函数)
static在c里面可以用来修饰变量,也可以用来修饰函数。
先看用来修饰变量的时候。变量在c里面可分为存在全局数据区、栈和堆里。其实我们平时所说的堆栈是栈而不包含对,不要弄混。
int a ;
main()
{
int b ;
int c* = (int *)malloc(sizeof(int));
}
a是全局变量,b是栈变量,c是堆变量。
static对全局变量的修饰,可以认为是限制了只能是本文件引用此变量。有的程序是由好多.c文件构成。彼此可以互相引用变量,但加入static修饰之后,只能被本文件中函数引用此变量。
static对栈变量的修饰,可以认为栈变量的生命周期延长到程序执行结束时。一般来说,栈变量的生命周期由OS管理,在退栈的过程中,栈变量的生命也就 结束了。但加入static修饰之后,变量已经不在存储在栈中,而是和全局变量一起存储。同时,离开定义它的函数后不能使用,但如再次调用定义它的函数 时,它又可继续使用, 而且保存了前次被调用后留下的值。
static对函数的修饰与对全局变量的修饰相似,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。
static 声明的变量在C语言中有两方面的特征:
  1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
  2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。

  问题:Static的理解

  关于static变量,请选择下面所有说法正确的内容:

  A、若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;

  B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;

  C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;

  D、静态全局变量过大,可那会导致堆栈溢出。

  答案与分析:

  对于A,B:根据本篇概述部分的说明b),我们知道,A,B都是正确的。

  对于C:根据本篇概述部分的说明a),我们知道,C是正确的(所谓的函数重入问题,下面会详细阐述)。

  对于D:静态变量放在程序的全局数据区,而不是在堆栈中分配,所以不可能导致堆栈溢出,D是错误的。

  因此,答案是A、B、C。

  问题:不可重入函数

  曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?

unsigned int sum_int( unsigned int base )
{
 unsigned int index;
 static unsigned int sum = 0; // 注意,是static类型的。
 for (index = 1; index <= base; index++)
 {
  sum += index;
 }
 return sum;
}

  答案与分析:

  所谓的函数是可重入的(也可以说是可预测的),即:只要输入数据相同就应产生相同的输出。
  这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函 数。因此如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。
  将上面的函数修改为可重入的函数很简单,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto 类型的变量,函数即变为一个可重入的函数。
  当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。

sizeof()函数深入理解

一、好首先看看sizeof和strlen在MSDN上的定义:

首先看一MSDN上如何对sizeof进行定义的:

sizeof Operatorsizeof expressionThe sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t.The expression is either an identifier or a type-cast expression (a type specifier enclosed in parentheses).When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment. When applied to a statically dimensioned array, sizeof returns the size of the entire array. The sizeof operator cannot return the size of dynamically allocated arrays or external arrays.然后再看一下对strlen是如何定义的:

strlenGet the length of a string.Routine Required Header:strlen size_t strlen( const char *string );Parameterstring:Null-terminated string LibrariesAll versions of the C run-time libraries.Return ValueEach of these functions returns the number of characters in string, excluding the terminal NULL. No return value is reserved to indicate an error.RemarksEach of these functions returns the number of characters in string, not including the terminating null character. wcslen is a wide-character version of strlen; the argument of wcslen is a wide-character string. wcslen and strlen behave identically otherwise.

二、由几个例子说开去。

第一个例子:

char* ss = "0123456789";sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针sizeof(*ss) 结果 1 ===》*ss是第一个字符char ss[] = "0123456789";sizeof(ss) 结果 11 ===》ss是数组,计算到\0位置,因此是10+1sizeof(*ss) 结果 1 ===》*ss是第一个字符char ss[100] = "0123456789";sizeof(ss) 结果是100 ===》ss表示在内存中的大小 100×1strlen(ss) 结果是10 ===》strlen是个函数内部实现是用一个循环计算到\0为止之前int ss[100] = "0123456789";sizeof(ss) 结果 400 ===》ss表示再内存中的大小 100×4strlen(ss) 错误 ===》strlen的参数只能是char* 且必须是以''\0''结尾的char q[]="abc";char p[]="a\n";sizeof(q),sizeof(p),strlen(q),strlen(p);结果是 4 3 3 2

第二个例子:

class X{int i;int j;char k;};X x;cout<<<<

第三个例子:

char szPath[MAX_PATH]

如果在函数内这样定义,那么sizeof(szPath)将会是MAX_PATH,但是将szPath作为虚参声明时(void fun(char szPath[MAX_PATH])),sizeof(szPath)却会是4(指针大小)

三、sizeof深入理解。

* 1.sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。
* 2.sizeof是算符,strlen是函数。
* 3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。sizeof还可以用函数做参数,比如:

short f();printf("%d\n", sizeof(f()));

输出的结果是sizeof(short),即2。
* 4.数组做sizeof的参数不退化,传递给strlen就退化为指针了。
* 5.大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因

char str[20]="0123456789";int a=strlen(str); //a=10;int b=sizeof(str); //而b=20;

* 6.strlen的结果要在运行的时候才能计算出来,时用来计算字符串的长度,不是类型占内存的大小。
* 7.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。
* 8.当适用了于一个结构类型时或变量, sizeof 返回实际的大小, 当适用一静态地空间数组, sizeof 归还全部数组的尺 寸。 sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸
* 9.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:

fun(char [8])fun(char [])

都等价于 fun(char *) 在C++里传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小如果想在函数内知道数组的大小, 需要这样做:进入函数后用memcpy拷贝出来,长度由另一个形参传进去

fun(unsiged char *p1, int len){ unsigned char* buf = new unsigned char[len+1] memcpy(buf, p1, len);}

有关内容见: C++ PRIMER?
* 10. 计算结构变量的大小就必须讨论数据对齐问题。为了CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。当然这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可.或者如果要按字节存储,而不进行数据对齐,可以在Options对话框中修改Advanced compiler页中的Data alignment为按字节对齐。
* 11.sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时 char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式

四、结束语

sizeof使用场合。

* 1.sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如:

  void *malloc(size_t size),   size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。

* 2.用它可以看看一类型的对象在内存中所占的单元字节。

void * memset(void * s,int c,sizeof(s))

* 3.在动态分配一对象时,可以让系统知道要分配多少内存。
* 4.便于一些类型的扩充,在windows中就有很多结构内型就有一个专用的字段是用来放该类型的字节大小。
* 5.由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。
* 6.如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。

2009年4月17日星期五

memset函数详细说明

1。void *memset(void *s,int c,size_t n)
总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。

2。例子
#include

void main(){
char *s="Golden Global View";

clrscr();

memset(s,'G',6);
printf("%s",s);

getchar();
return 0;
} 
3。memset() 函数常用于内存空间初始化。如:
char str[100];
memset(str,0,100);

4。memset()的深刻内涵:用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘\0’;例:char a[100];memset(a, '\0', sizeof(a));

memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。

strcpy就只能拷贝字符串了,它遇到'\0'就结束拷贝;例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。

5.补充:一点心得
memset可以方便的清空一个结构类型的变量或数组。

如:
struct sample_struct
{
char csName[16];
int iSeq;
int iType;
};

对于变量
struct sample_strcut stTest;

一般情况下,清空stTest的方法:
stTest.csName[0]='\0';
stTest.iSeq=0;
stTest.iType=0;

用memset就非常方便:
memset(&stTest,0,sizeof(struct sample_struct));

如果是数组:
struct sample_struct TEST[10];

memset(TEST,0,sizeof(struct sample_struct)*10);

6。strcpy
原型:extern char *strcpy(char *dest,char *src);
用法:#i nclude
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
memcpy
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#i nclude
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
memset
原型:extern void *memset(void *buffer, int c, int count);
用法:#i nclude
功能:把buffer所指内存区域的前count个字节设置成字符c。
说明:返回指向buffer的指针。

malloc,calloc,realloc区别

三个函数的申明分别是:
void* realloc(void* ptr, size_t newsize);
void* malloc(size_t size);
void* calloc(size_t numElements, size_t sizeOfElement);
size_t 表示字节
都在stdlib.h函数库内

它们的返回值都是请求系统分配的地址,如果请求失败就返回NULL

malloc用于申请一段新的地址,参数size为需要内存空间的长度,如:
char* p;
p=(char*)malloc(20);

calloc与malloc相似,参数sizeOfElement为申请地址的单位元素长度,numElements为元素个数,如:
char* p;
p=(char*)calloc(20,sizeof(char));
这个例子与上一个效果相同

realloc是给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度
如:
char* p;
p=(char*)malloc(sizeof(char)*20);
p=(char*)realloc(p,sizeof(char)*40);

注意,这里的空间长度都是以字节为单位。


C语言的标准内存分配函数:malloc,calloc,realloc,free等。
malloc与calloc的区别为1块与n块的区别:
malloc调用形式为(类型*)malloc(size):在内存的动态存储区中分配一块长度为“size”字节的连续区域,返回该区域的首地址。
calloc调用形式为(类型*)calloc(n,size):在内存的动态存储区中分配n块长度为“size”字节的连续区域,返回首地址。
realloc调用形式为(类型*)realloc(*ptr,size):将ptr内存大小增大到size。


free的调用形式为free(void*ptr):释放ptr所指向的一块内存空间。
C++中为new/delete函数。

C语言中const的作用

使用const的好处在于它允许指定一种语意上的约束------某种对象不能被修改--------编译器具体来实施这种约束。通过const,你可以 通知编译器和其他程序员某个值要保持不变。只要是这种情况,你就要明确地使用const ,因为这样做就可以借助编译器的帮助确保这种约束不被破坏。
(一)
首先解释一下const与指针的关系:
const在指针的声明中有一下三种形式:
const char *p = "hello"; // 非const指针,
// const数据,就是说p指向的那个内存空间的数据是不可变的,但p还可以指向新的内存地址。

char * const p = "hello"; // const指针,
// 非const数据,就是说这个指针p一旦赋值或初始化,就不能在指向其他位置了,但其指向的位置的数据值是可变的。

const char * const p = "hello"; // const指针,
// const数据,这个就很明显了,集上述两家之长处(也可能是短处哦,),上述两者都不可变。
一般来说,你可以在头脑里画一条垂直线穿过指针声明中的星号(*)位置,如果const出现在线的左边,指针指向的数据为常量;如果const出现在线的右边,指针本身为常量;如果const在线的两边都出现,二者都是常量。

恩,差点忘了,还有一种形式:
char const * p = "hello";
这其实与上边的情形一是一样的,只是由于个人习惯的不同,二者都是对的。

(二)
在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。
const(1) int fun(int const(2)& )const(3)
{
int temp;
retrun temp;
}
参数的 const属性(上例2处)一般用引用传递,是为了保证该参数在函数中不允许被修改,一旦修改,编译器会报错。

而返回值的const属性(上例1处)是保证函数的返回值不被修改,也许你会质疑这种可能性,但是这种可能性确实存在,
详细情形如下:(摘自effective c++)
const rational operator*(const rational& lhs,
const rational& rhs);

很多程序员第一眼看到它会纳闷:为什么operator*的返回结果是一个const对象?因为如果不是这样,用户就可以做下面这样的坏事:

rational a, b, c;

...

(a * b) = c; // 对a*b的结果赋值

我不知道为什么有些程序员会想到对两个数的运算结果直接赋值,但我却知道:如果a,b和c是固定类型,这样做显然是不合法的。一个好的用户自定义类型的特 征是,它会避免那种没道理的与固定类型不兼容的行为。对我来说,对两个数的运算结果赋值是非常没道理的。声明operator*的返回值为const可以 防止这种情况,所以这样做才是正确的。

呵呵,象Scott Meyers这样的大师见地就是不一般吧

接下来说明函数的const属性:(上例3处)
当然喽,一般用于成员函数了,它有以下属性:
(1)const对象的访问只能靠const成员函数
(2)const成员函数不被允许修改它所在对象的任何一个数据成员。

(三)尽量使用 const代替define 吧,因为const是类型安全的。
应该使用
const double pi = 3.1415926;
而不要用#define pi 3.1415926
后者是宏,仅仅是对程序中的pi用3.1415926代替,会让你对于一些编译时的错误很难定位。

[GLib学习]gtype.h文件(未完成)

gtype.h文件

在GLib里,所有的基本类型都被封装成g*的形式,如char封装成gchar,如下是一部分这样的代码:
typedef char gchar;
typedef short gshort;
typedef long glong;
typedef int gint;
typedef gint gboolean;

typedef unsigned char guchar;
typedef unsigned short gushort;
typedef unsigned long gulong;
typedef unsigned int guint;

typedef float gfloat;
typedef double gdouble;
值得注意的是它还把void *和const void *封装成gpointer和gconstpointer,大大方便了编程。
typedef void* gpointer;
typedef const void *gconstpointer;

封装这些类型后,还提供了这些类型的比较的函数指针:
typedef gint (*GCompareFunc) (gconstpointer a,
gconstpointer b);
typedef gint (*GCompareDataFunc) (gconstpointer a,
gconstpointer b,
gpointer user_data);
typedef gboolean (*GEqualFunc) (gconstpointer a,
gconstpointer b);
以下的函数指针未理解功能
typedef void (*GDestroyNotify) (gpointer data);
typedef void (*GFunc) (gpointer data,
gpointer user_data);
typedef guint (*GHashFunc) (gconstpointer key);
typedef void (*GHFunc) (gpointer key,
gpointer value,
gpointer user_data);

GFreeFunc函数指针看上去是用于释放内存的,Glib提供类似的函数g_malloc和g_free来分配和释放数据结构的内存。
typedef void (*GFreeFunc) (gpointer data);
GTranslateFunc函数指针用于将指针转换为字符串(暂时不确定,纯属猜测)
typedef const gchar * (*GTranslateFunc) (const gchar *str,
gpointer data);

[GLib学习]索引贴

***此贴记录关于我学习GLib过程,为了督促自己学习开源代码,写博客是个不错的方法。此贴就作为一个索引贴,会慢慢完善***

GLib库简介:
GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库,它提供C语言的常用的数据结构的定义、相关的处理函 数,有趣而实用的宏,可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。它能够在类UNIX的操作系统平台(如 LINUX, HP-UNIX等),WINDOWS,OS2和BeOS等操作系统台上运行。
IBM教程网上有浅显的入门:http://www.ibm.com/developerworks/cn/linux/l-glib/index.html

索引:

gtype.h学习:http://swinging-breeze.blogspot.com/2009/04/glibgtypeh.html
garray.h/garray.c学习:

2009年4月16日星期四

[转]Linux 漫画杂志两种

略作简要说明,一并介绍给喜欢 Linux,同时也喜欢漫画的朋友。

Ubunchu!

Ubunchu

Ubunchu! 是一本日本漫画杂志,其中讲述在学校电脑俱乐部中的三个同学使用 Ubuntu 的故事。Ubunchu! 的作者为 Hiroshi Seo,目前已由 ubuntu-jp LoCo 的两名会员翻译成英文,你可以获取它的 PDF 版本。

Hackett and Bankwell

Hackett and Bankwell

很有教育性的 Linux 漫画,主角为 Woody Hackett,一只看上去有点猥琐的企鹅,引导你如何进入 GNU/Linux 世界。目前已发行了两集,同样有 PDF 版本可以下载。

以下那张漫画比较有意思:大意是男的装完ubuntu之后想玩他的XE游戏,说本来不行,但是装了Wine之后就可以了!

2009年4月15日星期三

GNOME 2.26 Release Notes

Figure 1GNOME 2.26





GTK+ 2.16变化最明显:

GTK+ 2.16 is the latest release of the GTK+ toolkit, which is at the heart of GNOME. GTK+ 2.16 includes a couple of new features for developers, as well as extensive bug fixing and housecleaning for the upcoming GTK+ 3.0.

GtkEntry widgets can now display icons at the front or back of the entry widget (depending on your locale's text direction). These icons are optionally prelightable and clickable.

Icons in GtkEntry(框内现在可以放入Icon了)

GtkEntry widgets can now also be used to display a progress bar.

GtkEntry with a progress bar(框支持了以下进度条的效果

A new interface, GtkActivatable, has been added for widgets that can be connected to a GtkAction.




2009年4月13日星期一

推荐四月新番:《初恋限定》(原作:河水下希)

看过《草莓百分百》的人都知道河水下希,可以说是恋爱漫画的始祖了。同样是河水下希的作品:《初恋限定》最近也被动画化了。绝对是可以让人追的十分优秀的恋爱、搞笑动画。

网上的信息:

河下水希自草莓100%完結以來,再次於《週刊少年Jump》上連載的作品。這部漫畫是以各種各樣女孩的初戀故事為背景而展開的小故事。

2008年5月已完結,全部32話。

初中女生

全體人員是開始連載當時雪之下中學2年級學生,其後在作品中升級。

有原亞由美有原 あゆみ(ありはら あゆみ) 聲優:伊瀨茉莉也
喜歡衛。衛的哥哥操向她表白,但是她沒回答。是美少女,但雙腿力量非常大。也許是頭腦太不好,經常弄錯漢字。而且她也不擅長料理。
別所小宵別所 小宵(べっしょ こよい) 聲優:豐崎愛生
最喜歡哥哥的少女。視岬為情敵。但是想得的山本疼愛。很會做料理,但不反省家庭經濟的豪華的用餐,每次都受到哥哥的斥責。
千倉名央千倉 名央(ちくら なお) 聲優:藤村步
溫和、有同情心、誠實的少女。是曾我部喜歡的對象。但是,當她遇見美術部的畢業生連城由紀人時,就開始被他吸引。在單行本第4冊後提及原設定為有錢人,但因編輯反對而放棄設定。
土橋理香土橋 りか(どばし りか) 聲優:壽美菜子
擁有褐色的皮膚,萬能運動神經的少女。所屬網球部。寺井和正陪著。是寺井喜歡的對象,但是那個是本人沒戀愛的習慣。
江之本慧江ノ本 慧(えのもと けい) 聲優:伊藤靜
被年銷售額十三億日元的男子求婚過的美少女。平常捆紮頭髮。所屬手工藝部。但是對經常吵架的楠田產生感情。
不動宮不動宮 すみれ(ふどうのみや すみれ)
戲劇部副部長。是在時髦眼鏡上掛短項鍊、超長頭髮等獨特的流行感覺的人。

初中男生

全體人員是開始連載當時雪之下中學2年級學生,其後在作品中升級。

楠田楠田(くすだ) 聲優:淺沼晉太郎
容貌如小學生,好色的男子。喜歡經常與他吵架的江之本。
財津衛財津 衛(ざいつ まもる) 聲優:入野自由
美少年的性格。有成為急救隊員的夢。受有原亞由美表白,但是本人喜歡岬。
曾我部弘之曽我部 弘之(そがべ ひろゆき) 聲優:櫻井孝宏
妄想、很容易誤會男子。喜歡同班的千倉。對自己有絕對性的自信,然而由於連城由紀人的出現,喪失自信。
寺井春人寺井 春人(てらい はると) 聲優:岡本信彥
所屬網球部。戴眼鏡,自從喜歡土橋後,對網球更努力。

高中女生

除了久間菜之花是作品中插班的1年級學生外,其他人員開始連載當時是水仙寺高中1年,其後在作品中升級至2年。

山本岬山本 岬(やまもと みさき) 聲優:田中理惠
看一眼都會被吸引的美人,甚至作為情敵的小宵都被吸引。從小宵的哥哥良彥、操守的弟弟衛等也喜歡她,但是意想不到她本人對有原產生感情。
渡瀨惠琉渡瀬 めぐる(わたせ めぐる) 聲優:白石涼子
擔任游泳部的經理。經常為自己的巨乳而自卑,但是想法從和某男子的見面後改變。喜歡武居源五郎。
江之本夕江ノ本 夕(えのもと ゆう) 聲優:佐藤聰美
慧的姐姐。和慧截然相反。有幼稚的姿容和性格,姊妹關係正逆轉。廣泛的興趣令她有很多男性朋友。
久間菜之花久間 菜の花(きゅうま なのか)
自認渡瀨惠琉對對手後,追蹤她然後向水仙寺高中入學。外觀如小學生,但實力很高。

[編輯] 高中男生

除了武居和連城,其他人員開始連載當時是1年,其後在作品中升級至2年。(武居升到3年,連城畢業)除了財津、連城以外,其他人員是水仙寺高中的學生。

財津操財津 操(ざいつ みさお) 聲優:乃村健次
財津衛的哥哥,喜歡有原亞由美。與弟弟的性格、體型完全不同,一般人看不出是兩兄弟,亞由美一開始也不知操是衛的哥哥。與山本岬是青梅竹馬
有原有二有原 有二(ありはら ゆうじ) 聲優:吉野裕行
有原亞由美的哥哥,是一名妹控。被山本岬喜歡。
別所良彥別所 良彦(べっしょ よしひこ) 聲優:日野聰
別所小宵的哥哥,喜歡山本岬,最後因妹妹小宵而失戀。每次妹妹做豪華的用餐,都會斥責妹妹不反省家庭經濟。
千倉一真千倉 一真(ちくら かずま)
千倉名央的哥哥,是一名俊男。與有原、別所等朋友相同班級。在單行本第4冊後交代主劇情已經完結,但其實只於少部份時間出現。
上村上村(うえむら)
志願是漫畫家的少年。即使自覺有稍微自我意識過剩。喜歡江之本夕。
武居源五郎武居 源五郎(たけい げんごろう)
充滿豪俠氣概、熱愛游泳、喜歡泳衣的笨蛋。渡瀨惠琉的初戀(單戀)的對象。喜歡渡瀨惠琉的巨乳。
連城由紀人連城 由紀人(れんじょう ゆきと)
名校開帝高中的3年級生。雪之下中學美術部的OB,是畫了令千倉名央憧憬圖畫的人。藉著畢業後春假重返美術部,但這只是秘密地承擔苦惱的路線。最後出國幫助貧窮國家之人民,同時與不同國家之孩童一起繪畫。