众所周知,C++是一门高级语言,但却是一门比较贴近底层的高级语言,今天,让我们一起来学习一下C++和C语言中最重要的一个语言特性——指针(Pointer)

我自己的C++学习小群(尽管现在只有我一个人(或许现在有两个人)):洛谷的团队向着胜利奔赴 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

引入

下面让我们看一个非常经典的例子:swap(交换)。

当我们有两个变量a,b时,我们要在函数里交换它们的值,你会怎么做呢?

如果你不知道指针,你会这样做:

#include<bits/stdc++.h>
using namespace std;int swap(int a, int b)
{int tmp = a;a = b;b = tmp;
}int main()
{int a, b;cin >> a >> b;swap(a, b);cout << a << ' ' << b;
}

恭喜你,当你运行这段代码时,结果是:


[in]

11 12

[out]

11 12


答案很明显是错误的。

此时你会发现,其实两个变量的值并没有交换,为什么呢?因为传进函数的值其实是一个副本,当你交换两个变量时,交换的并不是并不是原来的变量,而是两个副本,自然交换结束后,也就是函数结束后,原来变量的值是没有改变的。

相当于你传进去的是一个const int常量,而常量是不可以修改的,于是他又创建了一个int,而这个int已经不是原本的int了,而你在main函数中使用的是原来未经修改的const int。

那么怎么做呢?

指针是什么

指针存放的是另一个变量的内存地址

指针就是内存地址,指针变量就是用来存放内存地址的一个变量

指针的基础操作

声明指针

type* pointer = &var;

其中,var是数据,“&”代表取变量地址,它的意思,就是让pointer的地址设为var的地址。

指针类型:“type*”

取地址

获得一个变量的内存地址,其实刚刚我们已经讨论过了,就是通过“&”运算符,来获取对应变量的地址。

cout << &11;

这个代码输出的并不是11,而是11的内存地址,因为“&11”表示的是11的内存地址(16进制表示),而不是11。

输出指针

int* p = &11;
cout << p;

现在你应该对指针有一点了解了,我觉得,你可能能猜到这段代码输出的是什么。其实他输出的和上一段代码是一样的,它只是增加了一个中间变量而已,输出的同样是11所在的内存地址。

取数据

指针存储的是一个变量的内存地址,如果他无法返回它存储的变量的话,那么它的存在就没有意义了,那么怎么取数据呢?

*pointer

在C++中,“*”既可以用来声明指针变量,也可以用来取数据,比如说

int p = &11;
cout << *p;

这段代码会输出什么呢?答案是11,因为第一步它把11的地址赋给了p,让后输出了p指针存放的数据,也就是取出了11(不是11的内存地址),因此输出11。

交换两个变量

现在我们应该知道如何交换两个变量的值了,我们先声明一个swap函数,而它的参数是两个指针,这就可以保证尽管传进来的是两个不同的指针,但是他们指向的内存地址是相同的。

int swap(int* a, int* b);

而下面交换变量就可以交换两个指针变量存储的数据的值了:

int swap(int *a, int *b)
{int tmp = *a;*a = *b;*b = tmp;
}

而main主函数中,传入的参数并不是int类型,而是int*指针类型:

int main()
{int a, b;cin >> a >> b;swap(&a, &b);cout << a << ' ' << b;
}

[in]

11 12

[out]

12 11


这样就可以得到一个准确的结果。

当然你也可以通过和scanf一样的方法,在函数里传引用,但实际上,如果你查看汇编代码就会发现,引用的底层和指针时同一种东西。具体用指针还是用引用,就是一个仁者见仁智者见智的问题了。

动态数组

众所周知,在数组长度可能很大时,开静态数组是很占空间的;但如果在主函数里根据n(长度)的值视情况开数组,又有可能会栈空间超限。因此我们可以有两种解决方式:

链表

链表的实现相对而言比较复杂,因此我单独写了一篇文章介绍链表:

C++ 链表详解_嘉定世外的JinJiayang的博客-CSDN博客

相对而言我认为还是比较详细的(看完记得点赞!)。

当然链表当中也会大量运用到指针的知识,所以最好在学会指针后再学习链表。

指针实现

一维数组模拟

我们可以声明一个数组

int *p = new int[len];

并将数组的首地址赋给p,然后就可以像普通数组一样进行操作了,这样就既可以让数组的大小是动态的,又操作简单

#include<bits/stdc++.h>
using namespace std;
int main()
{int n; cin >> n;int* p = new int[n+2];for(int i=1; i<=n; i++) cin >> p[i];for(int i=1; i<=n; i++) cout << p[i] << " ";
}

二维数组模拟

那如果我们希望一个二维数组怎么办?比如说我要打印一张乘法表。

我们同样可以用刚刚的方法进行模拟。

首先同样有一个指针p,但是它的值不再是new int[n+2],而是new int*[n+2],它指向的数组里面存储的是指针;在给一个个值进行赋值的时候,我们再将p指针里的每个指针(即每一行)都像一维数组那样进行声明,p[i] = new int[n+2],那么然后再将p指针指向的数组里面的指针指向的数组里面的值赋值,比如说赋我们需要的乘法表的值(i*j)。

注意:现在p指针存储的是一个二维数组,因此它是一个指向指针的指针——双重指针,需要写两个“*”号

如果刚刚那句话有一点难理解,没关系,你想一想p指针这个数组里面是什么,是不是也是指针(数组),因此它是双重的,是指向指针的;你也可以理解为,它就是一个数组,一个二维数组,那它自然是一个双重指针了。同理,如果你要一个三维数组,就需要一个三重指针,因为它是一个指向一个指向指针的指针的指针。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int main()
{cin.tie(), cout.tie();int n;int **p;cin >> n;p = new int*[n+2];for(int i=1; i<=n; i++){p[i] = new int[n+2];for(int j=1; j<=n; j++)p[i][j] = i*j;      }for(int i=1; i<=n; i++) {for(int j=1; j<=n; j++) cout << p[i][j] << ' ';cout << endl;}
}

指针总结

C++的指针是非常灵活,同时也是功能非常强大的,它可以让一段程序变得简洁,或易于扩展,比其它语言具有更大的优势,比如说Python中就没有指针,只能通过

import ctypesvar = ctypes.py_object
var.value = ...pointer = ctypes.POINTER(var)

来使用指针,但其实内部也是通过C/C++实现。

但是指针如果用不好,更容易引发各种各样的问题,如内存泄漏等。

作为一位C++的Programmer,学习指针是非常有必要的。当然,如果你是一位初学者,立刻完全掌握指针是毫无可能的,我们更要多练习。

如果你已经完全掌握了这一部分内容的话,推荐你看我的这篇文章,关于C++的链表和链式存储:

C++ 链表详解_嘉定世外的JinJiayang的博客-CSDN博客

Time to 点赞

看完后,别忘了

点赞!

收藏!

Thanks……

C++ Pointer指针相关推荐

  1. Golang unsafe.Pointer指针

    相较于 C 而言,Go 语言在设计时为了使用安全给指针在类型和运算上增加了限制,这让Go程序员既可以享受指针带来的便利,又避免了指针的危险性.除了常规的指针外,Go 语言在 unsafe 包里其实还通 ...

  2. Pointer 指针

    Pointer 指针 利用指针访问对象,指针指向一个对象,允许使用解引用符(操作符*)来访问对象int ival = 42; int *p = &ival;//p存放变量ival的内存地址,p ...

  3. plc的指针和c语言指针,关于STEP7 Pointer指针的问题

    buffer声明为pointer输入变量,DB_ID声明为word临时变量 L 最佳答案 1:首先需要了解 POINTER 的结构 POINTER是一个类似于C语言中指向指针的指针 见图1 POINT ...

  4. js this pointer 指针

    this JavaScript的函数内部如果调用了this,那么这个this到底指向谁? 答案是,视情况而定! 如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的 ...

  5. 内存管理-定时器循环、内存布局、tagged pointer、weak指针、copy、自动释放池

    先上代码,我们平时用的定时器,cadisplaylink.nstimer,CADisplayLink.NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用 ...

  6. 第七章 右左法则----复杂指针解析

    首先看看如下一个声明:   int* ( *( *fun )( int* ) )[10];   这是一个会让初学者感到头晕目眩.感到恐惧的函数指针声明.在熟练掌握C/C++的声明语法之前,不学习一定的 ...

  7. 右左法则----复杂指针解析

    首先看看如下一个声明: int* ( *( *fun )( int* ) )[10]; 这是一个会让初学者感到头晕目眩.感到恐惧的函数指针声明.在熟练掌握C/C++的声明语法之前,不学习一定的规则,想 ...

  8. 重新想象 Windows 8 Store Apps (49) - 输入: 获取输入设备信息, 虚拟键盘, Tab 导航, Pointer, Tap, Drag, Drop...

    重新想象 Windows 8 Store Apps (49) - 输入: 获取输入设备信息, 虚拟键盘, Tab 导航, Pointer, Tap, Drag, Drop 原文:重新想象 Window ...

  9. 指针01 - 零基础入门学习C语言41

    第八章:指针01 让编程改变世界 Change the world by program 指针啥玩意?似乎很神秘? 指针是C语言中的一个重要的概念,也是C语言的一个重要特色. 正确而灵活地运用它,可以 ...

最新文章

  1. quadTree 论文Real-Time Generation of Continuous吃透了
  2. linux+sed+-i替换路径,sed替换与别名配置
  3. Android中使用WebChromeClient显示Openlayers加载本地GeoJson文件显示地图(跨域问题解决)
  4. HTML基础-张晨光-专题视频课程
  5. 五大板块(2)—— 指针
  6. 4.3英寸屏双核 LG Prada K2通过FCC认证
  7. 当学术大家遇到技术大拿,如何攻克数据库应用头号难题?数位产学研大咖这样解读
  8. php动态交叉表,PHP Array交叉表实现代码
  9. SAP License:PS中比较常用的事务代码
  10. java中使用nextLine(); 没有输入就自动跳过的问题?
  11. Linux 多线程压缩/解压缩
  12. asp.net identity 学习1
  13. 在CentOS上安装Git(转)
  14. java生成pdf的流_Java 文件输出流.pdf
  15. 计算机留言板毕业论文摘要,留言板系统设计(毕业论文)
  16. 期货交易常用术语中英文对照表
  17. c语言:数组插入处理
  18. matlab演奏《起风了》代码
  19. CAN总线通信学习笔记
  20. sqlserver 电话号3-8位用*号代替

热门文章

  1. LIO-livox - 激光IMU初始化模块分析
  2. ionic3正式发布(ionic2升级到ionic3)
  3. Apache Drill初探
  4. 新绝代双骄3终极全攻略6
  5. PCB相关知识-PCB各层的用途
  6. Oracle中sql中unload的用法,Oracle跟Informix中load、unload
  7. jQuery是什么?
  8. 阿里巴巴的零知识证明
  9. JavaScript制作简易聊天窗口
  10. 并发编程(进程、线程、协程)