指针
一、指针的基本用法
1、指针变量定义
定义的格式:
类型名 *指针变量名;
int *p;
2、指针变量的引用
“&”取地址运算符,通过&运算符可以取出普通变量的地址【&内容变量=地址值】
“*”指针运算符,*可以取出指针变量所指向的普通变量的值,(间接引用普通量)【*地址变量 = 内容值】
指针变量的特殊意义:*p放在赋值号左边是写操作,如果放在赋值号右边是读操作
3、注意事项
1)可以通过赋值使一个指针变量“指向”某一普通变量(指针变量=&普通变量)
2)在C语言中正确的做法是先让指针变量指向一个确定的存储单元后,再通过该指针变量引用它所指向的存储单元
int *p; *p = 200; 此操作很危险,指针变量定义且初始化后才可以使用
3)变量名(普通变量、指针变量)都表示其存储单元内的值
4)若指针变量p指向变量a,即将变量a的地址赋给了指针变量p
int a=20,*p=&a; A、*p <=> a 等价都表示内容值 B、 p <=> &a 等价都表示地址值 C、&*p <=> &a <=> p 等价都表示地址值 D、*&a <=> *p <=> a 等价都表示内容值 E、(*p)++ a++ (*p)++ 表达式的值是*p的内容,此时p指针后移 (*p)-- a-- ++(*p) ++a ++*p ++(*p)表达式的值是*p的内容加1,同时p指针后移 --(*p) --a --*p
5)所有的指针变量在内存中分配的字节数相同
6)指针变量必须定义且初始化之后才可以使用
7)指针变量专门用存地址,禁止将一个整型值直接赋给一个指针变量
8)若数组做为形参,则将数组名做指针变量来处理
int fun(int a[10]) <=> int fun(int *a) = int fun(int a[])
二、指向数组的指针变量
1、指向数组元素的指针变量
int a[10], *p; p=&a[1];
2、指向一维数组的指针变量
int a[10]; int *p; p=a;
3、注意事项
1)在c语言中规定:数组名代表数组的首地址,而且是一个地址常量
2)当指针变量指向数组中的某一个元素时,指针变量加1后指向数组的下一个元素,指针变量减1时指向数组中前一个元素
3)当指针变量指向数组时,下标运算([])用于数组也可用于指针变量后
int a[N],*p=a; p+i <=> a+i <=> &a[i] *(p+i) <=> *(a+i) <=> a[i] <=> p[i] p++、 ++p <=> p+=1、 p=p+1
p-- 、--p <=> p-=1 、p=p-1 *p++ <=> *(p++) 先标地址,取旧地址内容值,地址向下加 *++p <=> *(++p) 先把地址向下加1,把移动后的内容值取出来 (*p)++; ++(*p) <=> ++*p 指针没有移动,但是指针所指的内容值加1 (*p)--;--(*p) <=> --*p
4)若两个指针变量指向同一个数组,则这两个指针变量可以进行大小比较
5)在形参中的数组实际上是一个指针变量,并不是真正的数组,因为该“数组名”的值是可以改变的,而真正的数组名的值是不能改变的
6)若形参是数组或指针变量,则在函数中可以通过该形参改变实参的值
三、指向多维数组的指针变量
1、指向多维数组元素的指针变量
int a[10][10]; int *p; p=a;
2、指向由m个元素组成的一维数组的指针变量
定义格式:
类型名 (*指针变量名)[m];
int (*p)[m];
3、多维数组元素的两种指针变量的定义格式
int *p; 列指针变量 int (*p)[m]; 行指针变量
4、注意事项
1)只有列指针才是“真正”指向元素,即指向某一个元素的存储单元
2)一维数组名表示的是列指针,二维数组名表示的行指针
3)注:若a是一个二维数组,则有:
a+i是行指针,即指向的是一整行,若对它加1则指向下一行 *(a+i)和a[i]一样,都是一个列指针即指向的是一个元素 *(a+i)+j和a[i]+j一样,都表示元素a[i][j]的地址,即与&a[i][j]等价。 *(*(a+i)+j)、*(a[i]+j)、(*(a+i))[j]和a[i][j]一样,都表示元素a[i][j]
4)注:
int w[2][3] *(w+1)[2]是元素,先计算[]后计算(),而[]是施加在()上
*(w+1)[2]表示的元素的值是w[3][0]
四、指向字符串的指针变量
字符串常量——C语言对字符常量是按首地址处理字符串常量
char str[]="abcd" <=> char str[]={"abcd"} char *p="abcd" ,不等价char *p={"abcd"} str="abcd" 不合法,常量不能放于赋值号左边 p="abcd" 合法
五、指向函数的指针变量
1、定义格式:
类型名 (*指针变量名)();
2、注意事项:
1)在定义指向函数的指针变量时,要注意有两个小括号必须要有,不需要定义形参
2)单独的函数名代表该函数的首地址(函数的入口地址)
3)函数的指针变量只能指向函数的入口处(函数的首地址)
4)给指向函数的指针变量赋值时,只写函数名即可,不必写参数
六、返回指针的函数
定义格式:
类型名 *函数名(形参列表)
{
};
七、指针数组
指针数组——一个数组的所有元素均为指针类型(地址)
定义格式:
类型名 *数组名[常量表达式];
int *p[M];
注意:
1)要注意它和定义指向由m个元素组成的一维数组的指针变量之间的区别
类型名 *数组名[常量表达式];
int *p[M];
类型名 (*指针名)[常量表达式m];
int (*P)[M];
2)它的每个元素都是一个指针类型(地址),即它的每个元素都相当于一个指针变量
代码实现的过程:
/* 2017年6月30日19:25:12 功能:指针函数和指向函数的指针 */ int add(int x, int y) { return x+y; //return返回调用函数最终的计算结果 } # include <stdio.h> int main(void) { int x, y; int total1, total2, total3; int (*p)(int x, int y); printf("Input x & y;"); scanf("%d%d", &x, &y); p = add; //p指向函数add; total1 = add(x, y); total2 = (*p)(x, y); // *p必须带()否则会出错; total3 = p(x, y); //p指向函数add; printf("1. %d + %d = %d ", x, y, total1); printf("2. %d + %d = %d ", x, y, total2); printf("3. %d + %d = %d ", x, y, total3); return 0; } /* 总结: 1.定义函数指针之后,必须首先将一函数名(代表该函数的入口地址)赋给函数指针,然后才能通过函数指针间接调用这个函数。 函数指针变量名 = 函数名; 在C语言中,函数名本身就是指向该函数的指针,因此可以用来函数指针进行赋值。函数名虽然是指针,但它是指针常量而不是指针变量,因而不能改变它的值。 2.在利用函数指针来间接调用其所指向的函数时,该函数的定义必须存在,否则将出现错误。 3.一个函数指针既可以指向用户自定义的函数,也可以指向C语言的标准函数。 在VC++6.0中显示的结果: ---------------------- Input x & y;11 11 1. 11 + 11 = 22 2. 11 + 11 = 22 3. 11 + 11 = 22 ---------------------- 注意: 1、C语言中, 函数名本身就代表着该函数的入口地址。通过这个入口地址可以找到该 函数,该入口地址称为函数的指针。我们可以定义一个指针变量,使它的值等于函数的入口地址, 那么通过这个指针变量也可以调用此函数,这个指针变量称为指向函数的指针,简称为函数指针。 2、定义形式如下: 数据类型 (*函数指针名) (形参); 3、指针函数与指向函数的指针是两个完全不同的概念,前者是函数,后者是指针,但是,指针函数的原型与函数指针的定义在格式却很相似,仅差一对圆括号。 例如: int (*fun) (int x, int y); fun是一个指向函数的指针变量,所指函数返回值的类型为int类型,所指函数有两个整型参数。*fun两侧的圆括号不能省略,fun先与“*”结合,表明fun是指针变量;(*fun)后面的(int x, int y),表明指针fun指向函数,所指函数有两个整型参数。(*fun)前面的int,表明fun所指函数的返回值的类型为int类型。 int *fun(int x, int y); fun是一个指针函数,该函数返回值的类型为“int *”类型,该函数有两个整型参数。fun先与其后的(int x, int y)结合,表明fun是一个函数,该函数有两个整型参数;fun前面的“int *”,表明函数fun的返回值是int类型的指针。 */
八、指向指针的指针变量
指向指针的指针变量——用来存放指针变量地址的指针变量
定义格式:
类型名 **指针变量名;
int **p;
九、空指针
指针变量可以有空值,即指针变量不指向任何变量,不指向任何有用的存储单【在系统中已将null定义为0,即null的值为0】
int *p=null; 此时p的值为空指针,即p不指向任何有用的存储单元,尽管null的值为0。 但我们不能认为p指向了地址为0的存储单元。
十、小总结
1、c语言有两种变量:
变量(普通变量)存储内容值,常量都是内容值,只能存放在普通变量中;
地址变量(指针变量)存储地址值;
2、*的三种用法:
乘法运算符
定义指针变量的标志
指针运算符
3、三种运算符之间的关系
&与*互逆
*与[]等价
&与[]互逆
代码分析解释:
1、由于指针所指向的内容是地址, 所以指针的运算实际上是地址的运算。指针有它自己特有的运算规律,与一般的整数是有区别的。 2、间接存取运算: & 取地址运算符 * 取值运算符 3、二者可以看作一对互逆运算符。在指针定义的时候“表示”“指向”, 在使用指针运算的时候,“*”表示取该指针变量所指向变量的值。 例如: int n =2, * p; p = &n; &(*p) 等效于p, (因为p = &n,则*p = n, 则&(*p) = )其结果为(*p)的地址,即n的地址; *(&n)等于n, 即地址(&n)所存放的值, 其结果就是2。 4、在进行指针运算时,要注意p = &n 与 *p = n 这两个表达式的区别: p = &n :是把变量n的地址赋给指针变量p,从而使p指向n,这时*p和n取值相同。 *p = n :是将变量n的值赋给p当前所指向的变量。(因p指向变量n,实际上就是将变量n的值赋给其自身,如*p=3 是将3赋给n) 所以应严格区分p、 *p、和&p三者的区别: p:是指针变量,其内容是地址量。 *p:是指针变量所指向的变量,其内容是变量的值。 &p:是指针变量本身所占据的存储地址。
4、二维数组的关系图
5、指针类型的几种定义
类型名 (*指针变量名)(); ——定义指向函数的指针变量
类型名 普通变量名; ——定义普通变量
类型名 数组名[]; ——定义数组变量
类型名 *指针变量名; ——定义指针变量
类型名 函数名() ——定义用户自定义函数
{....}
类型名(*指针变量名)[M]; ——定义行指针变量
指针的一些含义:
# include <stdio.h> void main(void) { int n = 5, * p; p = &n; printf("%d ", &n); printf("%d ", p); //p = &n; printf("%d ", &(*p)); //p = &n,则*p = n,即&(*p) = &n; printf("%d ", *(&p)); //【个人理解:&p表示指针变量p的地址,则*(&p)=p】 /* 以上四个表达式输出的是同样的值,都是n的地址值 */ printf("%d ", &p);// &p表示指针变量p的地址; /* 该表达式输出的是p变量的地址值 */ printf("%d ", n); printf("%d ", *p); // *p = n; printf("%d ", *(&n)); *(&n) = n; /* 以上三个表达式输出的是同样的值:5 */ }