指针是C语言中的一个重要概念,也是C语言的一个重要特色。
其优点有:
ü 有效的表示复杂的数据结构;
ü 动态分配内存;
ü 方便地使用字符串;
ü 有效而方便的使用数组;
ü 在调用函数时,能得到多于一个的值;
ü 能直接处理内存,等等。
1.概念:
如果程序中定义了一个变量,在编译时就给这个变量分配内存单元。程序中一般通过变量名来对内存单元进行访问(其实程序经过编译已经将变量名转换为变量的地址),即通过变量的地址可以直接访问变量。
一个变量的地址就称为该变量的“指针”。如果有一个变量专门用来存放另外一个变量的地址,则它称为“指针变量”。指针变量的值是指针(地址)。
2.定义:
示例: int *pInt;
这里“*”表示变量的类型为指针型的变量,指针变量的变量名为pInt。
3.指针变量作为函数参数:可以改变实参的值
#include <stdio.h> void Swap_1(int *p1, int* p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } void Swap_2(int n1, int n2) { int temp; temp = n1; n1 = n2; n2 = temp; } int main() { int a,b; int *p1,*p2; printf("please input two number:/n"); scanf("%d,%d",&a,&b); p1 = &a; p2 = &b; if(a<b) Swap_2(a,b); printf("/n%d,%d/n",a,b); }
输入:5,9
调用Swap_1 输出 9,5
调用Swap_2 输出 5,9
4.数组和指针
示例:
int a[10];
int *p;
p=a;
则有:
*(p+5) = *(a+5) = a[5] = p[5];
*(p++) ó a[i++]
*(p--) ó a[i--]
*(++p)ó a[++i]
*(--p) ó a[--i]
5使用数组名作为函数参数
例如:
f(int arr[10], int n);
这里形参arry指定大小为10,实际上指定其大小不起任何作用,因为C编译器对形参数组大小不做检查,只是将实参数组的首地址传给形参数组。因此形参数组可以不指定大小
即:f(int arr[10], int n);óf(int arr[],int n);
形参只是用来接收实参传递过来的数组首地址,实际上,C编译器都是将形参数组名作为指针变量来处理的。
f(int arr[],int n); ó f(int *arr, int n);
形参数组为指针变量,不是常量,可以进行运算。
6.多维数组的地址(以二维为例)
int a[3][4];
表示形式 | 含义 |
a | 二维数组名,指向一维数组a[0],即第0行首地址 |
a[0],*(a+0),*a | 第0行第0列元素地址 |
a+1,&a[1] | 第一行首地址 |
a[1],*(a+1) | 第1行第0列元素a[1][0]的地址 |
a[1]+2,*(a+1)+2,&a[1][2] | 第1行第2列元素a[1][2]的地址 |
*(a[1]+2),*(*(a+1)+2),a[1][2] | 第1行第2列元素a[1][2]的值 |
|
|
这里a+1,*(a+1)的值相同,但含义不同。
a+1: 二维数组a中第一行的首地址。
*(a+1): 并不是a+1单元的内容,因为a+1并不是一个变量的存储单元。*(a+1)óa[1],指向a[1][0]
虽然a+1,*(a+1)值相同,但a+1+1,*(a+1)+1值却不相同。
7.指向多维数组的指针
#include <stdio.h> int main() { int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23}; int (*p)[4]; int i,j; p=a; for(i=0; i<3; i++) { printf("/n"); for(j=0; j<4; j++) { // printf("%.2d ",*(*(p+i)+j)); printf("%.2d ",p[i][j]);//这里两种用法效果是一样的 } } }
理解:
int a[4]: a有四个元素,每个元素为整数
int (*p)[4]: (*p)有四个元素,每个元素为整形。即p所指向的对象为有四个元素的数组,所以p为行指针。
注意:int (*p)[4],int *p[4],含义大不相同,前者为二维数组指针,后者则为指针数组。
8.指向函数的指针
定义示例
int max(int a,int b);
int (*p)();
p=max;
1. int (*p)()与int *p()大不相同,前者表示定义指向返回值为int的函数的指针,后者表示定义返回值为指向int型的指针的函数。
2. p=max是将max的入口地址赋给指针变量p,函数名代表函数的入口地址。
3. p是指向函数的指针变量,它只能指向函数的入口而不能指向函数中间的某一条指令。此时,p+n,p++,p—等计算是没有意义的。
4. 给函数指针赋值,只是将函数入口地址赋给函数指针,因此不必列出参数。
使用指向函数的指针作为形参,即可通过将函数名作为实参来调用函数。这在需要调用格式相同,功能不同的函数的时候尤为有效。
9.指针数组和指向指针的指针
int *p[4];//[]优先级比*高
p为一个数组,该数组中有4个变量,每个变量都是指向整型变量的指针。
指针数组尤为适合指向若干个字符串:
char *szName[]={“Pingping”,“Tom”,“Helen”};
这样比定义二维数组有优点:
1. 定义二维数组需要指定列数,每行的列数相等,而字符串长度往往不等,这样会浪费内存单元。而用指针数组,则可以分别定义字符串,然后将指针数组中的元素分别指向各字符串。
2. 排序操作时不需要移动字符串而只需要改变指针数组元素的指向(即其元素的值)。
char **p;
p=szName;
10.有关指针的数据类型的小结
定义 | 含义 |
int i; | 定义整型变量i |
int *p; | 定义指向整型变量的指针变量 |
int a[n]; | 定义整型数组,它有n个元素 |
int *p[n]; | 定义指针数组p,它由n个指向整型变量的指针组成 |
int (*p)[n]; | p为指向含有n个整型元素的一维数组的指针变量 |
int f(); | f为返回值为整数的函数 |
int *p(); | p为返回值为一个指针的函数,该指针指向整型变量 |
int (*p)(); | p为一个指向函数的指针,该函数返回一个整型值 |
int **p; | p是一个指针变量,它指向一个指向整型变量的指针变量 |
11.有关指针变量的运算
1. 指针变量加减一个整数:p++,p--,p+i,p-I,p+=i,p-=i…一个指针变量加减一个整数并不是简单的将该指针变量的原值加减一个整数。而是将指针变量的原值和其所指向的变量所占内存单元字节数进行加减。
2. 指针变量赋值:
p=&a;//将a的地址赋给p
p=array;//将数组array的首址赋给p
p=&array[i];//将array数组的第i个元素地址赋给p
p=max;//max为已定义的函数,将max的入口地址赋给p
p1=p2;//p1,p2都是指针变量,将p2的值赋给p1
不可以把一个整数想当然的作为地址赋给指针变量,使能将已经分配的地址赋给指针变量。
3. 指针变量可以有空值,即该指针变量不指向任何变量。
p=NULL;
NULL实际上就是0,系统保证该单元不存放有效数据。
指针赋空值不指向任何变量,为空指针;
指针不赋值则是一个无法预料的值,亦即指向一个不确定的单元,这就是一个野指针。
指针在使用过程中需要尽量避免出现野指针。
任何指针变量都可以与NULL作相等或不相等的比较。
4. 指针变量可以相减
指向同一数组的元素的指针变量相减,表示两个指针之间的元素个数,相加则无意义。
5. 两个指针变量可以比较
指向同一数组的元素的指针,指向前面元素的指针变量小于指向后面元素的指针变量。不指向同一数组的指针变量比较无意义。