指针变量
- 指针:本质上就是地址;
- 指针变量:存放一个变量地址的变量;
- 定义指针变量:类型名 * 指针变量名;类型名即基类型,用来确定所占内存,便于±1;
- 在定义时初始化:int * p=&a;
- 不能把整数赋给指针变量;
- 引用指针:
- 赋值:p=&a;或*p=1;(后者要求p已经有所指向,不能未初始化)
- & 取地址运算符,&a,表示a的地址;
-
- 指针运算符,*p,表示p所指对象;
- 指针变量作函数参数
- 注意!!!不要试图改变指针形参的值来改变实参
- 要改变指针形参所指向的值来改变实参
- ✔’‘‘void swap(int * p1,int * p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}’’’
❌’‘‘void swap(int * p1,int * p2)
{
int * p;
p=p1;
p1=p2;
p2=p;
}’’'(形参p1,p2的值无法传回实参,所以失败)
通过指针引用数组
- 数组元素的指针:
- 数组名代表数组首地址;
- 把首地址赋给指针变量:p=&a[0];或 p=a;
- 引用数组:①下标法 ②指针法(good)
- 用指针引用数组
- 此时指针的运算:
- p++;p–; 这里的+1是指向数组下一个元素;
- 并不是简单+1,而是加上数组元素所占字节,这就要指针的基类型了;
- p1-p2,当指针指向同一数组有意义,用来计算相对距离,(p1-p2)/一个数组元素所占字节;
- 若p一开始指向a[0],* (p+i) = * (a+i) = a[i];
[]实际上是变址运算符,即a[i]按照a+i计算地址; - 直接用指针引用数组快;
‘’‘for(p=a;p<(a+10);++p)
printf(“%d”,*p);’‘’
虽然a也是地址,但为常量,不能a++; - 指针指向数组时可以带下标,p[i]=*(p+i);建议少用
-
- p++ 等价于 *(p++);
- ++(*p)是指数组元素值加1,而不是地址;
- 此时指针的运算:
- 用数组名作函数参数
- 定义的时候要注意arr[],括号!!!;
- 调用时候光写数组名!!没有[]
- 用数组名作参数时,形参中元素变化,实参也变;
- 用数组元素做形参和变量一样,只是值传递;
- 实参数组名代表一个固定地址(指针常量),但形参数组名按指针变量处理(地址不固定);
- 多维数组元素地址
- a代表首行首地址,a+1代表行号为1的首地址
- a[1]和 * (a+1)等价,*&a[1] [2]=a[1]+2= (a+1)+2
-
- (a+i) ==a[i]; * (a[i]+j) == * (*(a+i)+j);
- 在指向行的指针前面加一个*,就转化成指向列的指针;
- 在指向列的指针前加&,转为指向行的
- &a[i]和a[i]值一样,但是含义不同,前者&a[i] 或 a+i指向行,后者a[i] or *(a+i)指向列
- 在二维数组中,a+i == a[i] == *(a+i) == &a[i] == &a[i][0],值相同
- 指向多维数组的指针变量
- 指向数组元素的指针变量
- 二维数组的元素在内存中按序存放,因此可以用一个指向整型元素的指针,依次指向各元素;
- ‘’’
int a[3][4] = { 1,2,3,4,5,6,7,8,9,0,11,21 };
int* p;
for (p = a[0];p < a[0] + 12;++p)
//注意!!!不能p=a,p是int * 型2,a是int(*)[4]型(指向一维中有四位元素的一维数组)
{
if ((p - a[0]) % 4 == 0) printf(“\n”);
// 地址之差用于判断相对距离
printf(“%4d”, *p);
}‘’’ - a[i][j]在数组中的相对位置,i* m+j(数组大小n*m)
- a[i][j]的地址&a[0][0]+(i* m+j) or p+(i*m+j);
2.指向由m个元素组成的一维数组的指针变量 - 定义指针:int (*p) [4];指向包含四个整型变量的一维数组,注意()括号,若缺少括号,就是指针数组;
- 让p指向一维数组:p=a;(a是二维数组时) or p=&a;(a是一维数组时)
- int a[3][4]; int (* p) [4];p=a;((p+2)+3)表示a[2][3];
- 指向数组元素的指针变量
- 用指向数组的指针作函数参数
注意 类型!!! 要匹配- void average(float* p,int n );//定义的时候p是指向元素的指针
average(* score,12);//score是二维数组,调用的时候,实参一定要和赋值时一样的形式,或*score,或score[0],或&a[0][0] - void search(float(* p)[4],int n);
search(score,2);
- void average(float* p,int n );//定义的时候p是指向元素的指针
通过指针引用字符串
- 定义指针:const char * string=“i love you”;
//注意const ,字符串是常量,类型要一致; - 输出字符串(用指针):printf(“%s”,string);//类字符数组,在这放地址就行,遇到’\0’停止
- 可以对指向字符串的指针变量再赋值,改变他所指的串,但字符数组错过初始化就不能再整体赋值了
- 注意!!复制或者怎么得来的像字符串的,一定要注意最后有没有’\0’,需不需要额外添加一个
- 字符指针作函数参数:函数的实参和形参可以分别用字符数组名和字符指针变量,只要参数的本质是地址即可
eg.定义的时候void copy_ string(char from[],char to[]);
调用的时候可以用指针copy_string(a,b);//这里a 和b是先前定义的指针;
调用的时候也可以是数组名copy_string(from,to); - while((* to++ = * from++)!=‘\0’);等价于while(* to++ = * from ++);
- 字符指针变量和字符数组的比较
- 赋值方式:可以对指针变赋值,但不能对数组名赋值;
- 指针变量的值可以改变,但数组名代表一个固定的地址
- 字符数组中各元素的值可以改变,但字符指针指向的字符串常量的内容不可以改;
- 用指针变量指向一个格式字符串,可以用他代替printf中的格式字符串;eg.‘’’
const char * format;
format=“a=%d,b=%f\n”;
printf(format,a,b);‘’’
等价于printf(“a=%d,b=%f\n”,a,b);
只要改变format所指向的字符串,就可以改变输入输出格式,这种printf函数称为可变格式输出函数;
也可以用字符数组实现,但是错过初始化之后只能单独赋值,改变比较麻烦,所以用指针变量方便;
- 注意一定不要忘记让指针有所指向
指向函数的指针
- int (* p) (int,int);表示p指向函数类型为整型且有两个整型参数的函数,p的类型:int(*)(int,int)
- 通过指针变量访问他所指向的函数
- ‘’‘int max(int,int);//函数声明
int (*p)(int,int);//定义指向函数的指针
int a,b,c;
p=max;//让p指向max函数
c=(*p)(a,b); //函数调用,这里等价于max(a,b); ‘’’ - 不要忘记括号(),若int * p(int,int);就变成了返回值为指向整型变量指针的函数
- ‘’‘int max(int,int);//函数声明
- 定义和使用
- 一个指针变量可以先后指向同类型的不同变量
- 对指向函数的指针不能进行算术运算;
- 虽然用指针调用函数不如用函数名调用的快,但是指针变量调用函数比较灵活
eg.当有选择时,可以用一个指针变量调用不同选择下的不同函数;‘’‘int (*p)(int,int);
if(n1) p=max;
else if(n2) p=min;
else if(n==3) p=add;
c=(*p)(a,b);//调用p指向的函数
printf(“%d”,c);’‘’
- 用指向函数的指针作函数参数
- 示意’''void fun(int x,int y,int(*p)(int,int));
if(n1) fun(a,b,max);
else if(n2) fun(a,b,min);
else if (n==3) fun(a,b,add);
- 示意’''void fun(int x,int y,int(*p)(int,int));
//定义fun函数
void fun(int x,int y,int(*p)(int,int))
{
peintf(“%d\n”,(*p)(x,y));
}
//分别定义max,min,add函数 ‘’’
#返回指针值的函数
- 定义:类型名 * 函数名(参数)
- 例题:3名学生,4科成绩,输入序号,输出学生全部成绩
思路:用二维数组放成绩,一个返回指针的函数
简单示意:‘’’
float * search(float(p)[4],int n);
float * p;
p=search(scare,k);//arr score,num k
for(i=0;i<4;++i)
printf(“%5.2f\t”,(p+i));
//定义函数
float * search(float(*p)[4],int n)
{
return *(p+n);//在指向一维数组的指针上加减,就是在行上蹦跶
}‘’’
#指针数组和多重指针
- 指针数组
- 一个数组,若其元素均为指针类型数据,成为指针数组;
- 定义:类型名 * 数组名[数组长度];
- 指针数组可以指向若干字符串,同二维数组也可以解决,但是按照最长的字符串来定义列会浪费很多内存单元;
- 存放:const * name[]={“abc”,“bhj”,“jadoif”};
- 若想进行排序,不是移动字符串,而是改变指针数组各元素指向’‘‘void sort(const char* name[], int n)
{
const char* temp;
int i, j,k;
for (i = 0;i < n-1;++i)
{
k = i;
for (j = i + 1;j < n;++j)
{
if (strcmp(name[k], name[j]) > 0) k = j;
}
if (k != i)
{
temp = name[i];
name[i] = name[k];
name[k] = temp;
}
}
}’’'//strcmp(,)的参数是地址,对一维字符数组a,b,比较就是strcmp(a,b);
- 指向指针数据的指针
- 定义:char ** p;
- 使用:‘’‘const char* name[] = { “follow me”,“basoc”,“great”,“abc”,“good” };
const char** p;
int i;
for (i = 0;i < 5;++i)
{
p = name + i;
printf(“%s\n”, *p);
}’‘’
引用
- 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。其格式为:类型 &引用变量名 = 已定义过的变量名
- 特点:①一个变量可取多个别名。
②引用必须初始化。
③引用只能在初始化的时候引用一次 ,不能更改为转而引用其他变量。 - 引用作为参数
- void swap(int &p1, int &p2) //此处函数的形参p1, p2都是引用
{ int p; p=p1; p1=p2; p2=p; }
调用的时候直接swap(a,b);就可以交换
使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作
- void swap(int &p1, int &p2) //此处函数的形参p1, p2都是引用
- 常引用
- const 类型标识符 &引用名=目标变量名
- 用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
‘’‘int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确’‘’
- 引用作为返回值
- 类型标识符 &函数名(形参列表及类型说明)
{函数体} - 用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本
- 类型标识符 &函数名(形参列表及类型说明)