前言:为了准备就业时的专业笔试,自己打算将笔试常考的知识点重新复习一下,这里面还涉及到一些自己新学习的东西,幸好笔试的考点基本上比较固定。我计划按照这样的序列开始准备:C语言拾遗——数据结构与常见算法(重点)——面试考点(C/C++,数据库,数据结构,网络等)。今天的这是其中的第一部分。
C语言拾遗的主要材料是Alan R.Feuer的《C语言解惑》一书。
2012.11.5
*******************************************************************************
*******************************************************************************
第一章 操作符
这部分的内容其实比较简单,主要有以下几个方面需要特别注意的:
? 操作符的优先级与同级关联性
? 增量运算符++的前后缀区别
? 二进制位操作符
? 逻辑操作表达式的求值顺序
下面分别小结。
1. 操作符的优先级与同级关联性
这里只需要掌握优先级表即可,有高到低依次为:
? 关联操作符:如(), [], 以及由结构取部分的操作符->与'.'
? 一元操作符:逻辑非(!)正负号(+、-),++/--(自增/减运算),地址操作(&,&),强制类型转换(type)以及字节大小运算(sizeof)
? 四则运算:其中乘除余同级,高于加减运算
? 移位操作:<<,>>
? 关系操作符:其中小于(等于)和大于(等于)同级,高于“相等”比较(==, !=)
? 位操作符:按位与(&) > 按位异或(^) > 按位或(|)
? 逻辑操作符:逻辑与(&&) > 逻辑或(||)
? 条件操作符:a ? b : c
? 赋值操作符:+=, =, <等
? 逗号:','
? 关联性:除了一元操作符、赋值运算符与条件运算符是从右向左,其余全是从左向右关联
Exa-1::int x; x = - 3 * 4 % - 6 / 5; 则 x =
PS::x = (- 3) * 4 % (- 6 ) / 5 ,从左至右运算即可得x = 0
2. 增量运算符++/--的前后缀
++x:则x先自增1后作为操作数与操作符结合进行运算;
--x:x先作为操作数与操作符结合进行运算后,再自增1;
Exa-2:int x = 1, y = 1, z = 1; z += - x ++ + ++ y; 则x = ,y =
PS:x用1参与运算,y用2参与运算,之后x = x + 1;此题目x = 2, y = 2, z = 0;
3. 二进制位操作符
这里关键是根据定义规定将变量(整数)转变为二进制进行运算,注意优先级即可。
对于左移操作,如x <,表示x的各位依次向左移动3位,右边空出的位用0补齐;
而对于右移操作,如x >> 3,左侧空出的部分可能保留符号位(负数时为1),也有可能直接补0,这取决于不同的机器。
另: x ^ x = 0;
4. 逻辑操作符表达式的求值顺序
对于&&,||而言,计算机从左向右判断时,一旦可以判断整个表达式的值,便立刻停止计算,这会导致后续的部分没有机会运行。如:
Exa-3:int x= 1, y = 1, z = 1; ++x || ++y && ++z; 则x = , y = , z =
PS:原式等价于 (++x) || (++y && ++z),C语言按照从左向右的顺序对表达式求值,计算++x后发现式子变为TRUE || (++y && ++z),根据逻辑或规则,结果一定为TRUE,故C语言不再向下运行++y与++z,即y与z的值没有改变。
5. 其他一些补充
补充1:C语言的副作用。有时候语法没有错误,但是结果却不确定,如变量的值无法预料的情况—— z = (x / (++x) )
补充2:'#'号宏
#define PRINT(int) printf(#int " = %d\n", int) 这里的'#'使得实际参数替换形式参数'int'后被括在一对""中。
如
PRINT(x) printf("x" " = %d\n", x) printf("x = %d\n", x)这里C语言处理器会自动将相邻的字符串合并
补充3:重要的原则——编写程序时涉及到复杂的操作符表达式时尽可能使用()来表示结合顺序!
第二章 基本类型
要点:
? 字符类型与整数类型的关系在于ASC码值为整数值,如何解释取决于格式控制
? 浮点数向整数类型转换时会丢失精度,比如失去小数部分;
? 重要的原则——应当尽量避免不同类型数据进行运算;如不能避免,也应当使用强制类型转换符予以标明强调!
第六章 存储类
C语言中的变量有三个基本的属性:
? 类型:决定了该变量获得的存储空间大小以及允许的操作
? 作用域:说明该变量在程序的哪些上下文中可见(可用),常见的作用域边界有块、函数与文件
? 生存期:说明该变量的值有效的时间区间,只有在这个区间内该变量的值才是有意义(存在)的
这里最需要注意的是同一个程序中的同名变量的替换规则,其实该问题笼统来说就是:不同层次(块/函数/文件)中的同名变量引用会发生内层变量对外曾变量的替换。若变量用extern声明,则在该程序的作用域是该程序所有文件,且生存期同该程序相同;若变量用static声明,则从当前文件中该声明开始以下部分为该变量的作用域,生存期同该程序运行一致(同extern变量);每个函数的调用参数都是优先选用同层的变量,没有时才会使用更外层的可见变量。
Static + 局部变量:改变其生存期至整个程序,作用域限定在本文件;
Static + 全局变量:生存期不变,作用域限定在本文件内部,其他文件不可访问;
如程序:
+
头文件略与函数声明略;
Int i = 1;
Main()
{
Auto int i, j;
I = reset();
Printf("i = %d\n", Next());
}
另一个文件:
Static int i = 10; //Static变量只可在本文件中的Next()函数中可见
Next(void)
{
Return ( i +=1);
}
再另一个文件中:
Extern int i;
Int reset(void)
{
Return (i);
}
第七章 指针和数组
指针的要点很多也很繁琐,稍不留意就会弄混。这次看书搞懂了之前模糊的两个知识点:
? 数组形式Array[n]中,Array[n] = *(Array + n),其中n为数组的元素下标,具体计算地址时是n*sizeof(Array的基类型)
? 指针的指针。如定义char **p,则可以这样理解:(char *) (*p),其中*p表示p是一个指针变量,而(char *)则表示为这个指针变量的基类型是一个char型的指针
? C语言不会自动检查指针所指的边界,即如不加限制条件,*(p ++)会一直偏移下去导致溢出