C和指针读书笔记(5-8章)

第五章

1.可以利用右移操作来计算一个数值中含有1的位的个数。Value = value >> 1;value % 2 != 0;后面这条语句还可以写成(value & 1) != 0;

2.可以用value = value | 1 << bit_number来对对应的bit_number位置一;同理,可以通value = value & ~(1 << bit_number)来对对应的bit_number位清零。

3.&操作符产生它的操作数的地址。*操作符是间接访问操作符,与指针一起使用,用于访问指针所指向的值。Sizeof操作符判断它的操作数的类型长度,要字节为单位表示。(类型)被称为强制类型转换符,用于显式的把值转换为另外的类型。

4.养成一个习惯,当你进行相等性的测试比较的时候,你要检查一下你所写的确实是双等号。

5.&&||操作符均遵循“短路求值”的原则,即当符号左侧的值已经满足条件的时候,就不再对右侧表达式的值进行求值。

6.当你使用If语句的时候,不妨尝试采用条件操作符,例如

If ( a > 5 )

b = 3;

else

b = -20;

可以写成: b = a > 5 ? 3 : -20;是不是代码量更小了呢?

7. .操作符合->操作符的区别,当你拥有的是一个结构体变量s,那么s.a就是访问这个结构体中名为a的结构体成员。如果你拥有的使一个指向S的指针p,那么你访问a的时候就要用p->a才是正确的。

8.避免混合使用整型值和布尔值,如果一个变量包含了一个任意的整型值,应该现实的进行测试,而不是简写测试时非零还是零。如果是布尔类型,必须是0或者1.

9.要注意有符号数字的移位操作。

 

第六章(指针)

1.关于边界对齐,在要求边界对齐的机器上,整型值存储的起始位置只能是某些特定字节,通常是2或者4的倍数。

2.内存中的每个位置由一个独一无二的地址标识,内存中的每个位置都包含一个值。

3.变量与内存位置之间的关联并不是硬件所提供的,它是由编译器为我们实现的。

4.变量的类型取决于它被使用的方式,因此不能简单地通过检查一个值的位来判断它的类型。

5.变量的值就是分配给该变量的内存位置所存储的数值,即使是指针变量也不例外。

6.一般的,未初始化的指针咋linux中最常见的会引起“segmentation violation”或者“memory fault”,有时还会引起“bus error”在pc端的windows中,会引起保护性异常(general protection exception)。

7.指针未初始化的最严重的情况就是:它非法的指向了一个有用的地址,不自觉的这个地址的值被改变了,这种Bug是非常难找的。

8.当一个指针被创建但是并不立刻使用的时候,需要将他赋值为NULL,可以使他返回的值有很多,

9.*100 = 25这样的操作是非法的,因为100是一个整型值,间接访问操作只能作用于指针类型的表达式,如果你确实想把25存储于位置100,必须使用强制类型转换,*int *100 = 25.虽然《C和指针》这本书里说使用这种表达式的情况绝无仅有,但是做嵌入式底层的,这个应该还是有用武之地的。

10.*操作符的结合性是从右往左,int **c相当于int*(*c)

11.指针和一个整数量执行算术运算的时候,整数在执行加法运算前始终会根据合适的大小进行调整,这个调整就是把整数值和“合适的大小”相乘,例如int占用4个字节,一个int类型的指针+4,实际上加到指针上的整型值为4*4=16.这样做在实际使用的时候是更加合理的。这和指针的类型占用4个字节冲突吗?

12.指针的算数运算只限于两种,第一:指针 +-整数;第二:指针 - 指针。前者一般适用于指向数组中某个元素的指针,也适用于malloc分配的内存。后者只能在指向同一个数组中使用。指针相减得到的是两个指针在数组中的位置的差值,而不是地址的差值。

13.NULL指针进行解引用操作的后果?加入一个指针int  *p;p=NULL.编译期会默认将NULL指针指向零地址,假如在0地址存放了有效的值,这样会误操作改变了这块内存的值。

 

第七章 函数

1.无返回值的函数叫做(过程类型函数),有返回值的函数叫做真函数。

2.函数的返回值是很重要的,所有的函数都应该具有原型,尤其是那些返回值不是整形的函数。当程序调用了一个无法见到原型的函数的时候,编译期便会认为该函数返回一个整型值。

3.关于函数的参数:

(1)C函数的所有参数均以“传值调用”方式进行传递;

(2)传递给函数的标量参数是传值调用的;

(3)传递给函数的数组参数在行为上就像它们是通过传址调用那样。(本质是一个指针,传递给函数的是指针的一份拷贝,下标引用实际上是间接访问的另一种形式)。

4.抽象数据类型——ADT(abstract data type),抽象数据类型的基本想法很简单,模块具有功能说明和接口说明,前者说明模块执行的任务,后者定义模块的使用。

5.C通过运行时的堆栈来支持递归函数的实现,直接或者间接的调用自身。理解了递归之后最容易的方法不是纠缠它的执行过程,而是相信递归会顺利的完成它的任务。

6.递归函数的参数必须压到堆栈中,为每次递归产生的局部变量分配内存空间,寄存器的值必须保存好,当每次调用返回的时候,这些操作必须还原,恢复成原来的样子。

7.有时候,迭代可能比递归好用。采用递归计算斐波那契数列,当n=10的时候,f(3)被计算21次,当n=30的时候,f(3)被计算317811次,开销非常大。而用迭代就可以避免这个问题。

8.Stdarg宏是可变参数列表宏,定义于stdarg.h头文件中,它是标准库的一部分,声明一个va_list和三个宏va_start,va_argva_end。在参数列表中至少要有一个明明参数。

9.整数和字符之间的转换可以借助’0’来实现,字符转化为整数a - ‘0’,整数转化为字符b + ‘0’

 

第八章 数组

1.数组名的值是一个指针常量,在两种操作符的情况下可以例外,一个是sizeof,一个是&.sizeof返回的是整个数组的长度,&是一个指向数组的指针。

2.C之所以不进行下标检查,是因为下标引用可以作用于任意的指针,而不仅仅是数组名。

3.加入有一个数组array[10],那么2[array]array[2]其实是等价的。

4.指针在某些场合具有比下标更好的效率,例如使用for循环对一个数组进行清零或者复制的时候,但是指针的使用往往出现劣质的代码,效率一定取决于好的代码。

5.声明为寄存器变量的指针通常比位于静态内存和堆栈中的指针效率更高。

6.注意字符数组和字符串常量的区别,char massage[] = “hello world”char *message = “hello world”的区别。

7.区分数组的首地址和数组首元素的地址,假如一个数组声明为int matrix[3][10],那么matrix+1其实指向的是第二行的首地址,而不是数组第二个元素的地址。第二个元素的地址应该是matrix[0][1];

8.在将二维数组的数组名作为参数传递给函数的时候,二维数组的声明如下:void func2(int (*mat)[array_length]).或者void func2(int mat[][array_length]);只有第一维可以省略,其他维必须显氏的写出来。

9.数组的下标引用的优先级要高于间接访问。

10.计算数组元素的个数可以采用 #define N_KEYWORD = (sizeof(array)/sizeof(array[0]))

你可能感兴趣的:(C语言,读书笔记,指针)