1 背景
首先,看看下面的代码→_→
#include <math.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
double L ,o ,P
,_=dt,T,Z,D=1,d,
s[999],E,h= 8,I,
J,K,w[999],M,m,O
,n[999],j=33e-3,i=
1E3,r,t, u,v ,W,S=
74.5,l=221,X=7.26,
a,B,A=32.2,c, F,H;
int N,q, C, y,p,U;
Window z; char f[52]
; GC k; main(){ Display*e=
XOpenDisplay( 0); z=RootWindow(e,0); for (XSetForeground(e,k=XCreateGC (e,z,0,0),BlackPixel(e,0))
; scanf("%lf%lf%lf",y +n,w+y, y+s)+1; y ++); XSelectInput(e,z= XCreateSimpleWindow(e,z,0,0,400,400,
0,0,WhitePixel(e,0) ),KeyPressMask); for(XMapWindow(e,z); ; T=sin(O)){ struct timeval G={ 0,dt*1e6}
; K= cos(j); N=1e4; M+= H*_; Z=D*K; F+=_*P; r=E*K; W=cos( O); m=K*W; H=K*T; O+=D*_*F/ K+d/K*E*_; B=
sin(j); a=B*T*D-E*W; XClearWindow(e,z); t=T*E+ D*B*W; j+=d*_*D-_*F*E; P=W*E*B-T*D; for (o+=(I=D*W+E
*T*B,E*d/K *B+v+B/K*F*D)*_; p<y; ){ T=p[s]+i; E=c-p[w]; D=n[p]-L; K=D*m-B*T-H*E; if(p [n]+w[ p]+p[s
]== 0|K <fabs(W=T*r-I*E +D*P) |fabs(D=t *D+Z *T-a *E)> K)N=1e4; else{ q=W/K *4E2+2e2; C= 2E2+4e2/ K
*D; N-1E4&& XDrawLine(e ,z,k,N ,U,q,C); N=q; U=C; } ++p; } L+=_* (X*t +P*M+m*l); T=X*X+ l*l+M *M;
XDrawString(e,z,k ,20,380,f,17); D=v/l*15; i+=(B *l-M*r -X*Z)*_; for(; XPending(e); u *=CS!=N){
XEvent z; XNextEvent(e ,&z);
++*((N=XLookupKeysym
(&z.xkey,0))-IT?
N-LT? UP-N?& E:&
J:& u: &h); --*(
DN -N? N-DT ?N==
RT?&u: & W:&h:&J
); } m=15*F/l;
c+=(I=M/ l,l*H
+I*M+a*X)*_; H
=A*r+v*X-F*l+(
E=.1+X*4.9/l,t
=T*m/32-I*T/24
)/S; K=F*M+(
h* 1e4/l-(T+
E*5*T*E)/3e2
)/S-X*d-B*A;
a=2.63 /l*d;
X+=( d*l-T/S
*(.19*E +a
*.64+J/1e3
)-M* v +A*
Z)*_; l +=
K *_; W=d;
sprintf(f,
"%5d %3d"
"%7d",p =l
/1.7,(C=9E3+
O*57.3)%0550,(int)i); d+=T*(.45-14/l*
X-a*130-J* .14)*_/125e2+F*_*v; P=(T*(47
*I-m* 52+E*94 *D-t*.38+u*.21*E) /1e2+W*
179*v)/2312; select(p=0,0,0,0,&G); v-=(
W*F-T*(.63*m-I*.086+m*E*19-D*25-.11*u
)/107e2)*_; D=cos(o); E=sin(o); } }
话说,你能看懂吗?看懂了,你就可以参加国际C语言混乱代码大赛(IOCCC, The International Obfuscated C Code Contest),别小看,这可是一项国际编程赛事。
表1 C语言中的运算符号
()、 []、 -> 、 .、 !、 ++、 -- | 圆括号、方括号、指针、成员、逻辑非、自加、自减 |
++ 、 -- 、 * 、 & 、 ~ 、 ! 、 + 、 - 、 sizeof、(cast) | 单目运算符 |
* 、 / 、 % | 算术运算符 |
+ 、 - | 算术运算符 |
<< 、 >> | 位运算符 |
< 、 <= 、 > 、 >= | 关系运算符 |
== 、 != | 关系运算符号 |
& | 位与 |
^ | 位异或 |
| | 位或 |
&& | 逻辑与 |
|| | 逻辑或 |
? 、 : | 条件运算符 |
= 、 += 、 -= 、 *= 、 /= 、 %= 、 &= 、 |= 、 ^= | 赋值运算符 |
, | 顺序运算符 |
看看表1,C语言里面的运算符号已经有很多了,而且还有一些什么斜杠啊之类的。因此,我们不能少看符号,这些符号可能会将我们完全弄晕。
好吧下面说正题了^_^下面我将总结我易错的几个点,算是我的读书笔记吧。下面的内容很多都基于参考文献的那一本书。
2 注释符号
1.编译器会删除被注释的代码,对于“/**/”型注释,编译器不是简单的删除,而是用空格代替原来的注释。所以语句
int/*…*/i;
是可以正确编译的。
2.以下语句是正确的。
//Is it a \
valid comment?
因为“\”是一个接连符。
3. “/*……*/”型注释是不能嵌套的,例如像:
/*这是/*错误的*/坑爹啊*/
因为“/*”总是与离它最近的“*/”匹配。
4.只要斜杠“/”和星号“*”之间没有空格,都会被当作注释的开始。下面的例子是错的。
y=x/*p;
应该写成:
y=x/ *p;//中间有空格
y=x/(*p);
突然想起我曾经再某公司笔试的时候就是错在这里(ㄒoㄒ)//
3 接续符号和转义符号
C语言里通过反斜杠“\”表示断行,因此这货就是传说中的接续符,但是成为接续符是需要条件的,就是反斜杠后面不能有东西,而且反斜杠后面不能有空格,反斜杠的下一行之前也不能有空格。
反斜杠这货还能用作转义字符的开始标识。如下表所示。
表2 常用转义字符及其含义
转义字符 | 意义 | ASCII码值(十进制) |
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符''\' | 092 |
\' | 代表一个单引号(撇号)字符 | 039 |
\" | 代表一个双引号字符 | 034 |
\0 | 空字符(NULL) | 000 |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
C语言字符集中的任何一个字符均可以用转义字符来表示。
4 引号
引号注意的不多,就注意:
双引号引起来的都是字符串常量,单引号引起来的都是字符常量。
从上面这一句引申出来的,字符串常量把空字符‘ \0’自动附加到字符串的尾部作为字符串的结束标志。这常常作为笔试题目的考点。
5 逻辑运算符
逻辑运算符有两个坑人的地方;
⑴“||”和“&&”这两个逻辑运算符容易和按位运算符“|”和“&”弄混。
⑵使用逻辑运算符的时候应该记住下面两个事项;
①逻辑运算符“||”两边的条件只要有一个为真,其结果就为真;
②逻辑运算符“&&”两边的条件只要有一个为假,其结果就为假;
⑶对于双目运算,函数调用必须是第一个操作数。
//以下是错误的调用
If((a==1)||fun())
{
//……
}
//以下是正确的用法
If(fun()||(a==1))
{
//……
}
6 位运算符
6.1 概念
C语言中有6种位运算符。如下所示
& //按位与
| //按位或
^ //按位异或
~ //取反
<< //左移
>> //右移
6.2 位运算符的特殊用途
6.2.1 与
⑴清零 A数中为1的位,B中相应位为0。然后使二者进行&运算,即可达到对A清零目的。
⑵取一个数中某些指定位 取数A的某些位,把数B的某些位置1,就把数A的某些位与1按位与即可。
⑶保留一位的方法 数A与数B进行&运算,数B在数A要保留的位1,其余位为零。
判断奇偶性将变量 a的奇偶性。a与1做位与运算,若结果是1,则 a是奇数;将 a与1做位与运算,若结果是0,则 a是偶数。
6.2.2 或
或运算可以用来置位。
6.2.3 异或
⑴使特定位翻转 要使哪几位翻转就将与其进行∧运算的该几位置为1即可。
⑵与0相∧,保留原值.
⑶交换两个值,不用临时变量.我们可以在不用引入其他变量就可以实现变量值的交换用异或操作可以实现:
a = a^b;
b = a^b;
a = a^b;
//异或操作满足结合律和交换律,且由异或操作的性质知道,对于任意一个整数a^a=0;
//证明:a=a^b=a^(a^b)=b;
//b=a^b=(a^b)^(a^a^b) = (a^a)^a^(b^b) = a;
6.3 左移右移(这标题使我想起了一个烂笑话→_→,日本首相佐藤右藤)
左移运算符“<<”是双目运算符。其功能把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。左移n位就是乘以2的n次方(有符号数不完全适用,因为左移有可能导致符号变化)。
右移运算符“>>”是双目运算符。其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。右移n位就是除以2的n次方。对于有符号数,符号位向右移动后,正数的话补0,负数补1,对于有符号数,在右移时,符号位将随同移动:当为正数时,最高位补0,而为负数时,符号位为1,也就是汇编语言中的算术右移。
7 加加减减++--操作符
利用笔试题来学习一下吧。
①
j=(i++,i++,i++);//逗号表达式,i遇到每个逗号后,认为本计算单位已经结束,i这时候自加。
//华丽的分割线,下面是另外一题
int x;
int i=3;
x=(++i,i++,i+10);//运行后x的值是15.
小结:遇到每个逗号后,认为本计算单位已经结束。
②
for(i=0;i<10;i++)
{
}
//运行后i=10.i和10进行比较后认为,计算已经结束了,所以进行自增操作。
③
int i=3,a=0;
a=(++i)+(++i)+(++i);
printf("a=%d\n ",a);
//运行结果是,a=18,i=6;
//华丽的分割线
int i=3,b=0;
b=(i++)+(i++)+(i++);
printf("b=%d\n",b);
//运行结果是,b=9,i=6;
小结:遇到分号才认为本计算单位已经结束。
8 分解符号的申请——贪心法
处理神器贪心法,每一个符号应该包含尽可能多的字符。
编译器的处理步骤:从左到右一个个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符时,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续已经读入下一个字符,重复上述判断,直到读入的字符组成的字符窗已不再可能组成一个有意义的符号。
例子程序:
int a=2,b=3,c=0;
c=a+++b;
printf("c=%d\n",c);
//c=5,a=3,b=3
9 容易出错的优先级
图1 容易出错的优先级(文献1)
参考文献
[1]陈正冲. C语言深度解剖[M]. 第二版. 北京: 北京航空航天大学出版社,2012: 50-66.
[2]维基百科. 国际C语言混乱代码大赛. http://zh.wikipedia.org/wiki/%E5%9B%BD%E9%99%85C%E8%AF%AD%E8%A8%80%E6%B7%B7%E4%B9%B1%E4%BB%A3%E7%A0%81%E5%A4%A7%E8%B5%9B,2014-1-8.
[3]百度百科. 转义字符. http://baike.baidu.com/link?url=KnYnuaWsbM4y10KJYHQZOVoVPOm52rWVgbfAOSkdO_ep5zaVxEByBX2ISHIGd_rx,2014-1-9.
[4]百度百科. 位运算符. http://baike.baidu.com/link?url=adjAAH0J64dOckmhGCL2PVqxiOT6YYjD4vDZLwXvOVCwC9Pz93W1kbLXtfUln6GApr9T1vLt7BLCvJuBHuMSVa,2014-1-9.