C语言基础学习笔记——指针

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-n
sizeof(指向类型);
②指针与指针之间,只有减法运算:
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.指针与一维数组:用指针访问数组元素的几种方式。
①数组名实际上就是一个指向该数组中第一个元素的指针。

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]

④如果指针已经指向数组中的一个元素,则p+1指向数组的下一个元素。

访问数组元素的两种方法:
eg1:用一个整型变量接收

int arr[5]={1,2,3,4,5};
int nVal1=arr[0];
int nVal2=arr[1];

eg2:用指针访问

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则向后偏移一个元素,再取*就是该指向的内容,即值

11.const修饰的指针
①const在星号左边:修饰的是指针指向的内存。
  a.指针可以改变指向
  b.不可以通过指针改变其指向内存的值
②const在星号右边:修饰的是指针变量本身。
  a.指针不可以改变指向
  b.可以通过指针改变其所指向内存的值
③星号左右两边都有const
  a.指针不可以改变指向
  b.不可以通过指针改变其所指向内存的值
例子:

const int *p;		//值不可以改变
int const *p;		//值不可以改变
int *const p;		//指向不可以改变
const int *const p;//指向和值均不可改变

12.指针与字符串
字符数组表示的字符串,可以用字符指针指向它,这样这个指针就可以表示这个字符串(指针指向的是字符串的首地址,与①②③指针和一维数组类似)。

13.指针与函数
①指针变量作为函数参数:将指针变量以实参的形式传递给函数,进行的是地址传递,所以在函数中可以改变实参的值。
②数组名作为函数的形参和实参:进行的地址传递(数组作参数进行传递时,相当于指针,其内存大小也会退化为指针内存的大小)。
指针传递的优点在于:可以跨栈传递,在函数内部可以修改实参的值。
③函数的返回值是一个指针:那么函数的返回值类型必须定义为一个指针类型。

14.指向函数的指针
定义指向函数的指针变量的格式为:
①.数据类型 (*指针变量名) (形参列表)

float (*p)(float x, float y);		//括号不能省略,这是定义了一个指向函数的指针变量
float *p(float x,float y);			//这是一个返回值为指针类型的函数声明

②指向函数的指针变量的说明:
把函数首地址赋给指针变量时,直接写函数名即可,不用写括号和函数参数;利用指针变量调用函数时,要写明函数的实际参数。

15.指针与二维数组
用指针访问二位数组:

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,这是取的素组元素的值

16.指针的指针(二维指针、多维指针)
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。

int number=10;
int *p=&number;		//那么*p==number
int *pp=&p;			//那么*pp==p, *(*pp)==number

17.数组指针
定义方式:类型名称 (*指针名) [数组长度]

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的数组

数组指针指向的是整个数组,而不是数组元素,所以指针运算p+1相当于p+1*sizeof(数组名)。

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;

指针确实有点难理解,不过多用一下,实战一下对理解指针有莫大益处。

你可能感兴趣的:(C学习笔记,C语言基础,指针,指针与数组,二维指针,指针运算)