C语言的美与丑
C语言的设计非常如同公理体系般简洁,而所能够演绎出来的内容又足够完备。这是对C的美的一种简单概括。
我要说的“公理体系”中的最核心的组件,便是指针。有智者说到,计算机科学中的一切问题,都可以通过增加一层间接(indirection)来解决1 。指针可以用来解决什么问题呢?
继续阅读 →C语言的设计非常如同公理体系般简洁,而所能够演绎出来的内容又足够完备。这是对C的美的一种简单概括。
我要说的“公理体系”中的最核心的组件,便是指针。有智者说到,计算机科学中的一切问题,都可以通过增加一层间接(indirection)来解决1 。指针可以用来解决什么问题呢?
继续阅读 →基本上只有到了一年一度的高考日子才有兴趣研究那么几道数学题。我已脱离高考三年,能力也是退化了许多。户枢不蠹,流水不腐。重温几道数学题,仿佛可以将已锈蚀的脑子润滑起来。
问题:已知锐角\(\triangle ABC\)满足\(\sin A = 2\sin B \sin C\)。则\(\tan A \tan B \tan C\)的最小值为______。
解答:因为 \[ \tan A \tan B \tan C = \frac{\sin A \sin B \sin C}{\cos A \cos B \cos C}, \] 代入 \[\left\lbrace\begin{aligned} \sin A &= 2\sin B\sin C, \\ \cos A &= -\cos(B + C) = \sin B \sin C - \cos B \cos C \end{aligned}\right.\] 得
继续阅读 →Vala是一个建立在GLib上的语言。GLib是基于C语言的基础功能库,和Boost之于C++的地位有几分类似。GLib除了提供了一些数据结构、线程操作、输入输出外,还有一套完全基于C语言的对象系统GObject。在此基础之上,才有了GTK+,这个在Linux上比较流行的图形工具包。
GLib很不错,善用之可以避免重新造很多轮子。GObject可能是摆脱C++后不得已的选择,因为图形界面这样的东西太依赖于面向对象的特性了。它们都基于C,设计得或许很漂亮,可用起来就比较麻烦了,因为C编译器不像C++那样能自动生成代码。例如,C++的shared_ptr
可以以几乎透明的方式实现引用计数:每一份拷贝在进入和退出作用域的地方,C++编译器可以自动插入增加引用和减少引用的代码。换作C的话,则需要在代码中明确写出来。
为了减轻开发人员的负担,提高开发效率,Vala被发明出来,它模仿C#的语法,在语法层面上支持面向对象。值得指出的是,Vala的编译器先将vala源文件转换成C源文件,然后调用C编译器来编译。因为幕后是C语言,所以一方面不会带来性能上的退化,另一方面也不会引入兼容性的困难。
继续阅读 →我对虚拟机中运行的FreeBSD总是很满意。所以产生了在真实环境中运行的念头。但是我天真了。尝试了才发现FreeBSD并不能驱动我的无线网卡,上不了网的话其他的也不要谈了。
只好乖乖地转投了Linux对怀抱。在Arch和Gentoo之间选择了一会儿。Arch很好很方便,我还用了装上去用了一会儿。它的软件版本之新也着实把我吓了一跳(例如gcc已经更新到了6.1.0版本)。
我最终选择了更加折腾的Gentoo。Gentoo配置起来更麻烦一些,所以有必要把相关步骤记录下来,以供日后参考。
继续阅读 →suckless.org有两个小程序:dwm和surf。我很喜欢。
现在的软件有一个不好的趋势,那就是越做越大。这可能和计算机资源越来越充裕有关系。随随便便就有一个上百GB的硬盘,那么占用5GB甚至更多的空间安装一个Gnome桌面环境也未尝不可。
软件之间互相依赖。我只需要A软件的功能,但是A需要B的功能X,所以我还得安装B。然而B却自带了其他与之不相关的功能Y, Z, …功能Y, Z又依赖其他的软件C, D, …这样下去就没完没了了。
继续阅读 →微分方程的数值解法是一个庞大的话题。这里描述一种简单的、容易想到的数值解法,它可以用简单的计算机程序实现。
以微分方程 \[\frac{dv}{dt} + v = \sin t\] 为例,这是一个一阶线性微分方程。求解的时候,把时间离散化,即\(t=n\Delta T\),\(\Delta T\)是最小的时间单位。假设我们只关心\(t\geq 0\)时的解,那么\(n\)是自然数。
考虑导数的定义 \[\frac{dv}{dt} = \lim_{\Delta t \to 0} \frac{\Delta v}{\Delta t} = \lim_{\Delta t \to 0} \frac{v(t)-v(t-\Delta t)}{\Delta t}.\] 在离散化的时间里,我们不可能让\(\Delta t\)无穷小,最小也只能等于时间单位\(\Delta T\)。所以时间离散化以后,我们用\(\frac{\Delta v}{\Delta T}\)来代替导数\(\frac{dv}{dt}\)。
在一个\(\Delta T\)内,\(v\)的增量\(\Delta v = v(t) - v(t-\Delta T)\)。为了书写方便,我们用记号\(v[n]\)表示\(v(n\Delta T)\)。考虑到\(t=n\Delta T\),有 \[\Delta v = v[n] - v[n-1].\]
于是,原来的微分方程就可以写成离散的形式 \[\frac{v[n]-v[n-1]}{\Delta T} + v[n] = \sin t.\] 将\(v[n]\)解出,得 \[v[n] = \frac{v[n-1] + \Delta T\sin t}{1+\Delta T}.\] 这就是一个递推式,给定初始值\(v[0]\),就可以递推地计算出后续的所有\(v[n]\).
继续阅读 →要生成三角波,可以考虑用电流源给电容器充电/放电。因为电容的伏安关系式为 \[i = C{{\rm d}u\over{\rm d}t},\] 所以只要通过的电流恒定,电容两端的电压就会线性地变化。
如图所示的原理电路中,左边是两个方向相反的电流源(通常,从电源抽取电流的称为电流源,将电流流入地的称为电流阱),经过一个单刀双掷开关,轮流地给电容充放电。
但是这个开关要能实现自动地切换,才能实现给电容轮流充放电的功能。所以希望这个开关是受电压控制的开关,当控制电压为高电平时开关拨向上面,而低电平时拨向下面。
这个控制信号可以由施密特触发器产生。如图所示,电容上端电平送入一个反相的施密特触发器,当输入电平上升至上门限电压时,施密特触发器输出突变为低电平,当输入电平下降至下门限电压时,输出突变为高电平。这样就实现了控制信号的生成。
继续阅读 →如图,使用两个运算放大器可以实现RS锁存器的功能。
可以使用Falstad的电路模拟器观察该电路的行为。
继续阅读 →如图所示的电路,在\(t=0\)时刻,有\(i_{L_r}(0)=I_0\), \(u_{C_r}(0)=0\)。试求\(t>0\)时的\(i_{L_r}(t)\)和\(u_{C_r}(t)\)。
这电路含有动态元件,可以使用运算法来求解。
继续阅读 →我的FPGA开发板上有一个DS1302芯片,这是Dallas公司生产的计时芯片。它可以记录年、月、日、星期、时、分、秒信息,并且可以在断电时通过后备电源(电池)保持时钟的运行。
所以我打算利用这个芯片的功能做设计一个数字钟电路。首先需要解决的问题是和该芯片的通信。DS1302采用串行端口进行通信,因此我需要研究进行串口通信的方法。
研究DS1302的通信时序图,
看似简单的时序图其实包含了大量的信息,值得仔细地研究。
继续阅读 →