第7章 指针
7.1 指针的概念和定义
1. 指针的概念:指针就是内存地址,可以是简单变量、复杂变量、函数等的内存地址。
2. 指针的定义:int * yptr;yptr为指向整型变量的指针。
7.2 指针运算
7.2.1 指针运算符
1. 地址运算符&:返回变量在内存中的首字节的地址,单目运算符,优先级很高,右结合性。
2. 间接引用运算符*:又称复引用运算符,作用在指针变量和内存地址上,返回指针所指向的对象的值,单目运算符,优先级很高,右结合性。
3. 要注意的问题
1) &和*对指针变量有互补性。
2) 指针变量可以赋值为0和NULL,表示不指向任何对象。
(1) NULL为头文件stdio.h中定义的符号变量。
(2) 指针变量除了赋值为0和NULL外,只能赋值为地址。
(3) 用NULL更好一些。
3) 不同类型的指针变量之间不能相互赋值。
4) 指针变量指向未定时,不能对其所指内存位置输入值,否则可能会破坏系统的重要数据。
7.2.2 指针作函数参数
1. 把函数的形参定义成指针类型,而将实参也定义为指针类型或变量的地址,这样就可以实现“传址调用”
7.2.3 最低访问权原则和const常量限定符
1. 用于传值调用
1) int i=1:不做const常量限定,可以任意改变i的值。
2) const int i=1:做const常量限定,i的值不能变化。
2. 用于传引用调用
1) int * i=&j:不做const限定,可以任意改变指针i及其所指内存变量的值。
2) const int * i=&j:指向常量的非常量指针。
3) int * const i=&j:指向非常量的常量指针。
4) const int * const i=&j:指向常量的常量指针。
3. 注意的问题:
1) const限定时,被限定的变量在定义时必须得赋值,不然后面就不能赋值了,编译也会出错。函数调用则也是一样的道理:在参数赋值时,即相当于定义时赋值,如函数void printa(int b[],const int s)。
7.2.4指针运算
1. 指针加、减运算
1) 指针的加、减运算的前提条件是指针已经指向一个数组,否则运算是无意义的。
2) 对指针进行加、减运算,根据指针指向数据的类型,来确定指向移动多少字节。同样数组中下标的加减和数组地址,也根据数组元素的类型,来确定移动多少字节,如:a[i+1]、&a[0]+1。
3) 如果两个指针指向同一个数组,则它们之间的减法运算,代表两个指针指向的对象之间的位置差距。位置差距默认指的是数组元素的间隔,而不是字节的间隔,如果要求字节差距,可在指针变量或地址变量前加(int),将地址强制转换为整数变量。
2. 指针的比较运算
7.3 指针与数组
7.3.1 一维数组与指针
1. 一维数组的地址和指针的关系
1) 一维数组的数组名代表数组第一个元素的地址;当指针指向数组第一个元素时,指针变量和数组名是可以相互替换的。本质上来说,数组名就是指针,数组的方括号运算其实就是指针的加减运算。
2. 一维数组元素的表示方式
1) “数组名+下标”表示法
2) “数组名+偏移量”表示法
3) “指针名+下标”表示法
4) “指针+偏移量”表示法
3. 数组和指针作函数参数
1) 实参和形参全是数组名。
2) 实参是数组名,形参是指针。
3) 实参是指针,形参是数组名。
4) 实参、形参全是指针。
7.3.2 字符指针
1. 概念:指向字符变量的指针,字符串有两种实现方法:字符数组,字符指针。
2. 定义:char * str
3. 要注意的问题
1) 用字符指针实现字符串时,有两种赋初值的方法:
(1) char * str=“abcdef”
(2) 字符指针构成的字符串可以使用赋值号进行赋值,字符数组构成的字符串却不行:
[1] char * str;str=”abcef”,“abcef”代表字符串的首地址,也代表这个字符串!
[2] char * str1,* str2;str1=“abc”;str2=str1。
7.3.3 二维数组的地址及指针
1. 二维数组的地址
1) 列地址:数组元素的真实地址,如a[O][3],a[3]等。
2) 行地址:数组的行的首地址,如a,a+1等;
3) 列地址与行地址的关系:*(a)=a[0]=&a[0][1]
4) 要注意的问题:
(1) 二维数组名代表的是首行的地址,是行地址。行地址比列地址高一级别。
(2) a[0]/a[1]/a[2]代表的第0、1、2行的首地址,是列地址,也可以代表第0、1、2行这个数组的名字。
(3) &a[1][2]+1表示&a[1][3],与一维数组加减法相同。
(4) &a[i][j]=&a[0][0]+i*n+j,二维数组有n列。
2. 用列地址、行地址表示数组元素
1) 用列地址表示数组元素:*(a[1])=*(&a[1][0])=a[1][0]
2) 用行地址表示数组元素:*(*(a))=*(&a[0][0])=a[0][0]
3. 指向二维数组的指针
1) 二维数组的列指针:
(1) 概念:与一般指针是一样的。
(2) 定义:int * p,*l;p=&a[2][3];l=a[0]。
2) 二维数组的行指针:
(1) 概念:用行指针来处理二维数组是,行指针定义的长度应等于二维数组的列数。
(2) 定义:int (*p)[4],a[3][4];p=a
4. 行指针作为函数的参数
1) 利用函数对二维数组进行处理时,可以以二维数组名作为实参传递给函数,函数以行指针为形参来接收它。
7.3.4 由指针组成的数组
1. 定义:如int * a[3];表示3个指向整型数据的指针数组。
7.3.5 二级指针
1. 概念:将地址的地址放入某个变量中,则这个变量就是指向指针的指针变量,因而被称为二级指针。
2. 定义:如:char * * p,*pname[2]={”abc”,”def”};p=pname;p为二级指针。
7.3.6 二维数组处理小结
7.3.7 main函数的参数
1. 当操作系统调用main函数生成的执行文件时,可能要带参数,这个参数也是main()函数的参数。
2. int main(int agrc,char * argv[])
1) argc[]:字符指针(字符串)数组代表操作系统输入的命令,如 append file1 file2,程序不能对可执行文件名argv[0]进行引用。
2) agrc:argv数组的元素个数。
7.4 指针与函数
7.4.1 指向函数的指针
1. 函数指针的定义与引用
1) 函数指针定义的一般形式:如int (* p)(int,int),p为函数参数为俩整型数据的函数指针。
2) 函数指针的引用,将函数名赋值给函数指针,函数指针和函数名等价。
3) 需要注意的问题
(1) 函数名代表存放函数代码的内存首地址,即函数入口地址。对函数名使用加减法无意义。
(2) 函数指针只能指向同一类的函数。
2. 函数指针作函数参数
7.4.2 返回指针的函数
1. 返回指针类型的函数的一般形式:int * p(int,int)
7.4.3 从被调函数中获取数据的渠道
1. return
2. 全局变量
3. 传址调用
4. static变量;return 静态变量,return的不是copy,而是静态变量本身,相当于传址调用。
7.4.4 悬空引用
1. 概念:函数被调用后,系统会给其定义的变量分配临时空间,而一旦调用结束后,临时空间会被收回,而如果把变量地址返回给主函数,主函数在使用引用该地址变量时,就会出现悬空调用。
2. 预防悬空调用:
1) 将变量设置为static变量,调用结束后,临时空间不会被收回。
2) 在主函数定义变量,然后传址调用。
7.4.5 二级指针做函数参数
7.5 多级指针
7.5.1 多级指针的定义
7.5.2 多级指针与多维数组
1. 多维数组
用指针处理多维数组