`
wangshaofei
  • 浏览: 272607 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

酷壳文章转载

阅读更多

 

酷壳 - CoolShell.cn

You are not subscribed to this feed yet.

If you'd like to automatically receive updates to this feed, you can subscribe now.

 

via 酷壳 - CoolShell.cn by 陈皓 on 9/20/11

这是个StackOverflow上的问题 How can you program if you’re blind? 。在看到这个问题的时候,我感到应该不可能,但是我错了,这个问题的前两个答案让我深深地震憾了。

第一个答案的回复人是Jared(其在StackOverflow上的积分有将近14K),但是你能想得到他是一个盲人吗?他回复到——

我是一个完全失明的大学学生,我做过一些程序员的实习工作,所以我的回复基于我的这些经历。我使用Windows XP 和  Jaws 来为了读出屏幕上的内容。

对于Java 编程,我使用eclipse这个强大的IDE。我使用SWT开发GUI。对于.NET编程,其使用Visual Studio 2005,使用Jaws可以非常容易地操作VS2005,而且其还有一些很不错的脚本来可容易地用来做表单设计。

对于C/C++,我使用cygwin + gcc 也使用emacs 和 vim 做出编辑器(使用Emacspeak虽然有时候有点迟钝)。在实习过程中,我做了很多和Z/OS相关的编程工作。我使用rlogin通过cygwin登录大型机的USS系统,并使用C3270作为其3270仿真器来访问大型机的ISPF部分。

我依赖于合成语音系统,也需要 Braille display, 我发现使用合成语音系统很快,但是使用 Braille display有时候有些问题。比如程序有太多的嵌套括号。

关于Braille display,又叫盲文显示机,是能以盲文进行输出的电子机械式设备。一般来说,该设备通过在平坦表面上打孔来实现点阵的表现。有了该设备的帮助,无法使用一般的显示设备的失明用户也能够阅读文字。如下所示。

 

A woman using a braille display with a QWERTY keyboard, attached to a laptop computer.

第二个答案是Saqib提供的,其个人主页是http://www.saqibshaikh.com/,目前在Microsoft的Bing项目组,他回答到:

我是盲人,我对Windows, Mac, Linux 和 DOS有13年的编程经验了,我会的编程语言是C/C++, Python, Java, C#或是其它相似的语言,虽然问题问的是怎么来设置盲人的环境,但是我想从盲人怎么使用电脑来回答。

有些人使用“语音环境”,如T. V. Raman程序员和Emacspeak 环境。这样的环境需要有读屏程序来监控操作系统的行为,并通过合成语音系统或是Braille display 来告诉盲人屏幕上有什么。这样一样,盲人就可以操作任何的应用程序了。

我个人这段时候使用Visual Studio 2008(注:作者是09年回的这个贴的),用其来做一些修改。我关闭了一些VS2008的功能,如显示错误,因为这会让我分心。在加入微软以前,我都是在用notepad这样的东西开发程序。

对于读屏软件,我需要设置一下,以便其告诉我缩进。老实说我不太关心这个事,因为VS2008对程序缩进做得很好。但是对于Python来说,这个功能相当重要。最终,Emacspeak 可以使用不同的声音来让我区分缩进的语句块,以及一些语法(关键词,注释,标识,等等。)

对于Saqib,大家有兴趣可以看看他的视频访谈:Saqib Shaikh and Scott Hanselman: Designing for Accessibility

这个问题中多次提到了Google的盲人程序员 T.V. Raman,我在网上搜了一下他,他前段时间来过北京,新京报在今年早期报道过他——《T.V 拉蒙,互联网界也有“盲剑客” ——Google盲人工程师讲述软件设计之路

在这篇报道中,他经历过IBM, Adobe和Google 这三个公司,他可以在23秒内复原盲人魔方,1989年他就得到一台给盲人用的语音合成器和当时最先进的读屏软件。他现在使用电脑 没有任何障碍,他天天都上网浏览信息,他还可以使用特别的手机来看地图。

不知道你看完这些人的经历后,你有什么感觉?

  • 你是否会觉得技术的力量和社会的尊重让他们和正常人一样可以使用电脑?
  • 你是否会觉得我们这些正常人是不是平时抱怨的太多了呢?还有什么理由不努力的呢?
(全文完)

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 9/19/11

下面是近期收录的一些文章和资源,希望对你有用。

系统方面

High Scalability

Mozilla's Gecko rendering engine main flow

Mozilla's Gecko rendering engine main flow

各种教程


  • SQL注入口袋书Google Doc 需fanqiang)涵盖MySQL, MSSQL和Oracle,我觉得可以用来做你的程序的安全测试。
  • 一个超有意思的学习Javascript的在线课件了。下面的这个网页上有一个Web的命令行,你可以跟着他的提示去输入一些命令,并以此来学习Javascript,这个创意真是太好了,我觉得这应该推广到我们的学校中去,不是只听老师讲,还需要大家一起来动作。http://www.codecademy.com/

Web库

django-google-charts

  • 一个新的HTML5+CSS3的JS库Kendo UIhttp://demos.kendoui.com/ 这样的JS库有很多,如比较经典的ExtJS, YUI 和 jQuery。不过大家可以试试这个库。其支持移动设备。

AeroViewr

HTML 5

 编程规范

其它

  • 声讨PHP的一个slids http://zakx.de/phprant-en.pdf, 前面说到的两个网站都是使用PHP做到,不过,你可以通过这个PDF了解一下PHP有哪些地方不好。

—— 更新 2011.9.20  21:00 ——

@xzhaoyang 在留言中问我有没有C写CGI的文章,我看过最好的一篇是下面这篇:

http://www.tutorialspoint.com/cplusplus/cpp_web_programming.htm (注意fanqiang)

(全文完)

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 9/15/11

前几天酷壳发布过“vim简明攻略”,不知道大家练得怎么样了。如果你练了一下,那么这里这个速查卡就会对你有帮助了。以前本站也有过一个(vim速查卡),不过其太简单了。我觉得这个很不错,很全,很直观。这个速查卡来自这里。其用颜色标注了级别:

  •   Green   = 存活级
  •   Yellow   = 感觉良好
  •   Orange   / Blue = 高级
  •   Red   = 专家级

下面的图片点击可以看大图:

给程序员的VIM速查卡

给程序员的VIM速查卡(点击看大图)

你还可以下载PDF版的和Excel版的,如果你是色盲的话,还有蓝色版PDF的。如果你不是很喜欢的话,这里还有几个:

 

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 9/8/11

我们有很多Coding Style 或 代码规范。但这一条可能会经常被我们所遗忘,就是我们经常会在函数的参数里使用bool参数,这会大大地降低代码的可读性。不信?我们先来看看下面的代码。

当你读到下面的代码,你会觉得这个代码是什么意思?

widget->repaint(false);

是不要repaint吗?还是别的什么意思?看了文档后,我们才知道这个参数是immediate, 也就是说,false代表不立即重画,true代码立即重画。

Windows API中也有这样一个函数:InvalidateRect,当你看到下面的代码,你会觉得是什么意思?

InvalidateRect(hwnd, lpRect,  false);

我们先不说InvalidateRect这个函数名取得有多糟糕,我们先说一下那个false参数?invalidate意为“让XXX无效”,false是什么意思?双重否定?是肯定的意思?如果你看到这样的代码,你会相当的费解的。于是,你要去看一下文档,或是InvalidateRect的函数定义,你会看到那个参数是 BOOL bErase,意思是,是否要重画背景。

这样的事情有很多,再看下面的代码,想把str中的”%USER%”替换成真实的用户名:

str.replace("%USER%", user, false);   // Qt 3

TNND,那个false是什么意思?不替换吗?还是别的什么意思,看了文档才知道,false代码大小写不敏感的替换。

其实,如果你使用枚举变量/常量,而不是bool变量,你会让你的代码更易读,如:

 

widget->repaint(PAINT::immediate);
widget->repaint(PAINT::deffer);

InvalidateRect(hwnd, lpRect,  !RepantBackground);

str.replace("%USER%", user, Qt::CaseInsensitive); // Qt 4

如果对这个事不以为然的话,我们再来看一些别的示例,你不妨猜猜看看下面的代码:

component.setCentered(true, false);

这什么玩意儿啊?看了文档你才知道,这原来是 setCentered(centered, autoUpdate);

new Textbox(300, 100, false, true);

这又是什么啊?看了文档才知道,这是创建一个文本框,第三个参数是是否要滚动条,第四个是是否要自动换行。TNND。

上面的情况还不算最差,看看下面的双重否定。

component.setDisabled(false);
filter.setCaseInsensitive(false)

再来一个,如果你读到下面的代码,相信你会和我一样,要么石化了,要么凌乱了。

event.initKeyEvent("keypress", true, true, null, null,
                    false, false, false, false, 9, 0); 

看完这篇文章,我希望你再也不要把bool为作为函数参数了。除非两个原因:

  1. 你100%确认不会带来阅读上的问题,比如Java的 setVisible (bool).
  2. 你100%确认你想去写出无法维护很难阅读的代码

【更新2011/9/8】当然,别的参数也会有一样的问题,比如:new Textbox(300, 100, false, true);中的300 和 100,不知道是坐标还是长宽,只不过,一般长度或坐标这样的参数都不会被hard code,都会有变量名,而bool这种参数经常性地被传成true 和 false。 bool参数表现得更为明显一些罢了。

所以,程序中不要出现magic number,true/false 也是一种 magic number。但是,我想告诉大家,从API设计的角度来说,你无法强制调用者用常量来取代true/false,定义成枚举类型是最好的选择

最后,如果你想设计一个好的API,强烈推荐你读一下Nokia的Qt的《API Design Principles》,本文就是其中的“Boolean Trap”。

(全文完)

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 9/6/11

vim的学习曲线相当的大(参看各种文本编辑器的学习曲线),所以,如果你一开始看到的是一大堆VIM的命令分类,你一定会对这个编辑器失去兴趣的。下面的文章翻译自《Learn Vim Progressively》,我觉得这是给新手最好的VIM的升级教程了,没有列举所有的命令,只是列举了那些最有用的命令。非常不错。

——————————正文开始——————————

你想以最快的速度学习人类史上最好的文本编辑器VIM吗?你先得懂得如何在VIM幸存下来,然后一点一点地学习各种戏法。

Vim the Six Billion Dollar editor

Better, Stronger, Faster.

学习 vim 并且其会成为你最后一个使用的文本编辑器。没有比这个更好的文本编辑器了,非常地难学,但是却不可思议地好用。

我建议下面这四个步骤:

  1. 存活
  2. 感觉良好
  3. 觉得更好,更强,更快
  4. 使用VIM的超能力

当你走完这篇文章,你会成为一个vim的 superstar。

在开始学习以前,我需要给你一些警告:

  • 学习vim在开始时是痛苦的。
  • 需要时间
  • 需要不断地练习,就像你学习一个乐器一样。
  • 不要期望你能在3天内把vim练得比别的编辑器更有效率。
  • 事实上,你需要2周时间的苦练,而不是3天。

第一级 – 存活

  1. 安装 vim
  2. 启动 vim
  3. 什么也别干!请先阅读

当你安装好一个编辑器后,你一定会想在其中输入点什么东西,然后看看这个编辑器是什么样子。但vim不是这样的,请按照下面的命令操作:

  • 启 动Vim后,vim在 Normal 模式下。
  • 让我们进入 Insert 模式,请按下键 i 。(陈皓注:你会看到vim左下角有一个–insert–字样,表示,你可以以插入的方式输入了)
  • 此时,你可以输入文本了,就像你用“记事本”一样。
  • 如果你想返回 Normal 模式,请按 ESC 键。

现在,你知道如何在 Insert 和 Normal 模式下切换了。下面是一些命令,可以让你在 Normal 模式下幸存下来:

  • i → Insert 模式,按 ESC 回到 Normal 模式.
  • x → 删当前光标所在的一个字符。
  • :wq → 存盘 + 退出 (:w 存盘, :q 退出)   (陈皓注::w 后可以跟文件名)
  • dd → 删除当前行,并把删除的行存到剪贴板里
  • p → 粘贴剪贴板

推荐:

  • hjkl (强例推荐使用其移动光标,但不必需) →你也可以使用光标键 (←↓↑→). 注: j 就像下箭头。
  • :help <command> → 显示相关命令的帮助。你也可以就输入 :help 而不跟命令。(陈皓注:退出帮助需要输入:q)

你能在vim幸存下来只需要上述的那5个命令,你就可以编辑文本了,你一定要把这些命令练成一种下意识的状态。于是你就可以开始进阶到第二级了。

当是,在你进入第二级时,需要再说一下 Normal 模式。在一般的编辑器下,当你需要copy一段文字的时候,你需要使用 Ctrl 键,比如:Ctrl-C。也就是说,Ctrl键就好像功能键一样,当你按下了功能键Ctrl后,C就不在是C了,而且就是一个命令或是一个快键键了,在VIM的Normal模式下,所有的键就是功能键了。这个你需要知道。

标记:

  • 下面的文字中,如果是 Ctrl-λ我会写成 <C-λ>.
  • 以 : 开始的命令你需要输入 <enter>回车,例如 — 如果我写成 :q 也就是说你要输入 :q<enter>.

第二级 – 感觉良好

上面的那些命令只能让你存活下来,现在是时候学习一些更多的命令了,下面是我的建议:(陈皓注:所有的命令都需要在Normal模式下使用,如果你不知道现在在什么样的模式,你就狂按几次ESC键)

  1. 各种插入模式
    • a → 在光标后插入
    • o → 在当前行后插入一个新行
    • O → 在当前行前插入一个新行
    • cw → 替换光标所在位置的一个单词
  2. 简单的移动光标
    • 0 → 数字零,到行头
    • ^ → 到本行第一个不是blank字符的位置(所谓blank字符就是空格,tab,换行,回车等)
    • $ → 到本行行尾
    • g_ → 到本行最后一个不是blank字符的位置。
    • /pattern → 搜索 pattern 的字符串(陈皓注:如果搜索出多个匹配,可按n键到下一个)
  3. 拷贝/粘贴 (陈皓注:p/P都可以,p是表示在当前位置之后,P表示在当前位置之前)
    • P → 粘贴
    • yy → 拷贝当前行当行于 ddP
  4. Undo/Redo
    • u → undo
    • <C-r> → redo
  5. 打开/保存/退出/改变文件(Buffer)
    • :e <path/to/file> → 打开一个文件
    • :w → 存盘
    • :saveas <path/to/file> → 另存为 <path/to/file>
    • :x, ZZ 或 :wq → 保存并退出 (:x 表示仅在需要时保存,ZZ不需要输入冒号并回车)
    • :q! → 退出不保存 :qa! 强行退出所有的正在编辑的文件,就算别的文件有更改。
    • :bn 和 :bp → 你可以同时打开很多文件,使用这两个命令来切换下一个或上一个文件。(陈皓注:我喜欢使用:n到下一个文件)

花点时间熟悉一下上面的命令,一旦你掌握他们了,你就几乎可以干其它编辑器都能干的事了。但是到现在为止,你还是觉得使用vim还是有点笨拙,不过没关系,你可以进阶到第三级了。

第三级 – 更好,更强,更快

先恭喜你!你干的很不错。我们可以开始一些更为有趣的事了。在第三级,我们只谈那些和vi可以兼容的命令。

更好

下面,让我们看一下vim是怎么重复自己的:

  1. . → (小数点) 可以重复上一次的命令
  2. N<command> → 重复某个命令N次

下面是一个示例,找开一个文件你可以试试下面的命令:

  • 2dd → 删除2行
  • 3p → 粘贴文本3次
  • 100idesu [ESC] → 会写下 “desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu desu “
  • . → 重复上一个命令—— 100 “desu “.
  • 3. → 重复 3 次 “desu” (注意:不是 300,你看,VIM多聪明啊).
更强

你要让你的光标移动更有效率,你一定要了解下面的这些命令,千万别跳过

  1. NG → 到第 N 行 (陈皓注:注意命令中的G是大写的,另我一般使用 : N 到第N行,如 :137 到第137行)
  2. gg → 到第一行。(陈皓注:相当于1G,或 :1)
  3. G → 到最后一行。
  4. 按单词移动:
    1. w → 到下一个单词的开头。
    2. e → 到下一个单词的结尾。

    > 如果你认为单词是由默认方式,那么就用小写的e和w。默认上来说,一个单词由字母,数字和下划线组成(陈皓注:程序变量)

    > 如果你认为单词是由blank字符分隔符,那么你需要使用大写的E和W。(陈皓注:程序语句)

    Word moves example

下面,让我来说说最强的光标移动:

  • % : 匹配括号移动,包括 ({[. (陈皓注:你需要把光标先移到括号上)
  • * 和 #:  匹配光标当前所在的单词,移动光标到下一个(或上一个)匹配单词(*是下一个,#是上一个)

相信我,上面这三个命令对程序员来说是相当强大的。

更快

你一定要记住光标的移动,因为很多命令都可以和这些移动光标的命令连动。很多命令都可以如下来干:

<start position><command><end position>

例如 0y$ 命令意味着:

  • 0 → 先到行头
  • y → 从这里开始拷贝
  • $ → 拷贝到本行最后一个字符

你可可以输入 ye,从当前位置拷贝到本单词的最后一个字符。

你也可以输入 y2/foo 来拷贝2个 “foo” 之间的字符串。

还有很多时间并不一定你就一定要按y才会拷贝,下面的命令也会被拷贝:

  • d (删除 )
  • v (可视化的选择)
  • gU (变大写)
  • gu (变小写)
  • 等等
(陈皓注:可视化选择是一个很有意思的命令,你可以先按v,然后移动光标,你就会看到文本被选择,然后,你可能d,也可y,也可以变大写等)

第四级 – Vim 超能力

你只需要掌握前面的命令,你就可以很舒服的使用VIM了。但是,现在,我们向你介绍的是VIM杀手级的功能。下面这些功能是我只用vim的原因。

在当前行上移动光标: 0 ^ $ f F t T , ;
  • 0 → 到行头
  • ^ → 到本行的第一个非blank字符
  • $ → 到行尾
  • g_ → 到本行最后一个不是blank字符的位置。
  • fa → 到下一个为a的字符处,你也可以fs到下一个为s的字符。
  • t, → 到逗号前的第一个字符。逗号可以变成其它字符。
  • 3fa → 在当前行查找第三个出现的a。
  • F 和 T → 和 f 和 t 一样,只不过是相反方向。
    Line moves

还有一个很有用的命令是 dt" → 删除所有的内容,直到遇到双引号—— "。

区域选择 <action>a<object> 或 <action>i<object>

在visual 模式下,这些命令很强大,其命令格式为

<action>a<object> 和 <action>i<object>

  • action可以是任何的命令,如 d (删除), y (拷贝), v (可以视模式选择)。
  • object 可能是: w 一个单词, W 一个以空格为分隔的单词, s 一个句字, p 一个段落。也可以是一个特别的字符:"、 '、 )、 }、 ]。

假设你有一个字符串 (map (+) ("foo")).而光标键在第一个 的位置。

  • vi" → 会选择 foo.
  • va" → 会选择 "foo".
  • vi) → 会选择 "foo".
  • va) → 会选择("foo").
  • v2i) → 会选择 map (+) ("foo")
  • v2a) → 会选择 (map (+) ("foo"))

Text objects selection

块操作: <C-v>

块操作,典型的操作: 0 <C-v> <C-d> I-- [ESC]

  • ^ → 到行头
  • <C-v> → 开始块操作
  • <C-d> → 向下移动 (你也可以使用hjkl来移动光标,或是使用%,或是别的)
  • I-- [ESC] → I是插入,插入“--”,按ESC键来为每一行生效。

Rectangular blocks

在Windows下的vim,你需要使用 <C-q> 而不是 <C-v> ,<C-v> 是拷贝剪贴板。

自动提示: <C-n> 和 <C-p>

在 Insert 模式下,你可以输入一个词的开头,然后按 <C-p>或是<C-n>,自动补齐功能就出现了……

Completion

宏录制: qa 操作序列 q@a@@
  • qa 把你的操作记录在寄存器 a。
  • 于是 @a 会replay被录制的宏。
  • @@ 是一个快捷键用来replay最新录制的宏。

示例

在一个只有一行且这一行只有“1”的文本中,键入如下命令:

  • qaYp<C-a>q
    • qa 开始录制
    • Yp 复制行.
    • <C-a> 增加1.
    • q 停止录制.
  • @a → 在1下面写下 2
  • @@ → 在2 正面写下3
  • 现在做 100@@ 会创建新的100行,并把数据增加到 103.

Macros

可视化选择: v,V,<C-v>

前面,我们看到了 <C-v>的示例 (在Windows下应该是<C-q>),我们可以使用 v 和 V。一但被选好了,你可以做下面的事:

  • J → 把所有的行连接起来(变成一行)
  • < 或 > → 左右缩进
  • = → 自动给缩进 (陈皓注:这个功能相当强大,我太喜欢了)

Autoindent

在所有被选择的行后加上点东西:

  • <C-v>
  • 选中相关的行 (可使用 j 或 <C-d> 或是 /pattern 或是 % 等……)
  • $ 到行最后
  • A, 输入字符串,按 ESC。

Append to many lines

分屏: :split 和 vsplit.

下面是主要的命令,你可以使用VIM的帮助 :help split. 你可以参考本站以前的一篇文章VIM分屏

  • :split → 创建分屏 (:vsplit创建垂直分屏)
  • <C-w><dir> : dir就是方向,可以是 hjkl 或是 ←↓↑→ 中的一个,其用来切换分屏。
  • <C-w>_ (或 <C-w>|) : 最大化尺寸 (<C-w>| 垂直分屏)
  • <C-w>+ (或 <C-w>-) : 增加尺寸

Split

结束语

  • 上面是作者最常用的90%的命令。
  • 我建议你每天都学1到2个新的命令。
  • 在两到三周后,你会感到vim的强大的。
  • 有时候,学习VIM就像是在死背一些东西。
  • 幸运的是,vim有很多很不错的工具和优秀的文档。
  • 运行vimtutor直到你熟悉了那些基本命令。
  • 其在线帮助文档中你应该要仔细阅读的是 :help usr_02.txt.
  • 你会学习到诸如  !, 目录,寄存器,插件等很多其它的功能。

学习vim就像学弹钢琴一样,一旦学会,受益无穷。

——————————正文结束——————————

对于vi/vim只是点评一点:这是一个你不需要使用鼠标,不需使用小键盘,只需要使用大键盘就可以完成很多复杂功能文本编辑的编辑器。不然,Visual Studio也不就会有vim的插件了

(全文完)

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 8/25/11

本文来自“The most stupid C bug ever”,很有意思,分享给大家。我相信这样的bug,就算你是高手你也会犯的。你来看看作者犯的这个Bug吧。。

首先,作者想用一段程序来创建一个文件,如果有文件名的话,就创建真正的文件,如果没有的话,就调用?tmpfile()?创建临时文件。他这段程序就是HTTP下载的C程序。code==200就是HTTP的返回码。

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    g = fname ? fopen(fname, "w+") : tmpfile();
}

但是这个程序,只能在Unix/Linux下工作,因为 Microsoft 的?tmpfile()的实现?居然选择了 C:\ 作为临时文件的存放目录,这对于那些没有管理员权限的人来说就出大问题了,在Windows 7下,就算你有管理员权限也会有问题。所以,上面的程序在Windows平台下需要用不同的方式来处理,不能直接使用Windows的tmpfile()函数。

于是作者就先把这个问题记下来,在注释中写下了FIXME:

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */

    // FIXME Win32 native version fails here because
    //   Microsoft's version of tmpfile() creates the file in C:\
    g = fname ? fopen(fname, "w+") : tmpfile();
}

然后,作者觉得需要写一个跨平台的编译:

FILE * tmpfile ( void ) {
#ifndef _WIN32
    return tmpfile();
#else
    //code for Windows;
#endif
}

然后,作者觉得这样实现很不好,会发现名字冲突,因为这样一来这个函数太难看了。于是他重构了一下他的代码——写一个自己实现的tmpfile() – w32_tmpfile,然后,在Windows 下用宏定义来重命名这个函数为tmpfile()。(陈皓注:这种用法是比较标准的跨平台代码的写法)

 

#ifdef _WIN32
  #define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    //code for Windows;
}

搞定!编译程序,运行。靠!居然没有调用到我的w32_tmpfile(),什么问题?调试,单步跟踪,果然没有调用到!难道是问号表达式有问题?改成if – else 语句,好了!

if(NULL != fname) {
    g = fopen(fname, "w+");
} else {
    g = tmpfile();
}

问号表达式不应该有问题吧,难道我们的宏对问号表达式不起作用,这难道是编译器的预编译的一个bug?作者怀疑到。

现在我们把所有的代码连在一起看,并比较一下:

能正常工作的代码

#ifdef _WIN32
#  define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    code for Windows;
}

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    // FIXME Win32 native version fails here because
    //     Microsoft's version of tmpfile() creates the file in C:\
    //g = fname ? fopen(fname, "w+") : tmpfile();
    if(NULL != fname) {
        g = fopen(fname, "w+");
    } else {
        g = tmpfile();
    }
}

不能正常工作的代码

#ifdef _WIN32
#  define tmpfile w32_tmpfile
#endif

FILE * w32_tmpfile ( void ) {
    code for Windows;
}

else if (code == 200) {     // Downloading whole file
    /* Write new file (plus allow reading once we finish) */
    // FIXME Win32 native version fails here because
    //    Microsoft's version of tmpfile() creates the file in C:\
    g = fname ? fopen(fname, "w+") : tmpfile();
}

也许你在一开始就看到了这个bug,但是作者没有。所有的问题都出在注释上:

/* Write new file (plus allow reading once we finish) */
// FIXME Win32 native version fails here because
//     Microsoft's version of tmpfile() creates the file in C:\

你看到了最后那个C:\吗?在C中,“\” 代表此行没有结束,于是,后面的代码也成了注释。这就是这个bug的真正原因

而之所以改成if-else能工作的原因是因为作者注释了老的问号表达式的代码,所以,那段能工作的代码成了:

/* Write new file (plus allow reading once we finish) */
// FIXME Win32 native version fails here because Microsoft's version of tmpfile() creates the file in C:    //g = fname ? fopen(fname, "w+") : tmpfile();
if(NULL != fname) {
    g = fopen(fname, "w+");
} else {
    g = tmpfile();
}

我相信,当作者找到这个问题的原因后,一定会骂一句“妈的”!我也相信,这个bug花费了作者很多时间!

最后,我也share一个我以前犯的一个错。

我有一个小函数,需要传入一个int* pInt的类型,然后我需要在我的代码里 把这个int* pInt作除数。于是我的代码成了下面的这个样子:

float result = num/*pInt;
….

/*  some comments */

-x<10 ? f(result):f(-result);

因为我在我当时用vi编写代码,所以没有语法高亮,而我的程序都编译通过了,但是却出现了很奇怪的事。我也不知道,用gdb调式的时候,发现有些语句直接就过了。这个问题让我花了很多时间,最后发现问题原来是没有空格导致的,TNND,下面我用代码高亮的插件来显示上面的代码,

float result = num/*pInt;
....

/*  some comments */

-x<10 ? f(result):f(-result); 

Holly Shit!  我的代码成了:

float result = num-x<10 ? f(result):f(-result);

妈的!我的这个错误在愚蠢程度上和上面那个作者出的错误有一拼。

(全文完)

相关文章

via 酷壳 - CoolShell.cn by 陈皓 on 8/24/11

Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关系到用户安全的功能,希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。以下内容,转载时请保持原文一致,并请注明作者和出处

用户名和口令

首先,我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。如何管理自己的口令让你知道怎么管理自己的口令,破解你的口令让你知道在现代这样速度的计算速度下,用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则:

  • 限制用户输入一些非常容易被破解的口令。如什么qwert,123456, password之类,就像twitter限制用户的口令一样做一个口令的黑名单。另外,你可以限制用户口令的长度,是否有大小写,是否有数字,你可以用你的程序做一下校验。当然,这可能会让用户感到很不爽,所以,现在很多网站都提供了UX让用户知道他的口令强度是什么样的(比如这个有趣的UX),这样可以让用户有一个选择,目的就是告诉用户——要想安全,先把口令设得好一点。
  • 千万不要明文保存用户的口令。正如如何管理自己的口令所说的一样,很多时候,用户都会用相同的ID相同的口令来登录很多网站。所以,如果你的网站明文保存的话,那么,如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以,用户的口令一定要加密保存,最好是用不可逆的加密,如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。(另,对于国内公司的品行以及有关部门的管理方式,我不敢保证国内网站以加密的方式保存你的口令。我觉得,做为一个有良知的人,我们应该加密保存用户的口令)
  • 是否让浏览器保存口令。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码,浏览器只是其中一种。是否让浏览器保存这个需要你做决定,重点是看一下你的系统的安全级别是否要求比较高,如果是的话,则不要让浏览器保存密码,并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。
  • 口令在网上的传输。因为HTTP是明文协议,所以,用户名和口令在网上也是明文发送的,这个很不安全。你可以看看这篇文章你就明白了。要做到加密传输就必需使用HTTPS协议。但是,在中国还是有很多网站的Web登录方式还在使用ActiveX控件,这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过,我依然觉ActiveX控件不应该存在,因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。

用户登录状态

首先,我想告诉大家的是,因为HTTP是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的无关联的,一笔是一笔。而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。

所以,我们每个页面都需要对用户的身份进行认证。当然,我们不可能让用户在每个页面上输入用户名和口令,这会让用户觉得我们的网站相当的SB。为了实现这一功能,用得最多的技术就是浏览器的cookie,我们会把用户登录的信息存放在客户端的cookie里,这样,我们每个页面都从这个cookie里获得用户是否登录的信息,从而达到记录状态,验证用户的目的。但是,你真的会用cookie吗?下面是使用cookie的一些原则。

  • 千万不要在cookie中存放用户的密码。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以,你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。
  • 正确设计“记住密码”。这个功能简直就是一个安全隐患,我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能,系统会生成一个cookie,cookie包括用户名和一个固定的散列值,这个固定的散列值一直使用。这样,你就可以在所有的设备和客户上都可以登录,而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考:
    (——更新 2011/08/26,原文中有些小错误,并且说的不清楚,重新调整了一下——

1)在cookie中,保存三个东西——用户名登录序列登录token

用户名:明文存放。
登录序列:一个被MD5散列过的随机数,仅当强制用户输入口令时更新(如:用户修改了口令)
登录token:一个被MD5散列过的随机数,仅一个登录session内有效,新的登录session会更新它

2)上述三个东西会存在服务器上,服务器的验证用户需要验证客户端cookie里的这三个事。

3)这样的设计会有什么样的效果,会有下面的效果,

a)登录token是单实例登录。意思就是一个用户只能有一个登录实例。

b)登录序列是用来做盗用行为检测的。如果用户的cookie被盗后,盗用者使用这个cookie访问网站时,我们的系统是以为是合法用户,然后更新“登录token”,而真正的用户回来访问时,系统发现只有“用户名”和“登录序列”相同,但是“登录token” 不对,这样的话,系统就知道,这个用户可能出现了被盗用的情况,于是,系统可以清除并更改登录序列  登录token,这样就可以令所有的cookie失效,并要求用户输入口令。并给警告用户系统安全。

4)当然,上述这样的设计还是会有一些问题,比如:同一用户的不同设备登录,甚至在同一个设备上使用不同的浏览器保登录。一个设备会让另一个设备的登录token登录序列失效,从而让其它设备和浏览器需要重新登录,并会造成cookie被盗用的假象。所以,你在服务器服还需要考虑- IP 地址

a) 如果以口令方式登录,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为口令只有真正的用户知道。

b) 如果 IP相同 ,那么,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为是同一用户有同一IP(当然,同一个局域网里也有同一IP,但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能)。

c) 如果 (IP不同 && 没有用口令登录),那么,“登录token” 就会在多个IP间发生变化(登录token在两个或多个ip间被来来回回的变换),当在一定时间内达到一定次数后,系统才会真正觉得被盗用的可能性很高,此时系统在后台清除“登录序列”和“登录token“,让Cookie失效,强制用户输入口令(或是要求用户更改口令),以保证多台设备上的cookie一致。

  • 不要让cookie有权限访问所有的操作。否则就是XSS攻击,这个功能请参看新浪微博的XSS攻击。下面的这些功能一定要用户输入口令:
1)修改口令。
2)修改电子邮件。(电子邮件通过用来找回用户密码)
3)用户的隐私信息。
4)用户消费功能。
  • 权衡Cookie的过期时间。如果是永不过期,会有很不错的用户体验,但是这也会让用户很快就忘了登录密码。如果设置上过期期限,比如2周,一个月,那么可能会好一点,但是2周和一个月后,用户依然会忘了密码。尤其是用户在一些公共电脑上,如果保存了永久cookie的话,等于泄露了帐号。所以,对于cookie的过期时间我们还需要权衡。

找回口令的功能

找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计,下面我逐个点评一下。

  • 千万不要使用安全问答。事实证明,这个环节很烦人,而且用户并不能很好的设置安全问答。什么,我的生日啊,我母亲的生日,等等。因为今天的互联网和以前不一样了,因为SNS,今天的互联比以前更真实了,我可以上facebook,开心,人人网,LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook,Facebook的安全问答很强大,还要你通过照片认人,呵呵。
  • 不要重置用户的密码。因为这有可能让用户的密码遭到恶意攻击。当然,你要发个邮件给用户让其确认,用户点击邮件中的一个链接,你再重置。我并不推荐这样的方法,因为用户一般都会用笔记下来这个很难记的口令,然后登录系统,因为登录系统时使用了“记住密码”的功能,所以导致用户不会去修改密码,从而要么导到被写下来的密码被人盗取,要么又忘记了密码。
  • 好一点的做法——通过邮件自行重置。当用户申请找回口令功能的时候,系统生成一个MD5唯一的随机字串(可通过UID+IP+timestamp+随机数),放在数据库中,然后设置上时限(比如1小时内),给用户发一个邮件,这个连接中包含那个MD5的字串的链接,用户通过点击那个链接来自己重新设置新的口令。
  • 更好一点的做法——多重认证。比如:通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握,因为手机要能会丢了,而我的手机可以访问我的邮箱。所以,使用U盾,SecureID(一个会变化的6位数token),或是通过人工的方式核实用户身份。当然,这主要看你的系统的安全级别了。

口令探测防守

  • 使用验证码。验证码是后台随机产生的一个短暂的验证码,这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明,这是最简单也最有效的方式。当然,总是让用户输入那些肉眼都看不清的验证码的用户体验不好,所以,可以折中一下。比如Google,当他发现一个IP地址发出大量的搜索后,其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后,他需要给你发短信方式或是电话方式的验证码。
  • 用户口令失败次数。调置口令失败的上限,如果失败过多,则把帐号锁了,需要用户以找回口令的方式来重新激活帐号。但是,这个功能可能会被恶意人使用。最好的方法是,增加其尝试的时间成本(以前的这篇文章说过一个增加时间成本的解密算法)。如,两次口令尝试的间隔是5秒钟。三次以上错误,帐号被临时锁上30秒,5次以上帐号被锁1分钟,10次以上错误帐号被锁4小时……
  • 系统全局防守。上述的防守只针对某一个别用户。恶意者们深知这一点,所以,他们一般会动用“僵尸网络”轮着尝试一堆用户的口令,所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然,这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统,平均每天有5000次的口令错误的事件,那么你可以认为,当口令错误大幅超过这个数后,而且时间相对集中,就说明有黑客攻击。这个时候你怎么办?一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。
最后,再说一下,关于用户登录,使用第三方的 OAuth 和 OpenID 也不失为一个很不错的选择。

参考文章

以上内容,转载时请保持原文一致,并请注明作者和出处

相关文章

via 酷壳 - CoolShell.cn by sumtec on 8/22/11

感谢网友sumtec投递此文,很欢乐也有意思,与大家共勉

首先说明:

1、以下特征是真实遇到过的,同事犯过的,乃至我自己也犯过的;
2、为了剧情需要,某些例子进行了一些夸张修饰等演绎创作,如无雷同,请勿生气;
3、如果你出现过以下症状之一,并不代表你就是弱爆了,但是如果你一直出现,乃至一说到这个大家就能联想到你,那么你就得小心了;
4、如果你是集这几个的大乘者,恭喜你,你已经找到了离开这个行业的充足理由了。

好了,搞定!

“那个Bug解决了吗?”

“好了,搞定!”

“这么快?”

正当你非常欣喜的时候,就传来了噩耗:刚才还能编译成功的,就失败了。(好吧,我们的集成编译尚未成功配置上,理论上这种事情应该会被退回。)又或者能编译成功,但是呢,原来明明能起作用的一个下拉框,突然发神经的不起作用了。最隐蔽的莫过于,一切正常,但是当你看到代码的时候,你就晕厥过去了。比如我们曾经发现了一个Bug,简单说就是每次用户点击某个东西,就会执行下面的这段C#代码:

controlPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;

这个Bug很明显会导致速度越来越慢,因为同一个更新操作会被更新N次,并且这个N会越来越大。其实这个Bug已经够弱了,但是后来居然被修改为:

 

controlPropertyPanel.PropertyChanged -= this.UpdatePropertyOnChanged;
controlPropertyPanel.PropertyChanged += this.UpdatePropertyOnChanged;

这段代码能编译,能执行,但是就是弱爆了。因为这不仅仅没有从根本上去掉造成问题的逻辑,还会带来更多的困惑:为什么要先减后加呢?

这类特征,请大家看看有趣的《各种流行的编程风格》,我这个例子算是一种撞大运。我觉这吧,这类问题都是因为只想解决一些表面的东西为目的,完全不管底下的其它任何问题而造成的。

那估计是他的Bug

“这个问题为啥还没解决呢?”

“我觉得应该是他那里边的Bug,我调不了。”

“哦……”

这个“他”可以是某一位同事,或者前同事,或者微软,或者别的什么公司,再或者某个开源代码的作者。这些个我都遇到过,比如说是另一位现在在职的同事吧。当你告诉这位同事这个Bug似乎在他那儿,并且问问什么时候解决,他也许会很愧疚的立刻调试,可最后结果却仍然是开头对话主人翁的所写代码的问题。

再比如说是微软吧,那么对话可能就会包括:“啊,SilverLight真是烂,老是内存泄漏、崩溃等……”“是啊是啊!烂死了!早知道用Flash了。”又或者会说:“微软就是烂,Java就是好。”其实,我不想比较什么SilverLight还是Flash,.NET还是Java。因为在讨论这些问题之前,先最好想想,这真的是别人的错么?相信是其他人的错是一件很简单的事情,因为这样推脱之后你就可以啥都不做了,反正不是我的错。

如果真的发现了这是别人的Bug并证明了,那倒好说。但这种特征是一种纯粹的怀疑,并没有丝毫的证明。在仔细找了自己所有可能犯的错之后,如果你怀疑是别人的问题,那请求证一下。

无图无真相!

“楼主,无图无真相啊!”

“楼主,无代码无真相啊!”

“楼主,给翻译一下啊!”

据说Linus在别人询问Linux内存管理的一个什么问题时,回答道“Read the fxxxing source code”,很多时候我也有类似的冲动。我发现在信息发达的时代,不少人的阅读能力、动手能力都严重退化了。这些人最好就是你亲自来帮他把问题解决了,他才不想了解里面到底 发生了什么。这种问题体现在博客里面,就是寄希望于你写得图文并茂,图嘛最好花里胡哨同时言简而意概,文字嘛最好大段大段的代码。其实图不是重要的,只是为了好看,重点是代码,这样他一Copy就可以直接解决他们的问题了。

比方说,Silverlight里面没有各种图像格式的编码器,于是当你希望保存Jpg的时候怎么办呢?Google一下,发现原来有人写过一个FluxJpeg的编码器。下载下来一跑,唉还真能用哎。之后就直接签入,也不捎带看一下有没有什么问题,或者设计不合理的地方。(其实真的有,会很慢,因为有大量毫无必要的数组拷贝。)

又或者说,遇到了某个Bug,搜索一下发现,哎,还真有人遇到过,而且还有代码哎!把代码扒下来一跑,发现好像解决了,至于为什么就不管了。甚至还遇到过根本就不管解决不解决问题,反正代码扒下来了就签入了的。

再比如,写一篇博客讲解如何缩减.NET编译出来的文体大小,其中提到许多概念需要先阅读微软官方的一个文档。结果,还是会有人回复说,你那个文章里面提到那么多的Blob,也不说说Blob里面都有什么,大概是很不满意吧。可是这个文档里面都有啊,难道就不能自己阅读一下?其实即便我连这个文档都没有给出,自己也应该有这个能力去进行思考,去动手寻找。

千万不要退化成一个啥都要别人给你嚼烂了才能够吞下去,吞下去也不会消化吸收的人。这样的人大概别人给的是大便,只要有代码无真相,也会照样吃下去的。若真如此,那你打算如何提高呢?

那是个对象!

“这个ExpressionVisitor,它是用来干什么的?”

“……”

“好吧,或者这么说,他是一个什么东西?”

“他是一个对象!”

“啊?”

“哦,是一个对象的实例。”

大概这样的回答,和那个微软工程师说“你在直升飞机上”差不多——反正你也不能说是错的,但是就是没什么意义。其实不知道没啥问题,人又不是神,怎么可能都知道呢?不去仔细了解和学习问题也不严重,因为你可以改。但是当你习惯性的随便找一个绝对没错但又不说明任何问题的答案,甚至似是而非的东西来对付的时候,你就离弱爆的边缘很近了。

当然,上面的对话也许是比较极端的。一个稍弱一点的对话版本是:

“这个内存泄漏是怎么造成的呢?”

“嗯,会不会是图片放的位置不对呢?”

哈,还是很夸张对吧?没办法,写博客有时候需要夸张的文字,否则你无法理解我的意思是:有时候,大家会倾向于从自己的记忆中寻找一些相似的物品,然后选择相似度自认为比较高的东西出来当作答案,而全然不管两者之间的逻辑是否有哪怕那么一丝的关联。也许很多时候,我们确实需要从相似的东西开始,但请别把他当作终点。程序是需要严谨的逻辑的,所以你也必须非常严谨的去推演。

关于这类的问题真的太多太多了,比如我指着下面这段代码当中的红字:

var dictionary = new Dictionary<string, string>();
dictionary["someKey"] = “someValue”;

“这句话说明了什么?”

“说明dictionary是一个数组。”

集大成者

最后我举一个集大成者的例子,说,有个任务是要在SilverLight应用上面添加一个“收藏本站点”。好,怎么解决呢?网上一搜,发现有很多这样的代码:

function AddBookmark(Url, LabeName) {
  if (document.all)
  {
    window.external.addFavorite(Url, LabeName);
  }
  else if (window.sidebar)
  {
    window.sidebar.addPanel(LabeName, Url, '');
  }
}

然后直接扒下来就放上去了,通过某种方式在SilverLight中调用这段JavaScript,签入,搞定了!结果到了测试那边发现完全不能用,无论在IE6/7/8/9/10,还是在FireFox/Safari/Chrome上面,都不能使用。我问:

“这是什么原因呢?”

“不知道,反正浏览器报告没有权限,可能是浏览器的安全设置原因吧,或者操作系统的Bug,也可能是浏览器的某种Bug?”

“不可能啊?这些代码存在很多年了,要有问题早就能在网上搜索到了。”

“那也许是SilverLight调用的时候有什么安全问题。哎!SilverLight好烦啊!”

“那怎么还没有解决呢?”

“好,我马上解决它!”

很快,那段Javascript就变成了:

function AddBookmark(Url, LabeName) {
  try
  {
    if (document.all)
    {
      window.external.addFavorite(Url, LabeName);
    }
    else if (window.sidebar)
    {
      window.sidebar.addPanel(LabeName, Url, '');
    }
  }
  catch
  {
    alert("您的浏览器因为安全设置的问题无法收藏,请手动添加收藏!");
  }
}

看到这样的代码,我彻底震惊了。亲自调试了一下,发现确实报告了一个“没有权限”的异常。但是,我还发现,那个Url参数的值是“www.adomainname.com\test\page.html”。那这不废话么!浏览器认为你要收藏的是一个本地硬盘上的路径,怎么可能在一个Internet Zone上允许收藏这种路径呢?我于是指着代码问:

“这个Url是什么?”

“是一个变量”

“啊?”

“哦,不对,是一个参数。”

你是否也有类似的经历呢?

(全文完)

相关文章

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics