放入我的博客食用效果更佳(有很多oi学习资料)

1.指针基础

1.引用

C++有一个东西叫引用,引用相当于给对象(如:变量)起了另一个名字,引用必须用对象初始化,一旦初始化,引用就会和初始化其的对象绑定在一起,就是说引用的值就是被引用的对象的值,引用的值被修改时被引用的对象也会被修改,但不能定义引用的引用,因为引用不是对象,引用定义方式:类型 &引用名1=对象名1

比如:

int i=0;
int &x=i,y=i,z=i;

只有x是引用且与i绑定,y和z都只是一个初值为0的int类型变量。

我们往往可以使用引用类型来简化代码或者节省空间复杂度。引用有些地方需要注意可以看下面代码:

int &ref=10;      //错误,引用初始值必须是对象
double x=1.78;
int &ref2=x; //错误,ref2的引用类型与x不匹配
double &p=x; //正确
p=3.14;

如果去除错误项就运行上述代码,x的值就会变成3.14。

2.基本的指针

然后C++里还有一个东西叫指针,是一种对象。

指针和引用定义类似,只不过把&改成了*符号,可以不初始化。指针在本质上是一个地址,因此指针的赋值需要用取地址符&(注意和引用要区分)。获取指针指向的地址有两种方式,这里先介绍一种,用*指针名来获取指针指向的地址的变量的一个引用。举个例子:

int i=25;
int* x=&i;
int y=*x;
(*x)=6;

运行完上述代码后,i的值变成了6。

和引用相同,指针指向的类型必须与赋值给其的地址类型匹配。指针一般有三种形式:

1.指向一个对象

2.空指针

3.无效指针

空指针即指针值为NULL,这个东西定义在头文件cstdlib中。如果指针未被初始化或者指向对象的空间被回收等等则该指针为无效指针。我们应尽量避免出现无效指针,这往往会让你的代码出现错误而且难以调试。

指针可以使用加减运算符,表示向前或向后任意单位长度的对象的地址。

因为指针是对象,所以可以有指针的指针,指针的指针称为二级指针,二级指针的指针称为三级指针,三级指针的指针称为四级指针,以此类推。指针的功能十分强大,但也难以调试,很多程序员往往会在调试指针上花费大量时间。

2.指针拓展

1.引用,指针与常量

这是比较搞脑子的一块内容,请做好准备。

引用与常量的关系只有一中:常量引用,即引用本身的类型必须是常量,但引用的可以是常量也可以是对象。

const int i=5;
const int &r1=i; //正确,i是常量
const int &r2=15; //正确,15是常量
int i=42;
const int &r=i; //正确,r绑定了变量i,引用r不能修改i的值
r=0; //错误,不能用r来修改i的值

因此我们在函数中传递某些参数时可以使用常量引用来减少空间复杂度。

指针与常量的关系有两种情况,一种与引用类似,称为指向常量的指针,本身类型必须是常量,但指向的值不一定是常量,只是无法通过该指针来修改值,常量指针可以改变其指向的地址。如:

const int i=0,j=0;
const int *p=&i;
p=&j; //正确

另一种称为常量指针,这种指针必须初始化,且指向的地址初始化后不能被改变,这种指针的定义有点不同,需要这么定义:

类型 *const 指针名

如:

int k=12;
int *const p=&k;
const double d=1.5;
const double *const p2=&d; //p2是一个指向常量的常量指针

2.指针与一维数组

这里我们要介绍指针的第二种获取对应地址的变量的引用的方式:下标运算符。下标运算符可以获得指针指向位置往后任意个单位的地址的引用。这让大家想到了什么?数组!没错,数组本质上就是在系统栈空间中开出了一块连续的空间,然后使用数组时数组就是一个指向该数组第0位的常量指针,我们可以用星号来得到第0位的地址的引用。

知道了这些,我们就可以开挂了。有些人或许在抱怨C++中用不了下标为负数的数组,其实这是可以的。比如我们想开一个下标为[-10..9]的数组a,可以这么开:

int _a[20];
int *const a=&_a[10];

或者这样也可以:int _a[20],*const a=_a+10;然后我们就可以使用一个下标为可以为负数的a数组了。

同样,在传递参数时,传递整个数组不方便,我们可以把它作为指针来传递,这样做在修改时会直接在你原先的数组中修改。比如:

void func(int* a){
//内容
}

同理,交换两个数组也可以用类似的方式。但是由于直接开出来的数组用的是常量指针,无法交换,所以我们可以用普通的指针去指向它们,然后交换普通的指针。

	int _a[100],_b[100];
int *a=_a,*b=_b; //使用普通指针代替
for(int i=0;i<4;++i) a[i]=1;
std::swap(a,b);
for(int i=0;i<4;++i) printf("%d",b[i]);

上面这段代码就会输出1111。

3.指针与多维数组

多维数组就是多级指针,但是系统并不知道你一个指针所指向的区域有多大,所以作为参数向函数传进去时需要表明你的数组大小(注意:如果填的数组大小一定要与实际的相符)。同样,在函数中不会另开一个数组,如果出现了修改,会直接在你传进去的多维数组中修改。举个例子:

int b[14][20][20];
void func(int a[14][20][20]){
a[0][0][0]=1;
}
int main(){
func(b);
printf("%d",b[0][0][0]);
}

这段代码会输出1。

如果要在函数中返回多维数组,同样可以用指针。C++规定函数不可返回数组,所以我们需要将其调typedef之后以指针的形式返回。

typedef int A[20];
int a[20][20];
A* func(){
//内容
return a;
}

注意,这种方式不可返回函数中开的数组,因为在函数结束时该内存空间会被销毁,此时返回的指针会指向未知内存区域。

4.指针与函数

我们可以使用指针来换函数名,这样的指针称为函数指针。函数的类型是由其返回值和参数决定的,因此我们需要这样定义一个指向函数的指针:

函数返回值类型 (*函数指针)<函数参数表>

先写一个这样的函数

void func(const int &x){
//内容
}

主程序里可以这么写:

void (*p)(const int&);   //定义函数指针
p=&func; //将函数指针p指向函数func
(*p)(4); //调用p指向的函数
p(4); //这是一个与上一行等价的调用

函数指针是一个对象,因此我们可以像传变量一样把它传来传去,但是往往需要类型别名。如:

typedef void (*F)(const int&);
void func(const int &x){
//内容
}
F func2(F p){
return p;
}

这样我们就可以像sort函数那样传个cmp函数之类的来使代码功能更多。

4.指针的相关应用——链表

1.动态内存

C++语言中,我们可以使用new语句来在系统堆空间中开出点空间来并返回地址,我们可以使用指针来存储开出来的地址。如:

int* x=new int;  //x指向了一个未初始化的int类型变量

C++还支持开动态的一维数组:

int* x=new int[10];  //此时x是指向一个大小为10的数组的下标为0的位置的指针

特别的是,动态一维数组的下标范围可以不是常量表达式。如果要开多维的,就有些麻烦了,以二维为例,开一个大小为n×5的数组就需要这么开了:

int** x=new int*[n];   //注意此时x是一个二级指针
for(int i=0;i<n;++i) x[i]=new int[5];

工程上,使用new语句有时会出现一些鬼畜的错误,我们就需要使用一些其它的东西,比如说这个定义在头文件new中的nothrow对象,我们可以这么写:

int* x;
x=new (nothrow) int; //如果分配出错,x就会变成空指针

既然是动态内存,我们当然可以随时释放它。释放要使用delete表达式,形式是:

delete p;   //其中p一定要是一个指针,会把p指向的动态内存释放掉

注意,这里p指向的一定要是一个new语句开出来的内存的地址,不然会出错。执行该语句后,p变成了空悬指针,是一种无效指针。为了避免这类无效指针再来出一些奇奇怪怪的错误,我们可以把它变成空指针。

2.链表

如果要使用纯正的C++链表,我们需要使用结构体(或者类)嵌套定义并用动态内存去使用。比如,我们可以定义一个双向链表的结点:

struct Node{
Node* next; Node* pre;
int key;
};

然后我们可以使用动态内存去开结点,更改结点。我们会发现,很多时候都要用到类似与(*x).y的形式,非常不方便,C++给出的一种简便写法,就是x->y,这可以使你的链表更加简洁。一下是一段依次读入n个数然后输出的代码:

#include<cstdio>
struct Node{
Node* next;
int key;
};
int main(){
Node* head=new Node;
Node* x=head;
int n;
scanf("%d%d",&n,&head->key);
for(int i=1;i<n;++i){
x=x->next=new Node;
scanf("%d",&x->key);
}
x->next=NULL,x=head;
for(int i=0;i<n;++i){
Node* y=x;
x=x->next;
printf("%d ",y->key);
delete y;
}
return 0;
}

4.总结

指针是C语言的灵魂,也是C++中重要的一部分,用得好可以使你的代码更简洁,运行更快,功能更多,而动态内存和相关的链表速度有些慢,不建议在竞赛中使用。指针主要是可以实现一些对于内存空间的操作,这个概念有些抽象,所以当指针出错时,其调试难度也将大大增加。所以对于OIer们,我不建议大量使用指针(特别是在不是模板的代码中),在java等较新的语言中,有相当一部分使用了面向对象中的动态绑定替代了指针的使用。

版权声明:此篇文章为本博客管理员“傻逼”所写

【洛谷日报#75】浅谈C++指针的更多相关文章

  1. [洛谷日报第62期]Splay简易教程 (转载)

    本文发布于洛谷日报,特约作者:tiger0132 原地址 分割线下为copy的内容 [洛谷日报第62期]Splay简易教程 洛谷科技 18-10-0223:31 简介 二叉排序树(Binary Sor ...

  2. [洛谷日报第39期]比STL还STL?——pbds

    [洛谷日报第39期]比STL还STL?——pbds   洛谷科技 发布时间:18-08-3116:37 __gnu_pbds食用教程 引入 某P党:“你们C++的STL库真强(e)大(xin),好多数 ...

  3. [洛谷日报#204] StackEdit——Markdown 编辑器的功能介绍

    本文同时发表于洛谷日报,您也可以通过洛谷博客进行查看. 1.介绍与开始使用 1.1 这是什么? StackEdit是基于PageDown.Stack Overflow和其他堆栈交换站点使用的Markd ...

  4. 【洛谷日报#26】GCC自带位运算系列函数

    文章转自 洛谷 谈到GCC的黑科技,大家想到的一定是这句: #pragma GCC optimize (3)//吸氧 抑或是这句: #pragma GCC diagnostic error " ...

  5. 洛谷日报 & 原来博客(转载)

    震惊,新的功能:可以按Ctrl + F 进行关键字查询. \(update\) on 10.26:把这两个月的日报也加入进去了,并且修复了几个错误. 本文会把小编用过的博客和比较好的博客放在这里. 可 ...

  6. [转]浅谈C++指针直接调用类成员函数

    找了一番之后发现这篇文章讲的很清楚. 传送门

  7. 浅谈AC自动机模板

    什么是AC自动机? 百度百科 Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法. 要学会AC自动机,我们必须知道什么是Trie,也就是字典树.Tr ...

  8. BZOJ 3545 / 洛谷 P4197 Peaks 解题报告

    P4197 Peaks 题目描述 在\(\text{Bytemountains}\)有\(N\)座山峰,每座山峰有他的高度\(h_i\).有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个 ...

  9. 洛谷U41492(树上启发式合并)

    提交通道 洛谷日报 考虑非\(O(n^2)\)的预处理.一遍dfs时,check某颜色有没有的数组何时清空很尴尬:得到某树答案后如果不清,则影响接下来兄弟树的搜索:如果清了,父亲节点又难以收集答案. ...

随机推荐

  1. 51nod 1103【鸽巢原理】

    思路: 这道题嘛有些弯还是要转的,比如你说让你搞n的倍数,你别老老实实照她的意思去啊,倍数可以除法,取膜 . 因为n个数我们可以求前缀和然后取膜,对n取膜的话有0-n-1种情况,所以方案一定是有的,说 ...

  2. hdoj5805【模拟】

    BestCoder Round #86 B NanoApe Loves Sequence 题意: 中文题,题意就算了 思路: 弱的思路- 找一个最大,和第二大,第三大,标记下标(前面那个) ①:如果是 ...

  3. python __builtins__ dict类 (17)

    17.'dict', 用于创建一个字典. class dict(object) | dict() -> new empty dictionary # 空字典 | dict(mapping) -& ...

  4. BERT的几个可能的应用

      BERT是谷歌公司于2018年11月发布的一款新模型,它一种预训练语言表示的方法,在大量文本语料(维基百科)上训练了一个通用的"语言理解"模型,然后用这个模型去执行想做的NLP ...

  5. SpringMVC + ajax

    1.ajax 返回汉字乱码 解决方法: http://blog.sina.com.cn/s/blog_5f39177b0101it7h.html //方案一 response.setCharacter ...

  6. April Fools Contest 2017 F

    Description You are developing a new feature for the website which sells airline tickets: being able ...

  7. excel之实验数据处理线性拟合

    实验前准备:设计表格项,通过设计公式,从而输入原始数据后直接得到最终的结果数据,学习常用的VBA公式及处理:Cn-$B$4,其中的$B$4表示绝对单元格位置;SUM(Xm:Yn)求范围内的和. 针对实 ...

  8. oracle 数据导入、导出

    导入导出 --数据导出备份和导入 ------注意 导出和导入 必须是CMD 命令行下操作,而不是SQL编辑器中 --1.导出表 . --exp:导出关键字 ,userid:用户权限 ,file:保存 ...

  9. 从Java synchronized和volatile说起

    请参看https://www.cnblogs.com/chengxiao/p/6528109.html这个链接,说的特别好

  10. 程序员必须知道FTP命令

                                             程序员必须知道FTP命令 文件传输软件的使用格式为:FTP<FTP地址>,若连 接成功,系统将提示用户输入 ...