很好的 C语言入门教程(中文)

豆瓣上备受好评的 《Linux C 编程一站式学习》 网上几大书店都无货了,淘宝有,但是贵,然后竟然在 Github 上找到它的在线版本 http://akaedu.github.io/book/,真心要点赞啊。

linux-c-on-github

这个书还有个新版本,叫《一站式学习 C 编程》,也是无货的。具体内容我没有比较过,但是基础知识应该是差不多的。新版书名中把 Linux 去掉了,也是可以理解的,毕竟 C 不局限于 Linux,特别是现在多种平台,还有逐步升温的各式嵌入式设备,在可以 预见的未来 C 语言还将继续扛大旗

program-language-trends

这样看趋势还不明显,不过相信很快大家都会感受到的了。

Javascript hashCode 函数

网上找了好一轮,找到个比较像样而且足够短的 hashCode 实现,是从 Java 的 hashCode 中借鉴而得的。原理见 Java hashCode() ,也可以跟这里的 Java String 的源码 参照对比一下。

为了使用的方便,稍稍再改良了一下

function hashcode(str) {
  var hash = 0, i, chr, len;
  if (str.length === 0) return hash;
  for (i = 0, len = str.length; i < len; i++) {
    chr   = str.charCodeAt(i);
    hash  = ((hash << 5) - hash) + chr;
    hash |= 0; // Convert to 32bit integer
  }
  return hash;
}

hashcode("this is a string")
//-1853110172

这里接受的参数是一个 String,其它类型怎么办?可以先做一个统一的处理,比如

hashcode(JSON.stringify(obj))

序列化之后再使用 hashCode 函数,基本所有类型数据都通吃,除了含有循环嵌套的对象。

PS:
函数实现中有一行使用了 “|” 运算符,只是利用 Bitwise 运算符转换参数为 32bit,用来确保结果是个 32位整数。

如何生成唯一且不可预测的 ID

通常数据库可以生成唯一的 ID,最多的就是数字序列,也有像 MongoDB 这样产生组合序列的,不过这种形式的 ID 由于是序列,是可以预测的。如果想得到不可预测且唯一的 ID,方法还是有的。

下面主要以 Node.js 的环境为例。

Node-uuid

Github 上有个 node-uuid 项目,它可以快速地生成符合 RFC4122 规范 version 1 或者 version 4 的 UUID。

安装,既可以通过 npm install node-uuid ,也可以在 package.json 中定义。使用方式:

var uuid = require('node-uuid');

// 产生一个 v1 (基于时间的) id
uuid.v1(); // -> '6c84fb90-12c4-11e1-840d-7b25c5ee775a'

// 产生一个 v4 (随机) id
uuid.v4(); // -> '110ec58a-a0f2-4ac4-8393-c866d813b8d1’

听起来很有保证,只是……有点太长了。

坊间也有一些在 UUID 基础上随机截取几位的办法来组成一个新的短一点的字符串,只不过唯一性就又重新成为一个问号了。

Hashids

另外一个方法,来自 hashids,它的原理就是从数字经过一个加盐(salted)算法产生一个哈希(hash)字符串。这样算法就是通过混淆使结果具有不可预测性,而唯一性依然由数字本身来达成,从而得到(类似 youtube 里的)足够短,不可预测且唯一的 ID。

安装方式类似,只是 hashids 建议使用一个固定的版本。比如在 package.json 中

"dependencies": {
  "hashids": "0.3.3"
}

使用方法也很简单。先设定一个字符串作为 salt。

var Hashids = require("hashids"),
    hashids = new Hashids("this is my salt”);

然后对数字(准确来说是数字数组)进行编码

var hash = hashids.encrypt(12345);
// Nkk9

var hash = hashids.encrypt(683, 94108, 123, 5);
// aBMswoO2UB3Sj

对数组编码可以有很有趣的用法,比如需要在一个分布式的环境里使用,可以使用机器编号 + 序列数字组合数组然后再编码。

有编码就有解码

var numbers = hashids.decrypt("NkK9”);
// [12345]

编码的输出会随着数字的增大而变长,为了达到足够混淆,它可以指定最少长度。

hashids = new Hashids("this is my salt", 8);

var hash = hashids.encrypt(1);
// gB0NV05e

还有其它选项,比如控制输出所使用的字符,编码 MongoDB 的 ObjectId 等等。

上面介绍的两个项目,各有各优缺点,不过目的是一致的,可以根据实际情况选用。两个项目在其它语言上也有类似的实现。

如何在命令行中使用 proxy

国内的网络形势你懂的,要翻墙,无法是 VPN 或者 proxy。我个人还是用 proxy 比较多,浏览器里装个插件就能自动适应。但是那只是针对浏览器,命令行也很常用,遇墙就会卡着不动了。

有一个软件可以帮助你在 Command Line 里使用 proxy,叫 ProxyChains-NG (new generation)

在 Mac 上安装超简单(只要你机器上装好了 brew

brew install proxychains-ng

==> Downloading https://downloads.sourceforge.net/project/proxychains-ng/proxychains-4.7.tar.bz2
 ######################################################################## 100.0%
==> ./configure --prefix=/usr/local/Cellar/proxychains-ng/4.7 --sysconfdir=/usr/local/Cellar/proxychains-ng/4.7/etc
==> make
==> make install
==> make install-config
==> /usr/local/Cellar/proxychains-ng/4.7: 9 files, 92K, built in 10 seconds

其它平台的安装大同小异,先找找有没有一个命令能搞掂的,没有的话参考一下其文档的 Installation 部分。

然后,做一个简单配置,打开配置文件

vi /usr/local/etc/proxychains.conf

视乎你的安装方式不同,配置文件的地方略有不同,但会遵从平台的一般规范,例如在 Linux 上安装,配置文件的位置可能在 /etc/proxychains.conf

打开配置文件之后,略过前面所有,直奔最后一行,默认配置是使用 tor 的,根据你机器上 proxy 的种类配置好就可以了。常见的配置

http    127.0.0.1  8080
socks5  127.0.0.1  1080

其实上面几行就有 example ,找到合适的照抄就是了。

最后,使用也非常简单,只要在命令前面加个 proxychains4,比如

proxychains4 telnet targethost.com

PS. 还有个名字接近但更加老牌的同类软件, ProxyChains ,使用方法几乎一样。

Javascript void(0)

JavaScript-logo常有人疑惑一个看上去很简单的问题,<a href=”javascript:void(0)”>some button</a> 里面的 void(0) 是什么意思?又或者不明白为什么好像很多人要这么写而不是其它更加简洁的写法,比如 <a href=”#”>some button</a>

href=”javascript:void(0)” 实际上是为了在 link 被点击的时候,返回一个空值,告诉浏览器什么都不用做。因为浏览器会把当前页面里的内容替换为返回的结果。(这个效果反而让我感到奇怪,因为真的很少这么用)

而 href=”#” 看上去简洁,但是它其实有个默认的行为,会把页面重新定位到页面顶部,这就是为什么页面会跳一下的一个原因。因为更多的情况,点击后页面保持在原位才是我们想要的结果。使用 # 甚至可能出现一些更加奇葩的问题

除了这个,还有没有其它更加简洁的写法呢?会不会是 <a href=””>some button</a> 呢?这个点击后会刷新当前页面,显然不符合要求。应该是 <a href=”javascript:;”>some button</a> ,什么都不执行。但有时过分简洁也容易造成“不清晰”的印象从而引起不必要争拗。如果从代码可读性考虑,也为了团队内沟通少点分歧,我会建议使用 href=”javascript:void(0)”。

void 操作符最原始就是用来“制造” undefined ,特别是某段代码只需要其副作用(side effect)又不希望有返回值产生。void 本身没有副作用,也没有正作用(返回值是空),但却能包含一切,犹如黑洞一般神秘。

== 参考 ==
Void Operator in MDN

在苏宁买东西

在亲身经历过之后,才能感受到苏宁的购物体验是有多么的支离破碎。

作为一名普通消费者,贪便宜才是理性的体现,团购就很对胃口,所以就团购了一把。某大件电器,限购 3台。好吧,我下个单要两台。一切都很顺利,除了必须得绑定一下手机!我最讨厌绑定手机了,不过为了便宜,绑了一个。支付的时候选择了在线支付,如果没有什么特别理由或者提醒说这里最好别这么做的话,一般都会是在线支付。

问题开始来了,网银有金额限制。要跟超过70家银行合作快捷支付,可能真的没有那么多精力来为网银交易限额做个提示吧。好吧,我能理解给个提示真的工作量超大,那么先看看有没有什么办法可以改支付方式。没有!-_-! 退而求其次,先取消这个单,然后下个新单改为到门店支付。

然后,下不了单!打电话给客服,答复是,即使取消了订单,资格还是占着的,建议我用其它账号重新下单。这个逻辑本身就很有问题。谁设计的系统,取消了订单还占着资格?不知道是系统的 bug 还是设计的时候就没考虑周全。

苏宁限购的bug

新建一个用户,找了另外一个手机号绑定(连续两次干最讨厌的事,忍耐力是不是很好?),下单,支付方式为门店支付。

===== 华丽的分割线 – 场景瞬间切换至苏宁门店 =====

收银台的工作人员说没找到我的单号!?
苏宁订单错误

再次致电客服,说了一堆类似于我能明白你的意思的话,确认了团购的是不能在门店支付的,最后给出的解决办法是找门店导购员重新下单,改为货到付款。

团购的不能在门店支付?毫无提示。害我白跑一趟了。从这个点上可以隐约感觉到,苏宁其实不是一个整体,里面山头林立。

导购员不知道是没听明白我想要的方案还是怎么回事,一阵忙碌,跑来跑去,说是门店里做不了那个价格,要打报告,让我等。30分钟过去了,导购员回来说方案有了,可以做,就是步骤有点复杂。

先要搞张门店的会员卡,以较高的价格先买一台,然后得到返卷,买第二台的时候用返卷抵扣,最终价钱一样。操作起来也很复杂很费时间,来来回回打单,额外搞张会员卡,付款也要付两次。

中间我故意客气一点,表现得情绪稳定,但无意中导购员一句,“不麻烦,我也有提成的”,很真情流露的一句话,让我深深的感觉到 “服务是苏宁唯一的产品,顾客满意是苏宁服务的终极目标” 全是狗屁。

我相信绝对是有可以让顾客满意的办法,不过前提是得站在顾客的角度去思考。

整个过程有多处细节都可以避免最终的悲剧,包括:在选择网上支付的时候,给出足够清晰的提示网银限额,是否有其它方式来突破这个限额;在选择门店支付的时候,给出足够的提示,团购是不能在门店支付的,或者允许团购在门店支付;允许客户修改订单的支付方式;订单取消之后,对于有限购的项目归还占用的资格;门店线下的下单操作应该兼容线上的操作方式,不用那么迂回曲折的办法来实现和网上价格同步。

线上账户对于用户来说是个重要的入口,那么轻易地建议顾客再开一个账户或者使用他人账户的行为是不可取的。引导顾客使用正常流程,确保正常流程的可用性是评价一个系统成熟度的重要考量。

门店导购员的服务也显得很生硬,让客人等待超过半小时,商品不会推介,一杯水也没有。还有让人好奇的是比较热衷于劝顾客开会员卡,对其他顾客声称能做得比网上更低价。

这个 Frozen 不太冷

Frozen 海报
传说这是一部本来打算在 2000年上演的动画片,回炉了13年之后,再来个破茧而出。还好它选择的是一个很经典的主题,突破束缚,突破自己。经典的主题就该配经典的效果,那些冰雪魔法、那座屹立峰巅的冰晶宫殿,还穿插一些经典的梗,比如那根胡萝卜。

有些人可能期待王子或者哪个卖冰的来英雄救美一下,结果被震住了。男主角都只不过是陪伴一下,不显得那么寂寞空虚冷而已。

因为女王内心的不安,造成全城冰雪覆盖,以为自己迁一下都,躲起来就没事,但只要还要有人牵挂,想落得个耳根清净是不可能的。有些事情是早面对的好,方针一错,会错过好多时间,而且纠正起来特别麻烦,比如需要冰镇一下妹妹。

看到有很多讨论主题曲 《Let It Go》,不过我倒没有太多印象,可能因为整个电影里面都很迪士尼地在很多环节开唱,虽然比直接说对白要带感很多,不过也会冲淡了主题曲的感觉。对比起埃及王子的主题曲,这个就略为逊色。

总体来说,片子还是老少咸宜,值得重播的。

茂名螃(P)蟹(X)事件

please
事件的始末可以参考百科:3·30茂名反芳烃项目游行示威,其实这个更详细,就是需要翻下墙。

表达一下诉求是应该的,但是暴力就不应该了。用政府的话来说就是“群众们太不了解PX项目”,“操之过急的宣传”,事实是这样么?

在被引导的舆论当中,问题都被转移到 PX 是剧毒还是低毒刑事事件,对网络流传的消息进行辟谣上去了,官方的“正言”又开始继续,但是就是不谈论这个事情本身,仿佛是毫无意义的闹剧。

“如果不发生厦门这些事情,茂名PX项目的建设肯定不是现在这种情况。”茂名市环保局法规宣教科科长陈炎辉接受华夏时报记者采访时表示。不知道他是指如果没有厦门PX事件,就不会有茂名这次事件,还是指如果没有厦门PX事件,会导致更严重的冲突?对事件本身的理解的不同是可以得出完全不同的结论的。

如果说这次事件是由于操之过急的宣传引发的,那么怎样的速度才算是合适的呢?特别是其实在2008年8月,茂名已经表示要争取 PX 项目,花了接近 6 年时间终于进入到科普阶段,然后在一场推广会之后,就硬生生出来一个抗议事件?

冰冻三尺非一日之寒。

政府的反应迟缓,面对抗议演变成暴力冲突,然后警察与抗议民众发生“擦碰”(这个词一般是用于汽车事故的吧,反正我是想像不出来到底怎么回事),几天后在茂名新闻网发布了《全体市民书》(现在已经 404,不仅仅是这个,大量的有关消息都被删除了,掩耳盗铃也只能骗骗领导而已,难道他们不知道可以 百度 或者 Google)。

缺乏公信力,即使是再密集再强大的PX宣传攻势,也只是黄婆卖瓜。签订什么承诺书更加是没画蛇添足,是不了解群众心理,管理思维严重落后的表现。群众的眼睛是雪亮的,但是群众的想法却不一定是科学的,理性的。妄想群众能科学地理性地去了解PX和判断项目是否合适,这本身就是不科学的。

昨天 4月 8日微软宣布 13岁 XP 正式退休,而我国可能是 XP 保有量最大的国家。我经常会碰到的事情是电脑是近几年买的,装的 XP,还有更奇葩的会因为各种原因卸载了更为先进的操作系统改装 XP。硬件大家都能很快的跟进换代了,唯独是软件依然停滞不前,电脑用个老系统也许大多数时候也就是个个人问题,但如果整个社会的“软件”也停滞不前,恐怕就影响深远了。

Zeco CX6 投影仪刷固件

我一直以为刷固件离我很远,昨晚 CX6 开机之后就一直停在了开机画面,重启数遍无效,找到客服,说要刷固件,当时哪个场合太囧了!第一次拿到别人家里放,N 双眼睛等着看新玩意,结果告诉我要!刷!固!件!

好吧,那就现场刷一下吧,又遇到一件囧事,CX6 背后的 micro USB 插口深不见底啊,普通 5mm 长的嘴插进去就掉了,好吧,那就用手按着吧,根据说明一步一步,结果还是不停显示出错了,出错了…… 打电话问技术支持,当时是晚上 10:10,得到的消息是插头不够长,建议我削掉一点胶。

明显是又一个很不合理的设计,我找了 N 根 micro USB 的线,全部都是最近一两年的机型配的原装数据线,嘴都是 5mm 的,CX6 的要至少 6mm 长的才行,问题是都知道自己的特殊一点,就配一条 6mm 的嘛。虽然技术支持说可以邮一根过来,但是时间就太长了。

好,扯远了。准备好工具之后,就开始动手削!可能要牺牲一根华为的线了。

削线需要的工具

先把头部的胶削掉 0.5mm ,试了一下,还真不行,再削 0.5mm,都见到里面的“骨头”了,已经不能削更多了。上天保佑终于可以了。

露出 6mm

刷固件就难在,要先拿个牙签桶住 RESET 按钮不妨再开机,如果你习惯用右手的话,最好用左手去桶,不解释了。然后就没有什么难度,打开软件,选择一下固件来源,跟着都自动完成。

左手桶 RESET 键

烧录完成

至于为什么 CX6 会变砖,跟马航事件一样目前没有定论,不过最大的嫌疑是 App 更新造成的。App 自己都提示更新是修复已知问题,架构上是应该不会影响到系统无法启动的,关于这一点,得到的建议是不要点 App 自己的更新,呃…… 有骗小白的嫌疑。

这不说软件在稳定性上完全跟不上了,说说外观设计。同样是插头,HDMI / VGA 的就比较浅,至少外面就能看到,OTG 接口就不能再伸出来一点点么?同样的 TF-CARD 的,卡已经够小了,手指粗的人能塞卡进去,但是如果想取出来,你得有长指甲,注意是要长指甲,刚剪过的会够不着。底部的进风口距离固定点太近,三脚架的快装板多少会遮住一点。我认为,接口少一点其实更加好,可以更加专注提升软件质量,做接口其实还要考虑接口做工的可用性,至少用来急救刷固件的接口要确保场合适应性。