1.指针的概念:指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为"指针"(地址就是内存的物理地址)。
指针变量:存放变量地址的变量。
使用指针的目的:要通过指针能够找到被指的变量,或者说要通过指针间接访问被指向的变量。
2**.指针变量的定义**:一般形式:
类型标识符 *指针变量名;
例如:
int *PInt; //一个指向整型变量的指针
long *PLong; //一个指向长整型变量的指针
float *PFloat; //一个指向浮点型变量的指针
char *PChar; //一个指向字符型变量的指针
int *PInt1,*PInt2; //一次可以定义多个指针变量,每个变量名前都要加*号
说明:在定义指针时,一个*号只能代表一个指针;一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。
3.变量的值和变量的地址
地址运算符&:后面跟一个变量时,&给出该变量的地址。
假如a是整型变量,则&a表示变量a的地址,那么我们可以定义一个指向整型变量的指针ptr,把变量a的地址赋给指针。
int a=10;
int *ptr=NULL; //指针初始化,让其指向空,不这样做的话可能成为野指针,造成内存泄漏
ptr=&a;
4.指针变量的初始化
指针变量使用前,必须进行初始化,并且只能赋地址;如果一个指针在定以后没有初始化,应该给他赋一个空值,避免出现使用未被初始化的指针引起系统混乱(野指针)。
5.指针变量的赋值
①可以把一个变量的地址直接赋值给一个指针变量;
②在定义指针变量时,星号代表该变量是一个指针变量,而在指针赋值时,不能在指针变量名前加*号;
③赋值时不允许把一个数值赋给指针变量;
④指针变量的值可以改变,相当于指针指向的改变。
6.指针变量的大小:
是指针变量占用内存的大小(字节数)。在32位机上,所有指针类型变量占用内存字节数都为4(因为32位机就是 4字节 * 8个二进制位/字节计算出来的);如果在64位机上,指针占用内存大小就是8个字节。
9.指针运算:
①指针与整数的的加减运算,会进行指针指向地址的偏移:
p+n=p+nsizeof(指向类型);
p-n=p-nsizeof(指向类型);
②指针与指针之间,只有减法运算:
Pn-Pn1=(Pn-Pn1)/sizeof(指向类型);
如:
int arr[5]={1,2,3,4,5};
int *Pn1,*Pn2;
Pn1=&arr[0]; //设arr[0]的地址是1000
Pn2=&arr[3]; //设arr[3]的地址是1012
printf("%d\n",Pn2-Pn1); //计算结果为3,实际运算过程为(1012-1000)/4
③指针间的比较运算:就是地址值的比较。上面的例子中(Pn1 10.指针与一维数组:用指针访问数组元素的几种方式。 ②在实际数组前面加一个&与使用变量相同; ④如果指针已经指向数组中的一个元素,则p+1指向数组的下一个元素。 访问数组元素的两种方法: eg2:用指针访问 11.const修饰的指针 12.指针与字符串 13.指针与函数 14.指向函数的指针 ②指向函数的指针变量的说明: 15.指针与二维数组 16.指针的指针(二维指针、多维指针) 17.数组指针 定义时不能省略括号,因为[ ]的优先级比*高,如果没有括号就成了指针数组。数组指针定义时的数组长度、元素类型必须与指向的数组长度、类型保持一致。 数组指针指向的是整个数组,而不是数组元素,所以指针运算p+1相当于p+1*sizeof(数组名)。 访问数组元素的方式: 指针确实有点难理解,不过多用一下,实战一下对理解指针有莫大益处。
①数组名实际上就是一个指向该数组中第一个元素的指针。int arr[5]={1,2,3,4,5};
int *p=NULL;
p=&arr[0]; //等同于p=arr;
③在数组名后面加偏移量,改变指向的数组元素(指针指向位置的偏移量)p=&arr[1];
p++; //此时p的指向为arr[2]的地址,相当于p=&arr[2]
eg1:用一个整型变量接收int arr[5]={1,2,3,4,5};
int nVal1=arr[0];
int nVal2=arr[1];
int arr[5]={1,2,3,4,5};
int *p=arr;
int nVal1=*arr; //相当于nVal1=arr[0],数组名相当于首地址,加*号就是第一个元素的值
int nVal2=*(p+1); //相当于nVal2=arr[1],p指向数组首地址,加1则向后偏移一个元素,再取*就是该指向的内容,即值
①const在星号左边:修饰的是指针指向的内存。
a.指针可以改变指向
b.不可以通过指针改变其指向内存的值
②const在星号右边:修饰的是指针变量本身。
a.指针不可以改变指向
b.可以通过指针改变其所指向内存的值
③星号左右两边都有const
a.指针不可以改变指向
b.不可以通过指针改变其所指向内存的值
例子:const int *p; //值不可以改变
int const *p; //值不可以改变
int *const p; //指向不可以改变
const int *const p;//指向和值均不可改变
字符数组表示的字符串,可以用字符指针指向它,这样这个指针就可以表示这个字符串(指针指向的是字符串的首地址,与①②③指针和一维数组类似)。
①指针变量作为函数参数:将指针变量以实参的形式传递给函数,进行的是地址传递,所以在函数中可以改变实参的值。
②数组名作为函数的形参和实参:进行的地址传递(数组作参数进行传递时,相当于指针,其内存大小也会退化为指针内存的大小)。
指针传递的优点在于:可以跨栈传递,在函数内部可以修改实参的值。
③函数的返回值是一个指针:那么函数的返回值类型必须定义为一个指针类型。
定义指向函数的指针变量的格式为:
①.数据类型 (*指针变量名) (形参列表)float (*p)(float x, float y); //括号不能省略,这是定义了一个指向函数的指针变量
float *p(float x,float y); //这是一个返回值为指针类型的函数声明
把函数首地址赋给指针变量时,直接写函数名即可,不用写括号和函数参数;利用指针变量调用函数时,要写明函数的实际参数。
用指针访问二位数组:int array[2][3]={{1,2,3},{4,5,6}};
int (*p)[2][3]=NULL;
p=&array; //*p==array,p[0]=*p
array[0][0]==(*p)[0][0];
p[0][0][0]==array[0][0];
p[0][0][1]==array[0][1];
p[0]==array;
p[0][0]==array[0]==*(p[0]+0);
p[0][1]==array[1]==*(p[0]+1);
//看到这里是不是一脸懵??看看下面的你就懂了!
p+1; //跨过6*4个字节,p是指向二维数组的指针,偏移将是一个二维数组的长度
p[0]+1; //跨过3*4个字节,p[0]是指向一维数组的指针
p[0][0]+1; //跨过4个字节,p[0][0]是指向数组元素的指针
p[0][0][0]+1; //二位数组第一个元素值加1,这是取的素组元素的值
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。int number=10;
int *p=&number; //那么*p==number
int *pp=&p; //那么*pp==p, *(*pp)==number
定义方式:类型名称 (*指针名) [数组长度]int a[5]={1,2,3,4,5};
int (*p)[5]=&a;
int (*p)[5]; //指向数组长度为5的整型数组的指针
char *(*p)[10]; //指向数组类型为char *,长度为10的数组指针
int *p[10]; //一个类型为int *,长度为10的数组
int (*p)[5]; //*(p+1)跳过5*sizeof(int)个内存位置
int *p; //p+1指向下一个元素,即指向内存加sizeof(int)即4个字节
int arr[5]={1,2,3,4,5};
int (*p)[5]=&arr;
int *p1=arr;
(*p)[1]==p[0][1]==2;
arr[1]==*(arr+1)==2;
*(p1+1)==p1[1]==2;