程序员二进制计算器 v1.37
该1.37版本修复了之前1.36版的BUG。
长期以来,计算器工具彻底被某系统自带的Calc带歪了:按钮驱动、操作繁琐;一手不慎,全部输入都要报废;历史输入不能复用。而表达式驱动才是有效解决办法。
自从2014年本工具第一版上传以来,计算器越来越多往表达式驱动发展,现在连手机上都很多是表达式计算器了,这是一个可喜的进步。
如果结合屏幕取词功能,甚至可以像金山词霸一样即指即算,带来无限可能。
工具简介:
适合软硬件开发人员的计算器,二进制运算强大,支持64位。
采用表达式驱动,输入表达式便即时显示结果,抛弃传统计算器繁琐的按钮,表达式可复制粘贴、回调使用,可以任意复杂组合。
支持二进制串直接运算,如0b1101 & 0b0011= 1。
支持2/8/16进制常量、字符常量,以及输出为这些进制,轻松解决各种进制转换。
支持与、或、非、异或、移位(循环、逻辑、算术),直接读写二进制位,指定位段读、写、置1、清0、反转。
支持常用位运算、数学运算、关系运算、常用转换运算,以运算符或函数方式调用。
支持类C语言库函数调用。
运算结果可存放在变量中。
总之,此计算器相当于即时执行的C语言表达式,但用起来更方便、高效。
使用前请仔细阅读窗口内的说明书。
作者:胡彦
疯狂HTML5_CSS3_JavaScript讲义_原书光盘
讲HTML网页制作的书有很多,相对于那些建网站的书,李刚老师的这本书还是比较靠谱的,因为李刚会站在一个程序员开发软件的角度来介绍。
程序员二进制计算器 v1.36
专为程序员打造的计算器,二进制运算强大,支持64位。
采用表达式驱动,输入表达式便即时显示结果,抛弃传统计算器繁琐的按钮,表达式可粘贴或回调重复使用。
支持二进制串直接运算,如0b1101 & 0b0011= 0b0001。
支持与、或、非、异或、移位(循环、逻辑、算术),直接读写二进制位,指定位段读、写、置1、清0、反转。
二进制数据表达方式多样,数据可以K、M、G等单位为后缀。
支持类C语言库函数调用。
结果可以各种进制、各种单位输出。
运算结果可存放在变量中。
内置常用生活函数。
总之,此计算器相当于解释执行的C语言表达式,但用起来更方便、更高效。
使用前请仔细阅读窗口内的帮助,便可成为开发工作中的好帮手。
[程序员二进制计算器 v1.36 帮助]
一 用法
在底部框中输入要计算的表达式,会即时显示计算结果,当无结果时说明表达式输入有误。
按回车键保存该次输入、清空输入框,并显示详细结果,包括可能有的错误信息。
在底部框中按键盘上、下箭头,可以调出历史输入。
当光标在结果框内时,按回车或TAB键可使光标跳至输入框。
二 特点
即时计算。
便捷的历史回调、复制、粘贴功能。
支持64位整型运算,整数有效范围为 -9223372036854775808 至 9223372036854774784。
三 数的表示
1-整型数表示法
如何表示二、八、十六进制整数:
(1)二进制数
以0b或0B开头,后面的0、1数字之间可以用空格分隔。
0b1001 = 9
0b 1001 0110 = 0x96 = 150
0B1001 0110% = 150% = 1.5
(2)八进制数
以0开头:
010 = 8
027 + 7 = 23 + 7 = 30
(3)十六进制数
以0x或0X开头:
0x10 = 16
-0XabEF * 2 = -44015 * 2 = -88030
2-浮点数表示法
(1)小数点前后的省略表示
3.14
3. = 3.0
.14 = 0.14
(2)用科学计数法表示浮点数
3e2 = 300
3e+2 = 300
3e-2 = 0.03
3.e2 = 300
3.e+2 = 300
3.14e-2 = 0.0314
.14e+2 = 14
3-字符型表示法
支持字符常量,字符型自动转换为整型(值为该字符的ASCII码),支持C/C++转义字符。
如何得到字符的ASCII码:
'a' = 97
'A' = 65
'a' + 3 = 100
'a' - 'A' = 32
'\''=39
'\"' = '"' = 34
'\? = '?' = 63
'\\' = 92
'\a' = 7
'\b' = 8
'\f' = 12
'\n' = 10
'\r' = 13
'\t' = 9
'\v' = 11
4-量词后缀表示
一个数的后面,可以跟有倍率运算符,表示该数乘以相应的倍数,例如:
2w = 20000 (2万)
13y = 1300000000 (13亿)
4k = 4096
3% = 0.03 (百分之3)
详见“倍率运算”部分。
三 运算结果的输出格式
1-指定方法
格式: [格式前缀] 表达式
表达式前面可带有“格式前缀”(可选的),用来指定运算结果的输出格式。
“格式前缀”与C/C++的printf函数基本相同,但不支持%s或%S。
当省略格式前缀时,默认按%g方式输出。此时对较小的数按原样输出,较大的数按科学计数法输出。
2-整型的输出格式
当按二、八、十六进制输出时,是按其补码形式输出,最高位是符号位(正数为0、负数为1)。
所以此法可得到一个负数的补码表示。
(1)按二进制输出 %b或%B
%b等价与%B。
%b 12 = 0b1100
%b 0xffffffff = 0b1111 1111 1111 1111 1111 1111 1111 1111
(2)按八进制输出 %o或%O
%o等价与%O。
%o 10 = 012
(3)按十进制输出 %d
对于整型值,默认按十进制输出,此时%d可省略。
%d 123456789 = 123456789
(4)按十六进制输出 %x或%X
用%x时,字母abcdef输出为小写,用%X时,字母abcdef输出为大写。
%x 31 = 0x1f
%X 31 = 0X1F
(5)输出前的类型转换
对于浮点型值,用以上4种格式时,会先取整,再输出,因此它们可起到对结果取整的作用。
%d 12345.6789 = 12345
%d -12345.6789 = -12345
3-浮点型的输出格式
(1)按精简方式输出 %g
%g是默认的输出格式(可省略),此时不输出无意义的0,当数较大时自动按科学计数法输出。
%g 314 = 314
%g 3.14000 = 3.14
%g 3.14 * 2 = 6.28
%g 1234567 = 1.23457e+006
(2)按全部位数输出 %f
%f输出全部位,包括多余的0:
%f 3.14 * 2 = 6.280000
(3)如何指定小数点后保留位数 %m.nf
格式:%m.nf
其中,m和n都是整数,m指定总位数,n指定小数点后保留几位,如果m或n不足,则按实际位数输出。
例1:指定小数点后保留2位,其余位四舍五入:
%.2f 3.14159 = 3.14
例2:指定全部5位,且小数点后保留3位,其余位四舍五入:
%5.3f 3.14159 = 3.142
4-字符型的输出格式 %c或%C
如何得到ASCII值对应的字符(ASCII值转换为字符):
用%c,使结果(仅支持整型值)按字符形式输出即可。
%c 65 = 'A'
%C 100-3 = 'a'
5-智能大小输出格式
用于将一个较大或较小的、不易读的数,自动转换为易读的单位输出。
(1)以K、M、G、T、P、E为单位输出 %sz
%sz对结果按1024为单位换算,用于快速计算磁盘文件的大小(sz是size的简写):
当结果=1K且=1M且=1G且=1T且=1P且=1E时,以E为单位输出,例如:
%sz 10000000000000000000 = 8.673617E
(2)以W(万)、Y(亿)、WY(万亿)、YY(亿亿)、WYY(万亿亿)、YYY(亿亿亿)为单位输出 %num
%num对结果以万、亿等为单位输出,用于便捷得到一个大数的值,格式符合中国人的习惯:
当结果=1万且=1亿且=1万亿且=1亿亿且=1万亿亿且=1亿亿亿时,以亿亿亿为单位输出,例如:
围棋盘第1格放1粒米,以后每格放前一格2倍的米,一共需要多少粒米:
%num 2**(18*18) = 3.41758e+073YYY
(3)以kilo(千)、mil(百万)、bil(十亿)、tril(万亿)为单位输出 %val
%val对结果按1000为倍率单位输出,用于便捷得到一个大数的值:
当结果=1千且=1百万且=十亿且=1万亿时,以万亿为单位输出,例如:
%val 519322y = 51.9322tril (2012年国内生产总值,y是后缀运算符,表示前值乘以1亿)
6-固定比例输出格式
(1)按百分比输出 %2
%2将结果按百分比格式输出,例如:
对150种食品进行抽查,仅105种合格,合格率是多少:
%2 105/150 = 70%
(2)按万分比输出 %4
%4将结果按万分比格式输出,例如:
%4 0.00314 = 31.4%%
四 运算符与函数
1-运算符与分类
所有运算符,一律不区分大小写。
(1)一元运算符
只有1个操作数的运算符。
如果操作数是一个常数,它不需要用括号括起,并且与运算符之间不需要空格分隔,如:
sqr2 = 1.41421
cos0 = 1
当操作数是表达式时,需要用括号括起,以划分优先级:
sqr(1 + 2) = 1.73205
当操作数是一个内置常量时,它与运算符之间要有空格分隔:
cos pi = -1
对少数一元运算符,按书写习惯放在了操作数的后面,如阶乘!和百分比%
3! = 6
3% = 0.03
(2)二元运算符
需要2个操作数的运算符,如+ - * /。
(3)三元运算符
需要3个操作数的运算符,如条件运算符 ?: 。
2-内置函数
调用格式:函数名(参数1, 参数2, ...)
其中,所有函数名不区分大小写。
函数名后是用一对括号括起的参数列表,各参数间用逗号分隔。
每个参数可以是一个数值,也可以是复杂表达式。
参数个数必须符合该函数的定义,部分函数支持无穷多个参数(sum, ave, max, min)。
整个函数调用又是个表达式(值为函数的返回值),又可以参与构成其它表达式。
五 基本运算
1-四则运算
(1)加 +
3 + 2 = 5
(2)减 -
3 - 2 = 1
(3)乘 *
3 * 2 = 6
(4)除 /
除数不能为0。
3 / 2 = 1.5
(5)求余 mod
除数不能为0,支持对浮点数求余。
5 mod 3 = 2
5.2 mod 3.1 = 2.1
6.28 mod 1.5 = 0.28
2-等比运算
格式为 a : b = c : ?
整个表达式的值为“使等式成立的问号处”的值。这里冒号的含义等于除号。
例子:商场里100元的衣服,打折后卖75元, 则另一件150元的衣服,同样打折后卖多少? 写出表达式 100:75=150:?,结果为112.5
3-取整
(1)下取整 floor
返回不大于x的最大整数:
floor2.8 = 2
floor-2.8 = -3
(2)上取整 ceil
返回不小于x的最小整数:
ceil2.8 = 3
ceil-2.8 = -2
4-求绝对值 abs
abs-3.14 = 3.14
abs(3 - 5) = 2
5-求阶乘 !
操作数不能小于0,或大于100。
3! = 6
(3!)! = 6! = 720
6-倍率运算
一个表达式的后面,可以跟有倍率运算符,表示该表达式的值乘以相应的倍数。
此法在表示一个大数或特定数时,可以减少书写量。
(1)存储单位后缀运算符 K M G T P E
4k = (480/120)k = 4*1024 = 4096
2m = (2k)k = 2*1024*1024 = 2097152
4g = (2+2)g = 4*1024*1024*1024 = 4294967296
1t = 1024*1024*1024*1024 = 1099511627776
1p = 1024*1024*1024*1024*1024 = 1125899906842624
1e = 1024*1024*1024*1024*1024*1024 = 1152921504606847000
(2)数量后缀运算符一 w y wy yy wyy yyy
2w = 20000 (万)
13y = 1300000000 (亿)
1wy = (1w)y = 10000y = 1e+012 (万亿)
1yy = (1y)y = 1e+016 (亿亿)
1wyy = ( (1w) y )y = 1e+020 (万亿亿)
1yyy = ( (1y) y )y = 1e+024 (亿亿亿)
(3)数量后缀运算符二 kilo mil bil tril
11.034kilo = 11.034 * 1000 = 11034 (千)
1.392mil = 1.392 * 100w = 1392000 (百万)
1.35382bil = 1.35382 * 10y = 1353821000 (十亿)
51.9322tril = 51.9322wy = 5.19322e+013 (万亿)
(4)比率后缀运算符 % %%
求百分比 %
3% = 0.03
(500%)% = 5% = 0.05
500% mod 3 = 5 mod 3 = 2
求万分比 %%
12345%% = (12345%)% = 123.45% = 1.2345
7-幂运算、指数运算
(1)求平方根 sqr
sqr2 = 1.41421
sqr(1 + 2) = 1.73205
(2)求X的Y次方 **
幂运算,支持浮点数:
4 ** 3 = 4 * 4 * 4 = 64
2.5 ** 1.5 = 3.95285
(3)求e的x次方 exp
exp1.5 = e ** 1.5 = 4.48169
8-对数运算
(1)求以2为底的对数 lg
lg2 = 1, lg(2 * 2 * 2) = 3
(2)求以10为底的对数 ln
ln10 = 1
ln(10 * 10) = 2
(3)求以e为底的对数 log
log e = 1
log(e * e) = 2
9-取负 -
-3.14 * -(1+2) = -3.14 * -3 = 9.42
5--3 = 5 - (-3) = 5 + 3 = 8
5--(-3) = 5 - (-(-3)) = 5 - 3 = 2
六 二进制运算
1-位与 &
0b1111 & 0b1001 = 15 & 9 = 0b1001 = 9
2-位或 |
0b1100 | 0b0011 = 12 | 3 = 0b1111 = 15
3-位取反 ~
~( -2 ) = ~ 0b1111 1111 1111 1111 1111 1111 1111 1110 = 1 (32位版本)
~( -2 ) = ~ 0b1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 = 1 (64位版本)
4-异或 ^
0b1111 ^ 0b1001 = 15 ^ 9 = 0b0110 = 6
5-移位运算
(1)左移 <<
0b0001 << 2 = 1 <>
0b1100 >> 2 = 12 >> 2 = 0b0011 = 3
注:这里是采用逻辑右移还是算术右移,由系统决定。
如果确定做逻辑右移,请用下面的rshl运算符;确定做算术右移,请用下面的rsha运算符。
(3)循环左移 rol
格式:data rol n
功能:返回整型值data循环左移n位后的结果。
例子:%x 0x80000000 rol 2 = 0x2 (32位版本)
%x 0x8000000000000000 rol 2 = 0x2 (64位版本)
(4)循环右移 ror
格式:data ror n
功能:返回整型值data逻辑循环右移n位后的结果。
例子:%x 0xf ror 8 = 0x0f000000 (32位版本)
%x 0xf ror 8 = 0x0f00000000000000 (64位版本)
(5)逻辑右移 rshl
格式:data rshl n
功能:无论当前系统采用何种形式的右移,返回整型值data逻辑右移n位后的结果。
逻辑右移是指,当右移n位时,高n位全补0(不考虑符号位)。
例子:%x 0xf0000000 rshl 4 = 0x0f000000
(6)算术右移 rsha
格式:data rsha n
功能:无论当前系统采用何种形式的右移,返回整型值data算术右移n位后的结果。
算术右移是指,如果符号位为1,则右移n位时,高n位全补1,否则全补0。
例子:%x 0x80000000 rsha 8 = 0xff800000 (32位版本)
%x 0x70000000 rsha 4 = 0x70000000 (32位版本)
%x 0x8000000000000000 rsha 8 = 0xff80000000000000 (64位版本)
%x 0x7000000000000000 rsha 4 = 0x700000000000000 (64位版本)
6-二进制运算函数
(1)读二进位函数 rb
格式:rb(data, start, length)
功能:对整型值data,从低位的start位开始(位数从0开始计),连续取出其高位的length位的值,返回该值。
例子:%b rb(0b1010 0101, 4, 2) = 0b10
(2)写二进位函数 wb
格式:wb(data, start, length, value)
功能:对整型值data,将其中start位开始(位数从0开始计)、其后连续的的length位的值改写为value,返回修改后的data值。
例子:%b wb(0b1010 0000, 4, 4, 0b1111) = 0b1111 0000
(3)指定位置1函数 setb
格式:setb(data, start, length)
功能:对整型值data,从低位的start位开始(位数从0开始计),其后连续的length位全部置1,返回修改后的data值。
例子:%x setb(0x0, 8, 16) = 0x00ffff00
(4)指定位清0函数 rstb
格式:rstb(data, start, length)
功能:对整型值data,从低位的start位开始(位数从0开始计),其后连续的length位全部置0,返回修改后的data值。
例子:%x rstb(0xffffffff, 8, 16) = 0xff0000ff
(5)指定位反转函数 rvsb
格式:rvsb(data, start, length)
功能:对整型值data,从低位的start位开始(位数从0开始计),其后连续的length位全部反转,返回修改后的data值。
例子:%x rvsb(0x0, 8, 16) = 0x00ffff00
七 逻辑运算
支持6种关系运算,和3种逻辑运算,以及C/C++条件运算。
当逻辑结果为真时,结果为整型值1;当逻辑结果为假时,结果为整型值0。
1-关系运算
(1)大于 >
1+2 > 3+4 = (1+2) > (3+4) = 0
(2)大于等于 >=
3.14*2 >= 6.28 = 1
(3)小于 <
1+2 < 3+4 = (1+2) < (3+4) = 1
(4)小于等于 <=
3.14*2 <= 6.28 = 1
(5)等于 ==
3.14*2 == 6.28 = 1
(6)不等于 !=
3.14*2 != 6.28 = 0
2-逻辑运算
(1)逻辑与 &&
1<2 && 3<5 = (1<2) && (3<5) = 1
1<2 && 32 || 3>3 = 0
1>2 || 3>=3 = 1
(3)逻辑非 !
!0 = 1
!1 = 0
!(-3.14) = 0
3-条件运算 ? :
格式:表达式1 ? 表达式2 : 表达式3
结果:当表达式1为真时,整个表达式的结果等于表达式2,否则结果等于表达式3。
例子:1+2>3+4 ? 1+2 : 3+4 = 7
sqr(1+2<3+4 ? 1+2 : 3+4) = sqr(1+2) = 1.73205
4-逻辑结果值
逻辑结果值(0或1)又可作为整数参与其它运算。
(-3<5) + 2 = 1 + 2 = 3
1<2<3 = (1<2)<3 = 0e ? pi : e (得到常量pi和e中的大者)
myave = ave(65, 78, 84.5, 96) (求几个数的平均值,myave = 80.875)
对已定义的变量可以再次赋值,这时原值丢失,保存新值。
引用未定义的变量时会报错。
2-赋值表达式
赋值运算本又是个表达式,即赋值表达式。
整个赋值表达式的值,为=号右部表达式的值,例如
a = (b = 5) (此时变量a和b的值都等于5)
采用此法可以一次性定义多个变量。
3-内置的常量
以下内置常量可以直接使用,常量名不区分大小写。
不能对常量重新赋值,否则会报错。
e = 2.718281828459 (自然对数的底)
gold = 0.61803398874989484820 (黄金分割比率)
inch = 2.54 (1英寸等于几厘米)
kv = 273.15 (开氏温度 = 摄氏温度 + 273.15)
nmi = 1.852 (1海里等于几公里)
pi = 3.1415926535898 (圆周率)
十二 运算符的优先级
1-优先级顺序
运算符按优先级划分为如下14组,各个组的优先级由高到低,同一组内的优先级相同。
不清楚优先级时,请用使用括号。
(1) -(取负) !(逻辑非) ~(位取反) %(百分比) %%(万分比) !(阶乘) K M G T P E(存储单位后缀) w y wy yy wyy yyy kilo mil bil tril(数量后缀) id(arg1, arg2, ...)(函数调用)
(2) abs ceil floor lg ln log exp sqr rtd dtr sin cos tan ctan asin acos atan sinh cosh tanh intcm cmtin nmtkm kmtnm lbtkg kgtlb ftc ctf ktc ctk
(3) * / mod(求余) **(幂运算)
(4) + -
(5) <> rshl rsha rol ror
(6) > >= < <=
(7) == !=
(8) & (按位与)
(9) ^ (按位异或)
(10) | (按位或)
(11) && (逻辑与)
(12) || (逻辑或)
(13) ?: (条件运算) :=:? (等比运算)
(14) = (赋值运算)
2-用括号指定运算顺序
1 + 2 * 3 = 1 + (2 * 3) = 7
(1 + 2) * 3 = 9
1 * (2 + 3) = 5
author:
huyansoft
万能makefile写法详解,一步一步写一个实用的makefile
作者:胡彦 2013-5-21
本文档可能有更新,更新版本请留意http://blog.csdn.net/huyansoft/article/details/8924624
一
目的:编写一个实际可用的makefile,能自动编译当前目录下所有.c源文件,并且任何.c、.h或依赖的源文件被修改后,能自动重编那些改动了的源文件,未改动的不编译。
二
要达到这个目的,用到的技术有:
1-使用wildcard函数来获得当前目录下所有.c文件的列表。
2-make的多目标规则。
3-make的模式规则。
4-用gcc -MM命令得到一个.c文件include了哪些文件。
5-用sed命令对gcc -MM命令的结果作修改。
6-用include命令包含依赖描述文件.d。
三 准备知识
(一)多目标
对makefile里下面2行,可看出多目标特征,执行make bigoutput或make littleoutput可看到结果:
bigoutput littleoutput: defs.h pub.h
@echo $@ $(subst output,OUTPUT,$@) $^ # $@指这个规则里所有目标的集合,$^指这个规则里所有依赖的集合。该行是把目标(bigoutput或littleoutput)里所有子串output替换成大写的OUTPUT
(二)隐含规则
对makefile里下面4行,可看出make的隐含规则,执行foo可看到结果:
第3、4行表示由.c得到.o,第1、2行表示由.o得到可执行文件。
如果把第3、4行注释的话,效果一样。
即不写.o来自.c的规则,它会自动执行gcc -c -o foo.o foo.c这条命令,由.c编译出.o(其中-c表示只编译不链接),然后自动执行gcc -o foo foo.o链接为可执行文件。
foo:foo.o
gcc -o foo foo.o; ./foo
foo.o:foo.c #注释该行看效果
gcc -c foo.c -o foo.o #注释该行看效果
(三)定义模式规则
下面定义了一个模式规则,即如何由.c文件生成.d文件的规则。
foobar: foo.d bar.d
@echo complete generate foo.d and bar.d
%.d: %.c #make会对当前目录下每个.c文件,依次做一次里面的命令,从而由每个.c文件生成对应.d文件。
@echo from $< to $@
g++ -MM $ $@
假定当前目录下有2个.c文件:foo.c和bar.c(文件内容随意)。
验证方法有2种,都可:
1-运行make foo.d(或make bar.d),表示想要生成foo.d这个目标。
根据规则%.d: %.c,这时%匹配foo,这样%.c等于foo.c,即foo.d这个目标依赖于foo.c。
此时会自动执行该规则里的命令gcc -MM foo.c > foo.d,来生成foo.d这个目标。
2-运行make foobar,因为foobar依赖于foo.d和bar.d这2个文件,即会一次性生成这2个文件。
四
下面详述如何自动生成依赖性,从而实现本例的makefile。
(一)
本例使用了makefile的模式规则,目的是对当前目录下每个.c文件,生成其对应的.d文件,例如由main.c生成的.d文件内容为:
main.o : main.c command.h
这里指示了main.o目标依赖于哪几个源文件,我们只要把这一行的内容,通过make的include指令包含到makefile文件里,即可在其任意一个依赖文件被修改后,重新编译目标main.o。
下面详解如何生成这个.d文件。
(二)
gcc/g++编译器有一个-MM选项,可以对某个.c/.cpp文件,分析其依赖的源文件,例如假定main.c的内容为:
#include //标准头文件(以方式包含的),被-MM选项忽略,被-M选项收集
#include "stdlib.h"//标准头文件(以""方式包含的),被-MM选项忽略,被-M选项收集
#include "command.h"
int main()
{
printf("##### Hello Makefile #####\n");
return 0;
}
则执行gcc -MM main.c后,屏幕输出:
main.o: main.c command.h
执行gcc -M main.c后,屏幕输出:
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/bits/predefs.h /usr/include/sys/cdefs.h \
/usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.4.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \
/usr/include/stdlib.h /usr/include/sys/types.h /usr/include/time.h \
/usr/include/endian.h /usr/include/bits/endian.h \
/usr/include/bits/byteswap.h /usr/include/sys/select.h \
/usr/include/bits/select.h /usr/include/bits/sigset.h \
/usr/include/bits/time.h /usr/include/sys/sysmacros.h \
/usr/include/bits/pthreadtypes.h /usr/include/alloca.h command.h
(三)
可见,只要把这些行挪到makefile里,就能自动定义main.c的依赖是哪些文件了,做法是把命令的输出重定向到.d文件里:gcc -MM main.c > main.d,再把这个.d文件include到makefile里。
如何include当前目录每个.c生成的.d文件:
sources:=$(wildcard *.c) #使用$(wildcard *.cpp)来获取工作目录下的所有.c文件的列表。
dependence=$(sources:.c=.d) #这里,dependence是所有.d文件的列表.即把串sources串里的.c换成.d。
include $(dependence) #include后面可以跟若干个文件名,用空格分开,支持通配符,例如include foo.make *.mk。这里是把所有.d文件一次性全部include进来。注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了。
(四)
现在main.c command.h这几个文件,任何一个改了都会重编main.o。但是这里还有一个问题,如果修改了command.h,在command.h中加入#include "pub.h",这时:
1-再make,由于command.h改了,这时会重编main.o,并且会使用新加的pub.h,看起来是正常的。
2-这时打开main.d查看,发现main.d中未加入pub.h,因为根据模式规则%.d: %.c中的定义,只有依赖的.c文件变了,才会重新生成.d,而刚才改的是command.h,不会重新生成main.d、及在main.d中加入对pub.h的依赖关系,这会导致问题。
3-修改新加的pub.h的内容,再make,果然问题出现了,make报告up to date,没有像期望那样重编译main.o。
现在问题在于,main.d里的某个.h文件改了,没有重新生成main.d。进一步说,main.d里给出的每个依赖文件,任何一个改了,都要重新生成这个main.d。
所以main.d也要作为一个目标来生成,它的依赖应该是main.d里的每个依赖文件,也就是说make里要有这样的定义:
main.d: main.c command.h
这时我们发现,main.d与main.o的依赖是完全相同的,可以利用make的多目标规则,把main.d与main.o这两个目标的定义合并为一句:
main.o main.d: main.c command.h
现在,main.o: main.c command.h这一句我们已经有了,如何进一步得到main.o main.d: main.c command.h呢?
(五)
解决方法是行内字符串替换,对main.o,取出其中的子串main,加上.d后缀得到main.d,再插入到main.o后面。能实现这种替换功能的命令是sed。
实现的时候,先用gcc -MM命令生成临时文件main.d.temp,再用sed命令从该临时文件中读出内容(用输出到最终文件main.d。
命令可以这么写:
g++ -MM main.c > main.d.temp
sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g' main.d
其中:
sed 's,\(main\)\.o[ :]*,\1.o main.d : ,g',是sed命令。
main.d,把行内替换结果输出到最终文件main.d。
(六)
这条sed命令的结构是s/match/replace/g。有时为了清晰,可以把每个/写成逗号,即这里的格式s,match,replace,g。
该命令表示把源串内的match都替换成replace,s指示match可以是正则表达式。
g表示把每行内所有match都替换,如果去掉g,则只有每行的第1处match被替换(实际上不需要g,因为一个.d文件中,只会在开头有一个main.o:)。
这里match是正则式\(main\)\.o[ :]*,它分成3段:
第1段是\(main\),在sed命令里把main用\(和\)括起来,使接下来的replace中可以用\1引用main。
第2段是\.o,表示匹配main.o,(这里\不知何意,去掉也是可以的)。
第3段是正则式[ :]*,表示若干个空格或冒号,(其实一个.d里只会有一个冒号,如果这里写成[ ]*:,即匹配若干个空格后跟一个冒号,也是可以的)。
总体来说match用来匹配'main.o :'这样的串。
这里的replace是\1.o main.d :,其中\1会被替换为前面第1个\(和\)括起的内容,即main,这样replace值为main.o main.d :
这样该sed命令就实现了把main.o :替换为main.o main.d :的目的。
这两行实现了把临时文件main.d.temp的内容main.o : main.c command.h改为main.o main.d : main.c command.h,并存入main.d文件的功能。
(七)
进一步修改,采用自动化变量。使得当前目录下有多个.c文件时,make会依次对每个.c文件执行这段规则,生成对应的.d:
gcc -MM $ $@.temp;
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@;
(八)
现在来看上面2行的执行流程:
第一次make,假定这时从来没有make过,所有.d文件不存在,这时键入make:
1-include所有.d文件的命令无效果。
2-首次编译所有.c文件。每个.c文件中若#include了其它头文件,会由编译器自动读取。由于这次是完整编译,不存在什么依赖文件改了不会重编的问题。
3-对每个.c文件,会根据依赖规则%.d: %.c,生成其对应的.d文件,例如main.c生成的main.d文件为:
main.o main.d: main.c command.h
第二次make,假定改了command.h、在command.h中加入#include "pub.h",这时再make:
1-include所有.d文件,例如include了main.d后,得到依赖规则:
main.o main.d: main.c command.h
注意所有include命令是首先执行的,make会先把所有include进来,再生成依赖规则关系。
2-此时,根据依赖规则,由于command.h的文件戳改了,要重新生成main.o和main.d文件。
3-先调用gcc -c main.c -o main.o生成main.o,
再调用gcc -MM main.c > main.d重新生成main.d。
此时main.d的依赖文件里增加了pub.h:
main.o main.d: main.c command.h pub.h
4-对其它依赖文件没改的.c(由其.d文件得到),不会重新编译.o和生成其.d。
5-最后会执行gcc $(objects) -o main生成最终可执行文件。
第三次make,假定改了pub.h,再make。由于第二遍中,已把pub.h加入了main.d的依赖,此时会重编main.c,重新生成main.o和main.d。
这样便实现了当前目录下任一源文件改了,自动编译涉及它的.c。
(九)
进一步修改,得到目前大家普遍使用的版本:
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $ $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@; \
rm -f $@.$$$$
第一行,set -e表示,如果某个命令的返回参数非0,那么整个程序立刻退出。
rm -f用来删除上一次make时生成的.d文件,因为现在要重新生成这个.d,老的可以删除了(不删也可以)。
第二行:前面临时文件是用固定的.d.temp作为后缀,为了防止重名覆盖掉有用的文件,这里把temp换成一个随机数,该数可用$$得到,$$的值是当前进程号。
由于$是makefile特殊符号,一个$要用$$来转义,所以2个$要写成$$$$(你可以在makefile里用echo $$$$来显示进程号的值)。
第三行:sed命令的输入也改成该临时文件.$$。
每个shell命令的进程号通常是不同的,为了每次调用$$时得到的进程号相同,必须把这4行放在一条命令中,这里用分号把它们连接成一条命令(在书写时为了易读,用\拆成了多行),这样每次.$$便是同一个文件了。
你可以在makefile里用下面命令来比较:
echo $$$$
echo $$$$; echo $$$$
第四行:当make完后,每个临时文件.d.$$,已经不需要了,删除之。
但每个.d文件要在下一次make时被include进来,要保留。
(十)
综合前面的分析,得到我们的makefile文件:
#使用$(wildcard *.c)来获取工作目录下的所有.c文件的列表
sources:=$(wildcard *.c)
objects:=$(sources:.c=.o)
#这里,dependence是所有.d文件的列表.即把串sources串里的.c换成.d
dependence:=$(sources:.c=.d)
#所用的编译工具
CC=gcc
#当$(objects)列表里所有文件都生成后,便可调用这里的 $(CC) $^ -o $@ 命令生成最终目标all了
#把all定义成第1个规则,使得可以把make all命令简写成make
all: $(objects)
$(CC) $^ -o $@
#这段是make的模式规则,指示如何由.c文件生成.o,即对每个.c文件,调用gcc -c XX.c -o XX.o命令生成对应的.o文件。
#如果不写这段也可以,因为make的隐含规则可以起到同样的效果
%.o: %.c
$(CC) -c $< -o $@
include $(dependence) #注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了
%.d: %.c
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $ $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@; \
rm -f $@.$$$$
.PHONY: clean #之所以把clean定义成伪目标,是因为这个目标并不对应实际的文件
clean:
rm -f all $(objects) $(dependence) #清除所有临时文件:所有.o和.d。.$$已在每次使用后立即删除。-f参数表示被删文件不存在时不报错
(十一)
上面这个makefile已经能正常工作了(编译C程序),但如果要用它编译C++,变量CC值要改成g++,每个.c都要改成.cpp,有点繁琐。
现在我们继续完善它,使其同时支持C和C++,并支持二者的混合编译。
#一个实用的makefile,能自动编译当前目录下所有.c/.cpp源文件,支持二者混合编译
#并且当某个.c/.cpp、.h或依赖的源文件被修改后,仅重编涉及到的源文件,未涉及的不编译
#详解文档:http://blog.csdn.net/huyansoft/article/details/8924624
#author:胡彦 2013-5-21
#----------------------------------------------------------
#编译工具用g++,以同时支持C和C++程序,以及二者的混合编译
CC=g++
#使用$(winldcard *.c)来获取工作目录下的所有.c文件的列表
#sources:=main.cpp command.c
#变量sources得到当前目录下待编译的.c/.cpp文件的列表,两次调用winldcard、结果连在一起即可
sources:=$(wildcard *.c) $(wildcard *.cpp)
#变量objects得到待生成的.o文件的列表,把sources中每个文件的扩展名换成.o即可。这里两次调用patsubst函数,第1次把sources中所有.cpp换成.o,第2次把第1次结果里所有.c换成.o
objects:=$(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(sources)))
#变量dependence得到待生成的.d文件的列表,把objects中每个扩展名.o换成.d即可。也可写成$(patsubst %.o,%.d,$(objects))
dependence:=$(objects:.o=.d)
#----------------------------------------------------------
#当$(objects)列表里所有文件都生成后,便可调用这里的 $(CC) $^ -o $@ 命令生成最终目标all了
#把all定义成第1个规则,使得可以把make all命令简写成make
all: $(objects)
$(CC) $(CPPFLAGS) $^ -o $@
@./$@ #编译后立即执行
#这段使用make的模式规则,指示如何由.c文件生成.o,即对每个.c文件,调用gcc -c XX.c -o XX.o命令生成对应的.o文件
#如果不写这段也可以,因为make的隐含规则可以起到同样的效果
%.o: %.c
$(CC) $(CPPFLAGS) -c $< -o $@
#同上,指示如何由.cpp生成.o,可省略
%.o: %.cpp
$(CC) $(CPPFLAGS) -c $< -o $@
#----------------------------------------------------------
include $(dependence) #注意该句要放在终极目标all的规则之后,否则.d文件里的规则会被误当作终极规则了
#因为这4行命令要多次凋用,定义成命令包以简化书写
define gen_dep
set -e; rm -f $@; \
$(CC) -MM $(CPPFLAGS) $ $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' $@; \
rm -f $@.$$$$
endef
#指示如何由.c生成其依赖规则文件.d
#这段使用make的模式规则,指示对每个.c文件,如何生成其依赖规则文件.d,调用上面的命令包即可
%.d: %.c
$(gen_dep)
#同上,指示对每个.cpp,如何生成其依赖规则文件.d
%.d: %.cpp
$(gen_dep)
#----------------------------------------------------------
#清除所有临时文件(所有.o和.d)。之所以把clean定义成伪目标,是因为这个目标并不对应实际的文件
.PHONY: clean
clean: #.$$已在每次使用后立即删除。-f参数表示被删文件不存在时不报错
rm -f all $(objects) $(dependence)
echo: #调试时显示一些变量的值
@echo sources=$(sources)
@echo objects=$(objects)
@echo dependence=$(dependence)
@echo CPPFLAGS=$(CPPFLAGS)
#提醒:当混合编译.c/.cpp时,为了能够在C++程序里调用C函数,必须把每一个要调用的C函数,其声明都包括在extern "C"{}块里面,这样C++链接时才能成功链接它们。
五
makefile学习体会:
刚学过C语言的读者,可能会觉得makefile有点难,因为makefile不像C语言那样,一招一式都那么清晰明了。
在makefile里到处是“潜规则”,都是一些隐晦的东西,要弄明白只有搞清楚这些“潜规则”。
基本的规则无非是“一个依赖改了,去更新哪些目标”。
正因为隐晦动作较多,写成一个makefile才不需要那么多篇幅,毕竟项目代码才是主体。只要知道makefile的框架,往它的套路里填就行了。
较好的学习资料是《跟我一起写Makefile.pdf》这篇文档(下载包里已经附带了),比较详细,适合初学者。
我们学习的目的是,能够编写一个像本文这样的makefile,以满足简单项目的基本需求,这要求理解前面makefile几个关键点:
1-多目标
2-隐含规则
3-定义模式规则
4-自动生成依赖性
可惜的是,这篇文档虽然比较全面,却没有以一个完整的例子为引导,对几处要点没有突出指明,尤其是“定义模式规则”在最后不显眼的位置(第十一部分第五点),导致看了“自动生成依赖性”一节后还比较模糊。
所以,看了《跟我一起写Makefile.pdf》后,再结合本文针对性的讲解,会有更实际的收获。
另一个学习资料是《GNU make v3.80中文手册v1.5.pdf》,这个手册更详细,但较枯燥,不适合完整学习,通常是遇到问题再去查阅。
其它文章和代码请留意我的blog: http://blog.csdn.net/huyansoft
[END]
一个Lex/Yacc完整的示例(可使用C++)
作者: 胡彦
本框架是一个lex/yacc完整的示例,用于学习lex/yacc程序基本的搭建方法,在linux/cygwin下敲入make就可以编译和执行。
本例子虽小却演示了lex/yacc程序最常见和重要的特征:
* lex/yacc文件格式、程序结构。
* 如何在lex/yacc中使用C++和STL库,用extern "C"声明那些lex/yacc生成的、要链接的C函数,如yylex(), yywrap(), yyerror()。
* 重定义YYSTYPE/yylval为复杂类型。
* 用%token方式声明yacc记号。
* 用%type方式声明非终结符的类型。
* lex里正则表达式的定义、识别方式。
* lex里用yylval向yacc返回属性值。
* 在yacc嵌入的C代码动作里,对记号属性($1, $2等)、和非终结符属性($$)的正确引用方法。
* 对yyin/yyout重赋值,以改变yacc默认的输入/输出目标。
* 如何开始解析(yyparse函数),结束或继续解析(yywrap函数)。
本例子功能是,对当前目录下的file.txt文件,解析出其中的标识符、数字、其它符号,显示在屏幕上。linux调试环境是Ubuntu 10.04。
总之,大部分框架已经搭好了,你只要稍加扩展就可以成为一个计算器之类的程序,用于《编译原理》的课程设计。
文件列表:
lex.l: lex程序文件。
yacc.y: yacc程序文件。
main.hpp: 共同使用的头文件。
Makefile: makefile文件。
file.txt: 给程序解析的文本文件。
使用方法:
1-把lex_yacc_example.rar解压到linux/cygwin下。
2-命令行进入lex_yacc_example目录。
3-敲入make,这时会自动执行以下操作:
(1) 自动调用flex编译.l文件,生成lex.yy.c文件。
(2) 自动调用bison编译.y文件,生成yacc.tab.c和yacc.tab.h文件。
(3) 自动调用g++编译、链接出可执行文件main。
(4) 自动执行main,得到如下结果:。
bison -d yacc.y
g++ -c lex.yy.c
g++ -c yacc.tab.c
g++ lex.yy.o yacc.tab.o -o main
id: abc
id: defghi
int: 123
int: 45678
op: !
op: @
op: #
op: $
AllId: abc defghi
参考资料:《Lex和Yacc从入门到精通(6)-解析C-C++包含文件》, http://blog.csdn.net/pandaxcl/article/details/1321552
其它文章和代码请留意我的blog: http://blog.csdn.net/huyansoft
2013-4-27
Doctors1.0.1
该版本解决了之前1.0.0版本中找不到链接库libc.lib的问题,以及IDE下点击Project菜单出现的BUG。
Doctors是标准C++语言子集的编译器,它可以将C++源程序编译链接成Win32平台上可执行的EXE文件。代码采用OOP语言完全手写而成,提供了IDE界面和命令行二种使用方式,其设计初衷是为程序提供更多的诊断功能。压缩包内含软件、实现文档和使用说明。
更新版本请留意:http://blog.csdn.net/huyansoft
Doctors1.0.0
Doctors是标准C++语言子集的编译器,它可以将C++源程序编译链接成Win32平台上可执行的EXE文件。代码采用OOP语言完全手写而成,提供了IDE界面和命令行二种使用方式,其设计初衷是为程序提供更多的诊断功能。
压缩包内含软件、实现文档和使用说明。
更新版本请留意:http://blog.csdn.net/huyansoft
ISO-ANSI C标准译文与注解 C-C++预处理部分.htm
本文档完整翻译了C标准(99版)中预处理和相关章节的内容,并在许多必要之处附加了注解和程序示例,以帮助读者理解标准原文,同时制作了详细的中英文索引备查。<br>译者:胡彦<br>出处:http://blog.csdn.net/huyansoft<br>如果转载,请保留译者和出处信息,谢谢!
ISO-ANSI C标准译文与注解 C-C++预处理部分.chm
这个CHM文件有问题,您不要再下载它了.
请下载下面这个HTML格式的:
http://download.csdn.net/source/476473