C和指针:第五,六章

第5章

1.  位移

逻辑位移与算术位移

逻辑位移:左边移入的位用0填充

算术位移:左边移入的位由原先该值的符号位决定,符号位为1,则移入位均为1,符号位为0则移入位均为0。

无符号做移位操作时都是逻辑移位,对于有符号的移位是逻辑移位还是算术移位,取决于编译器。所以,如果是有符号的移位操作,它是不可移植的。

如果移位的位数比操作数的位数还要多,移位的结果是未定义的。

 

2.  复合赋值符的优点在于使源代码简洁,易阅读和易书写。

 

3. 单目运算符有:(), !, +, –, ++, –, sizeof, ~, &, *

 

4. 关系运算符有:>, >=, <, <=, !=, ==

 

5. 逻辑运算符有:&&, ||,&&运算时会有“短路求值”

 

6. 条件运算符有: ? :

 

7. 逗号运算符:表达式的值是最右边表达式的值

 

8. 布尔值:0值为假,非0值为真。

    避免混合使用整形值 与布尔值,如:

    #define FALSE 0

    #define TRUE  1

    if (flag == TRUE) …

    if (flag) …

    不要与任何特定值进行比较测试变量是否为值,哪怕是TRUE或FALSE。相反,应当像下面这样测试变量:

    if (positive_cash_flow) …

    if (!postive_cash_flow) …

 

9. 左值与右值:能出现在赋值符号左边的就叫左值,能出现在赋值符号右边的就叫右值。

    表达式不一定就是右值,有时也可做左值。如下标引用可以是表达式,而且也可做左值。

 

10. 类型转换

    1.) 整型之间运算时做类型的提升。

    2.) 浮点型转换为整型时,小数部分被舍弃,而不是四舍五入。如果浮点数的值过于庞大,无法容纳于整型值中,结果是未定义的。

    3.) 有时类型转换的结果会有机器平台有关,将32位系统上的运算拿到16位系统上,如果没有做强制的类型转换,数据将有溢出的危险。

 

11. 操作符的属性

      复杂表达式的求值顺序由3个因素决定:操作符的优先级,操作符的结合性以及操作符是否控制执行的顺序。两个相信的操作符哪个先执行取决于运算符的优先级,如果两者的优先级相同,那么取决于结合性(是就从左到右或是从右到左)。如果表达式中出现逻辑运算符,条件运算符或是逗号运算符时,可以对表达的求值顺序施加控制,它们或者保证某个子表达式能够在另一个子表达式的所有求值完成之前进行求值,或者可能使某个表达式被完全跳过不再求值。

 

由于表达式的值并非完全由操作符优先级决定的,所以,应避免书写类似下面的语句:

c + –-c;

因为加法操作符无法得知加号左边的c是在加号右边的c操作之前还是之后进行求值。--c在c之前或之后执行,表达式将有两种不同的结果。

 

如果顺序会产生区别,最好使用临时变量,让每个函数调用都在单独的语句中进行

 

 

 

 

第6章   指针

 

1. 指针的初始化和非法的指针

和变量一样,指针变量如果是静态的,初始化为;如果是自动的,不会被初始化。由于这未初始化的指针还有可能会指向一块已知的内存地址,如果此时对指针做解除引用操作,并赋值,此时内存块的数据被修改。然而,这并非你的本意。此类bug非常难以捕捉。所以,在指针进行解除引用操作时,要确保已被初始化

 

2.  NULL指针

标准定义NULL指针,为一个指向任何东西的指针。如果要让一个指针变量为NULL,可以给他赋一个零值。

NULL指针通常用来做条件表达式的值,比如用于查找数组中某个特定的值,可能返回一个指向查找到的数组元素的指针。如果该数组不包含指定条件的值,函数就返回一个NULL指针。当然,这样做带来的危险是,一个单一的值不胜代表了两种不同的意思,它违背了软件工程思想。解决办法是,让函数返回两个值,一个是状态值,一个是指针。状态值判断是否查找成功,如果成功就指向它所查找到的元素。

 

对一个NULL指针进行解除引用操作是非法的。

如果你的指针知道将被初始化到什么地,就应该将它初始化,否则就初始化为NULL。风格良好的程序会在指针解除引用之前对它进行检查。

 

3.  指针表达式,如果有:

    char ch = ‘a’;

    char *cp = &ch;

 

    1.) &ch,做右值时,代表变量ch的地址; 不能做左值

    2.) cp,做右值时,代表cp的值,做左值; 代表cp的内存空间。如:cp = ‘e’;  char temp = cp; 

    3.) &cp,做右值时,代表cp的地址; 不能做左值

    4.) *cp,做右值时,代表cp所指向地址的值; 做右值时,代表ch内存空间

    5.) *cp + 1,做右值时,代表所指向地址的值,再加1; 不能做左值

    6.) *(cp + 1),做右值时,代表ch下一个地址的值; 做左值时,代表ch下个内存空间。在这里这个内存空间是未定义的,所以它的值也是未定义的。

    7.) ++cp,做右值时,先对指针cp指向地址加1,这里是ch后面的内存空间,再将这个值拷贝另外一份指针,此时指针的拷贝内存空间是未定义的; 不能做左值

    8.) cp++,做右值时,先对cp指针做拷贝,再对指向cp地址后面的内存空间,此时指针拷贝的内存空间和cp指向的内存空间都是未定义的不能做左值

    9.) *++cp,做右值时,代表ch下一个地址的值;做左值时,ch下一个的内存空间。

    10.) *cp++,做右值时,先拷贝一份cp,再取cp指向地址的值,这里是ch;做左值时,先拷贝一份cp,再代表ch的内存空间(简单归纳就是,右值结果为ch的值,左值结果为ch的内存空间)

    11.) ++*p,做右值时,先取cp指向地址的值,再将该值加1;不能做左值

    12.) (*cp)++ 做右值时,先取cp指向地址的值,再将该值加1,但返回的值是ch之前的值不能做左值

    13.) ++*++cp 做右值时,这里的操作符顺序都是从右到从的,先对cp指向的地址加1,并拷贝这个指针,再取拷贝指针指向地址的值,这里是ch后面的地址空间的值,最后对这个值再加1,这里的值是未定义的,因为cp指针拷贝的内存空间是未定义的;不能做左值

    14.) ++*cp++ 做右值时,先拷贝cp指针,再将cp指向地址加1,再取拷贝指针指向地址的值,并对这个值加1;不能做左值

 

4. 指针运算

1.) 算术运算只限于两种形式:

    a. 指针  ± 整数

       当一个指针和一个整数常量执行算术运算时,整数在执行加法运算前始终会根据合适的大小进行调整。这个“合适大小”就是指针所指向类型的大小,“调整”就是把整数值和“合适大小”相乘。

    这种形式的指针算术运算只限于操作数组元素。如果运算后的结果指针指向数组以外的内存空间,其结果是未定义的。

    b. 指针 - 指针

        只有两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针。其结果的类型是ptrdiff_t,这是种有符号的整型。其值是两个指针的内存距离,它与数组类型无关,并且可以是负数。如果两个指针指向不同数组,做减法时,其结果是未定义的。

 

2.) 关系运算

    任何两个指针之间都可以执行<, <=, >和>=等关系运算,如果两个指针同时指向同一个数组中的元素时,这两个指针所做关系运算用来判断数组元素的先后关系。

posted @ 2010-11-14 17:45  jeff_nie  阅读(232)  评论(0编辑  收藏  举报