umask,补码,ASCII码:稍微深入考虑一点

1. umask

豆瓣网友指出,微信读书有个好处,就是能看到别人的标注和笔记,别人也能看到自己的。kindle也能共享,但是远没有微信方便和活跃。我就偷偷看别人的笔记,比如大家读古龙时候的感叹。有时猜测,这位读者是个孩子,那位读者觉得自己不是孩子了。有时自己也感叹,哇,当年没有读懂,或者当年肯定读不懂。有时还参与两句。

也看娄嘉鹏老师的技术笔记,他的很多阅读不是为了学习,而是考虑在教学中如何清晰讲解,比如下面这条就挺有意思。

娄老师标注的是文字原文是这样的,"特别强调一下,网络上有很多关于计算umask遮罩后权限值的讲解,比较主流但是错误的讲解方式是使用“同位相减”的做法来计算遮罩后的值"。

"特别强调一下",WJY同学表达过,这是相当有脾气的说法。作者的意思是"就这你还不明白?我还得再说一遍。"对这句话,教师当慎言。
umask_and_privilegeumask_and_privilege
(1)

后面作者提到了错误的看法,还举了例子。用 权限666 减去 掩码011,同位相减得到权限值 655。这个结果是错的,正确的权限值是 666。

为啥正确呢,不知道。作者没有给出正确的算法。教学中教师通常容易犯的错误是只把正确的说了,但是(既不说原理,也)不说哪些是错的。比如深蹲时双脚就像旋入地面,原理是绷紧下肢提高对膝盖的控制,尤其是最低端,对应的错误是大腿 (内侧?) 放松。教练就说,旋进地面啊,拳要打进去,站直,你怎么连什么是站直也不会,不是超伸。好,终于说到了什么是不对的。

正确的权限值是 666,这可以通过实验验证,但是原理还是不清楚。

作者说,权限666 和 掩码011 间的运算,当作按位减是不对的,那么什么是对的呢?对这个算法的描述,作者用的动词是"遮罩掉"。遮罩就是掩码mask。但是,"遮罩掉"是个什么操作,难度不 (显然) 是按位减么?作者定义了个名词,但是没有解释。

上网搜索一下,比如[https://www.cnblogs.com/wish123/p/7073114.html]在《Linux中设置UMASK值》这篇博客用的动词是,从最大权限777中"去掉"umask值。

遮罩掉、去掉,有同学说,不是挺明白的么?

(2)

什么是明白?就是能编程序实现。C语言里有定义遮罩掉、去掉这两个函数或操作符么。你是不是又想做按位减法了。

对了,娄老师标记这段话时,是怎么评论的呢?他说,"一般与掩码与,umask取反与"。我最初没看明白,但是娄老师这句话显然比作者更靠谱,至少 与、取反这样的术语,比"遮罩掉、去掉"更接近机器的语言。在靠谱这个意义上,不说遮罩掉、去掉这样的人话,更像是做人事。

我没问娄老师到底啥意思,又不急着知道,觉得自己想想挺好玩的。

事实上,我能体会到 遮罩掉、去掉 的含义,也就是说,我可以枚举/穷举出所有的结果,而且这本书里有例子可以验证。于是我打了个表格,以下第一列p代表 privilege权限,第二列是umask,第三列是求出的结果。

|P | umask | r |
|0 |  0    | 0 |
|0 |  1    | 0 |
|1 |  0    | 1 |
|1 |  1    | 0 |

看第2行即010,确实不是按位减。这一位,也就是作者举的 666 去掉 011 以后 得 666,而不是 655,6或5转换成二进制为110和101的最后一bit。
    110
?   001
-------
    110

有个这个表格,我们可以写一大堆if-else……了。

(3)

不优雅啊,我估计RH和GYB同学会这么说。

离散数学课和数字电路课上,我们学过把真值表转换成逻辑表达式。过程略过,结果是

 r = p & !umask

这说我想起了娄老师说的话,"umask取反与",umask取反,然后与p。
微信图片_20200729010927
2. 补码

什么时候用补码?负整数在计算机中用补码表示。

为什么需要补码?通过补码,CPU可以使用加法器做减法计算。

怎么算补码?反码加1。

为什么?

那些天同时在看这本书,[https://book.douban.com/subject/25882201/],From Mathematics to Generic Programming。看到补码运算的时候写了一段笔记,简单解释补码计算的原理。

(1)补码

补码是什么?

补码 = 模 - 数 ..................公式1

模是什么?模是进制的大小。钟表12进制,模就是12。人类用10进制,模是10。

模12的例子:

(-3) = 12 - 3 = 9   ;9点是差3个小时12点

(-2) = 12 - 2 = 10  ;10点是差2个小时12点

模10的例子:

-2 = 10 - 2 = 8


模8的例子:

-2 = 8 - 2 = 6

对上述例子中的最后一个,模8中的-2,我们验证一下。

二进制下2取反+1,

(010)-> 取反 -> (101) -> +1 -> (110)

-2   ->->->->->->->->->->->->->   6

(2)取反

取反操作 <=> (模-1)-数    ............公式2

这是因为 数+反 = 模-1

看两个例子

  001
+ 110
------
  111

  010
+ 101
------
  111

(3)推导

取反     <=> (模-1) - 数         ..........根据公式2
取反 + 1 <=> (模-1) - 数 + 1     ..........左右都加1
取反 + 1 <=>  模    - 数         .......... -1+1 => 0
取反 + 1 <=>  补码               .......... 公式1

所以"取反+1"就是补码。
微信图片_20200729010930

(4)原理

基于的原理是 : 模 就是 0。

12点就是0点,毕十10就是零蛋。限制位数,0x100,就是 0x00。

更深层的原理是因为 : 求余,求模。

这就是为什么求余运算的名字是求模的原因吧。

(5)编码举例

除去符号位 (假设模8) ,以下每行的两个数字编码相同。

| +0 | -0 |
| 1  | -7 |
| 2  | -6 |
| 3  | -5 |
| 4  | -4 |
| 5  | -3 |
| 6  | -2 |
| 7  | -1 |

每行的两个数字,它们绝对值的和是8,即模。

举例,-7的补码是

  原码        111,

  按位取反得到 000,

  加1得到     001.
  
刚好与同一行的 1 的原码 001 相同。

我们还能注意,负数里面还有个 -0。
微信图片_20200729010933
3. ASCII

这段非常短,就是想回答下不少同学可能有过的疑惑,ASCII里为什么 A是65,a是97,这么有零有整的。不怎么好记啊。

因为,如果你不看两列的ASCII码表,而是找一下16行16列的ASCII码表就会发现,或者按十六进制就会发现,就像1024是整数,'A'和'a'都是整数。

A 是 0x41,即4*16+1 = 65;

a 是 0x61,即6*16+1 = 97。

还有'0',是0x30,即 48。
ascii
为什么ASCII要在大写、小写、数字之间加些乱七八糟的字符,逗号、冒号、方括号,还不把这些符号连续地放在一起。正是为了填充,让A、a、0在整数开头吧。