Integer promotion in C

虽然使用 C 语言已经很长时间了,但对于一些 C 语言的细节一直没有特别的注意,这次就遇到了在表达式中整型精度提升的问题。

问题描述

问题的发现是在一个 C51 的单片机程序中。相关的代码如下:

P4 = ~P4 >> 4;

本来很简单的一个语句,但在 sdcc 下面编译运行后却得到了令我意外的结果。我马上又使用 Keil C 进行了测试,结果就符合我的预期。那么它们的区别到底是什么呢?假如 P4 == 0x0F,那么对于 sdcc 结果是 0xFF,对于 Keil C 的结果是 0x0F,而后者是我想要的。通过对比二者生成的汇编代码,发现 sdcc 的汇编代码很长(20行),而 Keil C 的代码只有 4 行(预料之中),我根本没有仔细分析一下 sdcc 的代码就直接到它们的网站上去提交了一份 bug report。事实证明我有些太草率了。

问题分析

通过这篇文章的标题就可以知道问题的本质是什么,如果我仔细看一下 sdcc 生成的汇编代码,或者哪怕到 sdcc 的论坛上浏览一下:上个月就有个家伙问过类似的问题(虽然是从另外的方面),并得到了很好的解释。很快我的邮箱里面也收到了 Maarten Brock 的回复:

I see nothing wrong here. C integer promotion rules require the P4 value to be upcast to an int and then have its bits inverted. And after that the resulting int is shifted down. Didn't you get a warning about the ~ operator having strange effects on operands smaller than int?

To get what you probably expected, you should either cast the ~P4 value back to unsigned char or assign it to an intermediate unsigned char variable and shift that down in a second operation.

可惜当时我仍然没有立刻领会了这段话的含义,又经过了几次实验才最终搞清楚。在进行 ~P4 » 4 这个运算的时候,由于第一个取反之后还要进行其它的运算,所以必须在运算前提高 P4 的精度,对于前面的实例,P4 的值将在运算前变成 int 类型,在 C51 的环境中就是 0x000F,然后取反再右移就得到了 0xFFFF(包含符号位扩展),重新赋值给 P4 才使得它最终成为 0xFF。而这种行为是符合 C 语言的标准的,也就是说 Keil C 的行为不符合标准。

没过几天,我正好翻看 Linden 的 Expert C Programming,结果也看到了对 integer promotion 的分析,看来我的 C 语言知识还需要再补充一些。BTW,Linden 的这本书真的是非常的精彩,信息量很大,而且讲述也很有趣,非常推荐大家阅读。

Hello Hyde

第一次尝试使用比较流行的静态网页生成工具,记录一下基本操作过程和感受。

安装

安装 hyde 还是比较容易的,首先安装 pip,好像大部分 python 用户都喜欢用这个安装工具:

# apt-get install python-pip

之后安装 hyde 就比较简单了,直接 $ pip install hyde 就可以,而且也会自动解决依赖关系。

配置

建立一个新的项目可以直接用下面的命令:

$ hyde -s myhyde create

这会使用缺省的 layout 布局页面,myhyde 就是项目的目录,里面的内容就已经是一个完整的网站源码了,之后只要在 myhyde 目录执行 $ hyde serve 就可以在 localhost:8080 上看到网站的效果了。

对网站的配置实际上就是对这个示例代码的阅读、理解和修改的过程。逐步修改配置文件 site.yaml,页面布局脚本 layout/*.j2,和最终 content 目录中的 html 文件,以使网站代码符合我的应用。我把整个过程都通过 git 版本控制系统记录了下来,成为另外一个学习笔记。

页面布局脚本是 Jinja 模板,它的网站上有详细的文档可以参考。Hyde的主页上也包含了很多有价值的配置文档。有关 Markdown 的语法结构,可以参考这个

问题

目前虽然网站已经可以上线,但还是有一些问题没有解决:

  1. css 还要继续学习,一些样式还需要调整。

  2. 代码列表和行内代码没有很好的区分,好像还需要额外的过滤器。 (Edit: 增加 Markdown 的 codehilite 扩展,就可以比较好的区分代码列表和行内代码了。)

SSH reverse tunnel

访问 NAT 内部的服务器

A 处的计算机都在防火墙的后面,IP 地址是如 10.88.32.x 的非法 IP,我在 B 处就无法访问它们。为了可以远程登录 A 处的主机,就要使用 SSH 的反向隧道功能。

首先是在A处的计算机上执行

$ ssh -N -R 10002:localhost:22 [email protected]

只要保证这条命令一直执行,就可以在 B 处的电脑上打开 10002 这样一个反向隧道,之后只要在 B 处使用

$ ssh my_a_user@localhost -p 10002

就可以连接 A 处的电脑上了。其中 my_a_user 是 A 处电脑的用户名。而我主机上的 myuser 用户还可以没有合法的登录终端,这样也很安全,万一 B 处电脑重启什么的,还可以让别人帮我运行这个命令,不用担心告诉他们密码了。

如果希望从别的主机也可以连接 B 电脑的对应端口,还需要修改 sshd 的配置选项:GatewayPorts 为 yes。