指针
物理内存中,每一个字节都通过一个地址来标识。
为了存储更大的值,将两个或者更多个字节合在一起作为一个更大的内存单位。
不同的机器上,数据的存储方向或许不同。
一个包含了多个字节的数据类型,要注意它的地址是它最左边的位置还是最右边的位置开始。
当一个32位(字)的机器保存8位(字节)的数据,就会有24位空余下来,机器读取这个8位数据从32位的左边或者从右边开始,将会影响最终读取到的值。
- 变量名称和内存存储位置之间的关联是由编译器来决定的。这就意味着,不同的编译器或许会存在不同的标准。
- 对于编程者来说,在程序中通过变量名称来访问该变量,到硬件上来说,是通过地址来访问内存位置的。
- 一组保存在内存中的数值,仅仅是数值。采用不同的解释方式,获得不同的结果。本质上是对一组0和1进行解释,解释的方式取决于它们的使用方式。
->因此不能简单地通过一个值的位来判断他的类型。必须观察这个值的使用方式。
- &->取地址;*->解引用
- 要记住,指针变量的值就是一个数字。
- 使用指针的时候,要注意两类错误:未初始化的指针和非法的指针。
- 指针变量和其他的并没有区别,如果是静态的,就会被初始化为0,如果是自动的,那么这个变量不会被初始化。
- NULL指针,可以用来表示指针此刻并未指向任何东西。在查找某个值的函数中,使用这个NULL技巧让一个指针传达两个信息:是否存在这个值(存在和不存在),如果存在,那么是那个元素?
- 有一种策略,更为保险。让函数返回两个独立的值:状态值和指针。
- 在创建一个指针的时候,初始化成某个值或者NULL。
- 在对指针进行解引用之前,最好对它检查一番。
- *操作符具有从右向左的结合性(当你遇到指针多层嵌套的时候)
- 对于*和++、--这类符号的结合,要注意,值发生的变化和表达式最终返回的值是不一样的。
例如,*--a,这里,a自减,之后解引用,a的值-1了,返回给解引用*的值也-1了。
但是,*a--,这里,先返回给解引用a的值,之后a的值再自减,这里给*的值和a本身的值就不一样了。
总结来说,指针本身也是保存值的,但是这个值,是程序中某个变量的地址。
函数
- 当程序调用一个无法看到原型的函数时,编译器便认为该函数返回一个整型值。
- 函数的定义同时描述了函数列表和函数体。
- 变量取决于读取的形式,采用不同的数据类型读取方式,所获得的值也是不同的。
- C中所有的参数都是传值调用,也就是说,调用的函数所获得的是参数值的拷贝。这样能够有效防止修改程序的实际数值。
- 传递指针的时候,是传址调用,通过指针内保存的地址,传递给函数的是参数值的地址,函数通过间接访问的方式,实际上修改了参数的值。
- 函数的执行顺序
程序的执行顺序是十分重要的,在编写调试过程中,经常调整语句顺序来实现功能。很多细节的顺序都会影响到功能的实现和程序的性能。
在C与指针中,介绍了递归和迭代。
在以前读过的C语言书中,经常用斐波那契数来讲递归,然而这种方式是错误的,首先斐波那契数本身是一个复杂的数学概念,无形中提高了理解代码的门槛。第二,使用斐波那契数并没有给程序更好的性能。
在学习新事物的时候,我们需要一步一步进行,而不是想着一步登天,基础是很重要的东西,即使你理解了复杂的题目,但是基本的概念不清楚,你也只是搞定了一个特例而已。所以要沉下心,耐心的去理解底层的定义和架构。
递归
- 包含两个条件,第一是结束条件,第二是执行体。
- 每一次执行了递归函数,都希望它能够更靠近限制条件,直到最后把被限制,再一层一层跳出。
- 在内存中,堆栈结构很适合递归的调用。每一次新的调用,都将创造开辟新的,调用结束返回的时候,逐层释放。
在递归调用的时候,需要注意递归和一般语句之间的顺序,当你的程序是需要随着递归的顺序来确定顺序时,就需要确定你是要保存递归调用之前的数据还是递归调用之后的。
- 递归调用在使用递归创建堆栈的时候,能够短暂保存局部变量的值(在堆栈中)。
可变参数调用
- 可变参数调用要使用stdarg宏来实现,调用#include。
- 一个类型va_list和三个宏va_start、va_arg、va_end.
- 在调用的时候,这些宏无法判断函数传入的参数的类型和数量。