C++指针, since 2020-11-22

2020-11-22  本文已影响0人  Mc杰夫

(2020.11.22 Sun)

指针(Pointer)是什么?

指针是一个地址,指向存储某一个数据的存储地址。指针变量是一种特殊性质的变量,它把地址存在一个变量中,通过先找出地址变量中的值(一个地址),再由此地址找到最终要访问的变量的方法,刺猬指针变量和访问方法。地址变量就是指针。

指针除了可以指向变量之外,还可以指向内存中的任何数据结构,和函数。注意,程序中参加数据处理的量不是指针本身的量(因指针本身是一个地址量),而指针所指向的内存区域中的数据(称为指针目标)才是需要处理的数据。

如何定义指针

定义指针的语法形式:

存储类型名 数据类型 *指针变量名

int *p1;
stat float *p2;
char *p3;

指针变量前面的类型名代表着该变量所指向的数据的类型,而指针变量本身的类型是unsigned long int。

注意定义指针过程中,比如int p1,其中的用来代表p1这个变量是指针变量,而指针变量是p1,而不是*p1。*p1表示p1所指的变量值。

指针的初始化

定义之后,使用之前,需要要赋一个合法的值。初始化的形式如下

存储类型 数据类型 *指针名=初始地址

比如将a的内存地址做为初值赋给int型指针pa

int a, *pa = &a; //第一种表达
int a; // 第二种表达,和前一种等价
int *pa = &a;
int a, *pa; //第三种表达
pa = &a; 

其中的符号&a代表了变量a的内存地址。注意数据类型和指针类型要一致。

初始化为空指针

int *p = 0;

安全起见,定义指针时,最好初始化,哪怕是初始化为0的指针。

下面是通过指针为所指向变量赋值的方法

#include <iostream.h>
void main () {
    int a = 1;
    cout << 'a=' << a << endl;
    int *pa = &a;
    *pa = 2;
    cout << 'a=' << a << endl;
}

指针的运算

取地址和取值运算

指针运算是以指针变量所持有的地址值作为运算量进行的运算。
两个运算符

针对运算符,需要明确指针变量(int *p)和符号的组合所代表的不同含义

指针的算数运算

诸如p+n, p-n, p++, ++p, p-q等操作。

  1. 加法运算p+n,其中的n是个整数,p是指针。指针作为地址量加减的整数n,其意义是指针当前指向位置的前或后第n个数据的位置,即数据长度不同的数据,所以这种操运算的结果取决于指针指向的数据类型,而非仅仅从p所指的地址移动n个值。

p+n的实际操作是(p) + n * sizeof (数据类型)

其中(p)代表了指针p中的地址值而非p本身的地址(&p)。sizeof(数据类型)的长度单位是字节

  1. p++, p--, ++p, --p这些操作和加法运算相似,都是指向了下一个数据的位置,而非内存地址紧接着的下一个。如int *p的p++,如果初始时p的值(内存地址)是1000,则++p后p的值是1002(因一个int占2个字节)。
y = *p++;

根据符号运算的优先级,和++优先于=,和==属于同一优先级,其结合规则是从右向左。所以优先进行++运算,相当于

y = *(p++);

该表达式的含义是,访问p当前值指向的目标,并将目标的值赋给y,p指向下一个目标。

  1. 指针减法p-q
    这里的p和q都是指针变量,所指向的数据类型相同p-q的结果值时两只真指向的地址位置之间的数据个数
int x[5],a;
int *px=&x[1], *py=&x[4];
a = py-px;

返回的a表示x[1]和x[4]之间相隔的元素个数。

验证下面代码

#include <iostream.h>
void main () {
    int a[5] = {1,2,3,4,5};
    int *pa, *pb;
    pa = &a[0];
    pb = &a[4];
    cout << "*pa=" << *pa <<endl;
    cout << "*pb=" << *pb <<endl;
    pa = pa+2;
    cout<<"pa+2"<<*pa<<endl;
    pa = pa++;
    cout<<"pa++"<<*pa<<endl;
    int c = pb-pa;
    cout<<"pb-pa"<<c<<endl;
}
指针的关系运算

两个指针的比较,假设数据在内存中的存储逻辑是由前向后,指向后方的指针大于指向前方的指针,也就是说

int a,b; #假设&b>&a
int *pa,*pb;
pa=&a, pb=&b;
pb>pa; #true

指针与0的比较,用于判断指针是否为空指针。

指针的赋值

(2020.11.23 Mon)
当向指针变量赋值时,赋的值必须是地址常量或变量,不能是普通的整数
赋值有三种方式如下

char a, *p;
p = &a;
float *p,*q;
p=q;
char a[10], *p;
p = a; //数组名代表了该数组的首地址

此外,前面提到的指针与整数的运算也是指针赋值的形式。

void指针和const指针

指向void类型的指针称为void指针。用关键字const修饰的指针称为const指针。

  1. void指针。一般来说,给不同类型的指针赋值时错误的,但void指针是个特例,C++允许使用空类型(void)指针,即不指定指针指向一个固定的类型,定义为
void *p;

它表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。它可以指向任何类型的C++数据。也就是说,可以用任何类型的指针直接给void指针赋值。不过如果需要将void指针的值赋给其他类型的指针,需要进行强制类型转换。

int a;
int *p=&a;
void *p2 =p;
int *p3 = (int *)p2; //void指针强制转换成int类
  1. const指针。指针定义加上关键字const,可根据位置不同表不同含义。
const int *p; //指向常量的指针p
int * const q; //指针本身的值不可改变,但其所指的值可以
const char * const m; //什么都不能改

他们的区别如何理解:const int * p中的const是修饰int,也就是p所指向的变量,而int * const q中的const修饰q,也就是一个不可变的指针变量q,它不能指向其他数据。

指针和数组

数组名表示的是该数组的首地址。定义一个指向数组的指针,可以如下定义(两种方式)

int a[10];
int *p=a;
int *p = &a[0]; //&a[0]是元素a[0]的地址,也是数组a的首地址
访问数组元素的方法
  1. 下标,a[0], a[1], a[2], ...
  2. 地址法,数组名为数组首地址,数组中的任意元素的访问可以通过首地址的加减法来实现,比如a[3]可以是*(a+3)
  3. 指针法,类似于地址法,把首地址赋值给一个指针,通过指针的加减法寻找元素
int a[10];
a[3]; //下标法
*(a+3); //地址法
int *p = a;
*(p+3); //指针法
多维数组元素的访问

一个二维数组比如a[4][5],其中的a[i]表示第i-1行的首地址,(地址法)推导得知a[3]等于&a[3][0],a[0]+1等于&a[0][1]。a[i][j]有多种访问法

*(*(a+i) + j);
*(a[i]+j);
*(a+i)[j];
*(a+4*i+j);
指针数组和数组指针

指针数组是其元素为指针的数组;数组指针是指向数组的指针。

数据类型 ( * 指针名) [常量表达式];

int a[5] = {1,3,5,7,9};
int (*p)[5]=&a; //指向包含5个整型元素的一维数组的指针
count<<"*p[0]="<<*p[0]<<endl;

数据类型 *指针数组名 [常量表达式];

int *p[6]; //[]的优先级比*高,故p先与[]结合,是数组类型,再与*结合形成指针

指针数组常用于字符串的操作

static char *name[3] = {"tom","mary","rose"};
cout<< "name[0]="<<name[0]<<endl;

name的每个元素都是指向字符数据的指针类型数据,其中name[0]指向tom,name[1]指向mary,name[2]指向rose。用指针数组处理字符串不仅可以节省内存,还可以提高运算效率,比如对5个姓名排序,字符串交换位置速度慢,而交换地址则快得多。

指针与函数

指针作为函数参数

函数参数是指针,是传址调动,也就是是实参指针和形参指针变量指向同一个内存地址。形参指针所指向的地址中内容的改变也会影响实参。函数调用时传入的参数应该是一个地址。

#include <iostream.h>
void swap(int *, int *);
void main()
{
    int a =4,b=0;
    swap(&a, &b); //调用时传入的是参数的地址
}
void swap(int *x, int *y) // 传入指针变量
{
    int m = *x;
    *x = *y;
    *y = t;
} //函数的传址调用中,传递的参数的值不改变,指针本身的值并不改变,改变的是它指向的值
指针型函数

当一个函数的返回值是指针类型时,该函数是指针型函数。
使用场景:通常非指针型函数调用结束后,可以返回一个变量,但每次都只能返回一个数据。如果需要从被调函数返回一批数据到主调函数中,就可以使用指针型函数来解决。其语法格式如下

数据类型 *函数名(参数表)

int *fun(int a,int b); //返回值是一个int型的指针
#include <iostream.h>
#include <string.h>
char *max(char *a, char *b) 
{
    return(strcmp(a,b)?a:b);
}
void main()
{   
    char *p;
    p = max('hello','good');
    cout<<p<<endl;
}
函数指针

函数指针是指向函数的指针。语法格式为

数据类型 (*函数指针名) (参数表);

int (*p) (int, int);

在定义了指向函数的指针变量后,在使用此函数指针之前,必须先给它赋值,使它指向一个函数的入口地址。由于函数名是函数在内存中的首地址,因此可以将函数名赋给函数指针变量,赋值的语法格式

函数指针名=函数名;

p = funcl;

其中,函数名所代表的函数必须是一个已经定义过的,和函数指针具有相同返回类型的函数,并且等号后面只需写函数名而不要写参数。比如不能写成如下形式p = funcl(a,b)。函数指针指向某函数后,可以用下列形式调用函数

(*指针变量)(实参表列)

表达式(*p)(a,b)相当于funcl(a,b)。
与定义一般变量指针数组一样,C++中可以定义具有特定返回类型和特定参数类型的函数指针数组。函数指针数组可以是多维的,在实际编程中一般只用到一维函数指针数组,语法格式如下

数据类型 (*函数指针明[常量表达式])(参数表);

int (*p[5])(int,int); //定义了一个含有5个元素的函数指针数组,每个元素都是一个指向函数的指针,
// 且指向的函数都是返回值类型为整型、带两个整型参数的函数 

指针和字符串

char *str2="C++";

二级指针

声明一个指针来指向指针,这个指针称为指向指针的指针,称为二级指针。定义形式

存储类型 数据类型 **指针变量名

int i, *p=&i;
int **pp = &p; //声明了一个指向指针的指针pp,其指向指针p

Reference

1 刘蕾编著,21天学通C++(第五版),电子工业出版社

上一篇下一篇

猜你喜欢

热点阅读