C++的指针总结

引言

初入c++,肯定会对指针这个概念非常熟悉但是为什么c/c++要使用指针?

其实每一种编程语言都使用指针,指针并不只是C/C++的独有特性。C++将指针暴露给了用户(程序员),而Java和C#等语言则将指针隐藏起来了。不光如此,指针还有很多妙用,后面会着重展开详解。 


 一,指针(*)的概念分析

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

指针的类型 *指针变量的名称;
eg: int *p

要搞清一个指针需要搞清指针的四方面的内容:

  • 指针的类型
  • 指针所指向的类型
  • 指针的值或者叫指针所指向的内存区
  • 指针本身所占据的内区。 

相关链接:一文让你不再害怕指针之C指针详解(经典,非常详细) - 知乎 (zhihu.com)

指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。必须加以区分!!!

首先,先来看几个复杂类型的指针声明(有难入易更容易理解)

int p; //这是一个普通的整型变量
int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针

二,指针的算术运算

指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

假设 ptr 是一个指向地址 1000 的整型(int)指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004。如果 ptr 指向一个地址为 1000 的字符(char),上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

因为int类型在32位程序中占4个字节,字符char类型占1个字节

递增一个指针

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序:

例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。请看下面的程序:(递增变量指针(ptr++),以便顺序访问数组中的每一个元素

#include <iostream>

using namespace std;
const int MAX = 3;

int main()
{
    int  var[MAX] = { 10, 100, 200 };
    int* ptr;

    // 指针中的数组地址(指针指向数组开头)
    ptr = var;
    for (int i = 0; i < MAX; i++)
    {
        cout << "Address of var[" << i << "] = ";
        cout << ptr << endl;

        cout << "Value of var[" << i << "] = ";
        cout << *ptr << endl;

        // 移动到下一个位置
        ptr++;
    }
    return 0;
}

三,运算符&和*

C++ 提供了两种指针运算符

  • 取地址运算符 && 是一元运算符,返回操作数的内存地址。例如,如果 var 是一个整型变量,则 &var 是它的地址。&var 的运算结果是一个指针
  • 间接寻址运算符 * 间接寻址运算符 *,它是 & 运算符的补充。* 是一元运算符,返回操作数所指定地址的变量的值。
int main ()
{
   int  var = 3000;
   int  *ptr;
   int  val;
// 获取 var 的地址
ptr = &var; //输出prt=0xbff64494(即3000的地址)
// 获取 ptr 的值
val = *ptr; //输出val=3000
}

四,数组和指针的关系

 数组的数组名其实可以看作一个指针。看下例:

int array[10]={0,1,2,3,4,5,6,7,8,9},value;  
...  
...  
value=array[0];//也可写成:value=*array;  
value=array[3];//也可写成:value=*(array+3);  
value=array[4];//也可写成:value=*(array+4);  

上例中,一般而言数组名array代表数组本身,类型是int [10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。 

 五,指针和结构类型的关系

 可以声明一个指向结构类型对象的指针。

struct MyStruct  
{ 
       int a;  
       int b;
       int c;  
}  
MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是MyStruct*,它指向的类型是MyStruct。

怎样通过指针ptr来访问ss的三个成员变量?  

ptr->a;  
ptr->b;  
ptr->c; 

 六,传递指针给函数

C++ 允许传递指针给函数,只需要简单地声明函数参数为指针类型即可。

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

#include <iostream>
#include <ctime>

using namespace std;

// 在写函数时应习惯性的先声明函数,然后在定义函数
//定义一个函数(参数为指针)
void getSeconds(unsigned long* par);

int main()
{
    unsigned long sec;
    getSeconds(&sec);//获取sec的地址
    // 输出实际值
    cout << "Number of seconds :" << sec << endl;
    return 0;
}

void getSeconds(unsigned long* par)
{
    // 获取当前的秒数
    *par = time(NULL);
    return;
}

 七,从函数返回指针

 C++中函数是不能直接返回一个数组的,但是数组其实就是指针,所以可以让函数返回指针来实现:

 另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

要想返回一个数组,使用智能指针之类的东西才是正途。(后续总结智能指针。。。)

 

posted @ 2021-07-15 16:41  唯有自己强大  阅读(1338)  评论(0编辑  收藏  举报