C++指针变量的基本写法
指针变量与应用——动态数组
在C++中,有一种神奇的变量,它不可以表示一个值,但是可以表示某个元素的地址,通过地址来访问这个元素。
打个比方:你有一张地图和一个坐标,你就可以通过访问这个坐标来达到你访问坐标所表示的元素的目的。指针变量就是这个“坐标”。
下面我们来具体看看指针变量的应用。
1、指针变量的性质
正如上面所说,指针变量不可以表示一个值,但是可以指向别的元素的地址,通过这个地址来间接访问这个元素的值。
由于它的性质,指针变量不可以直接=一个元素,赋值时要注意。
具体操作下面会讲到。
2、指针变量的声明
如何声明一个指针变量? 有如下表达式:
数据类型+“*”+指针名
通常我们这样赋值:
int main()
{
int *p=NULL;
return ;
}
这样我们就定义了一个指针类型的变量p,NULL是空内存,这个内存里什么元素都没有,你可以之后再给p赋一个元素的地址。(可以不用=NULL,但是这是个人习惯,类似于return 0好习惯这种……)
这个语句的意义是:定义一个int类型的指针p,指向空地址。
那么怎么把一个元素的地址赋给一个指针变量呢?
有如下语句:
#include<cstdio>
using namespace std;
int main()
{
int a;
int *p=NULL;
p=&a;
return ;
}
/*int main()
{
int a;
int *p=&a;
return 0;
}*/
上面两个主函数的效果是一样的。
我们说说这两段代码的意义:
相信大家都用过scanf( ),在我们输入变量名前要加一个符号“&”,这就是地址符,表示变量名的地址。
我们的指针要指向一个地址,当然就是:指针名=&变量名啦!
3、用指针变量调用元素值
既然我们会赋值了,下一步就是调用元素值,但是指针指向的是一个地址,不能直接参与运算,这时候我们要用到间接运算符“*”。(就是定义的时候那个星号)
如果我们有一个元素a,需要用指针来输出它,怎么操作?
对于这个问题,有如下程序:
#include<cstdio>
using namespace std;
int main()
{
int a;
scanf("%d",&a);
int *p=&a;//定义一个指针变量p指向元素a
printf("%d",*p);//间接运算符+指针名表示指针所指元素
return ;
}
代码注释已经很详尽了,我们的指针指向一个元素,用“*”+指针名即可访问指针所指元素
注意:通过指针操作元素值和直接操作元素值是一样的,比如下面这段代码:
#include<cstdio>
using namespace std;
int main()
{
int a,b,s,t,*pa=NULL,*pb=NULL;
pa=&a,pb=&b;
a=,b=;
s=*pa+*pb;
t=*pa**pb;
printf("%d %d",s,t);
return ;
}
程序给出的结果是30 200。
4、指针的+、-运算
首先我们给出一个基本的定义:
当我们定义数组的时候,系统会给出连续的地址,比如a[5],假设a[0]的地址是0,那么a[1]的地址就是1……以此类推。
此时,我们直接把地址+1(指针+1),就可以访问数组的下一个元素。
#include<cstdio>
using namespace std;
int main()
{
int a[],*p=&a[];
for(int i=;i<;i++)
scanf("%d",&a[i]);
for(int i=;i<;i++)
{
printf("%d ",*p);
p++;
}
return ;
}
对于p--,同理。这个语句输出了a[ ]中的所有变量。
5、指针与数组
指向数组的指针叫数组指针,众所周知,一个数组的地址是连续的,首地址就是他所占有的几个单元的首地址,我们可以把数组名赋给指针,也可以把数组中某个元素的地址赋给它。
有以下语句:
int a[],*p=a;
则以下三个语句
&a[0],a,*p,均指向同一个单元——数组的首地址。
那么可以推导:&a[i]、a+i、p+i,均指向数组a中a[i]的地址。
有如下代码:
#include<cstdio>
using namespace std;
int main()
{
int a[],*pa=a;
for(int i=;i<;i++)
scanf("%d",&a[i]);
for(int i=;i<;i++)
printf("%d %d %d %d\n",*(pa+i),pa[i],a[i],*(a+i));
return ;
}
我们输如5个数:1 2 3 4 5
系统给出了5行:
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
这说明上面4个语句:*(pa+i),pa[i],a[i],*(a+i)是等价的。
代码说明和注意事项:
1、a(数组名)可以加“*”变为常量指针,a是开始元素,根据指针的加减原理,a+i是第i个元素的地址。
2、a是常量名,不能直接进行指针的+、-操作(这里指的是p++、p--这种赋值操作非法,但是a+i这种是合法的),但是pa是指针变量,可以进行加减操作。
6、指针申请系统空间
我们用如下语句可以申请一个系统空间给指针p:
int *p=new(int);
此时*p的内容不确定。
这个语句是动态数组的基础。
7、用指针当数组名
1、原理:之前说过,如果我们一次申请多个空间,系统会发给我们连续的新地址,可以当做数组用。
2、具体操作
有如下代码:
#include<cstdio>
using namespace std;
int main()
{
int n,*p;
scanf("%d",&n);
p=new int[n+];//申请连续的n+1个空间给指针p
for(int i=;i<=n;i++)
scanf("%d",&p[i]);
for(int i=;i<=n;i++)
printf("%d ",p[i]);
return ;
}
如果我们输入:
5
1 2 3 4 5
系统给出
1 2 3 4 5
上面的代码你可以理解有一个数组,数组名就是指针名,其余操作和第5个板块中提到的一样。(通过数组名+下标访问)
我们还可以改成这个样子:
#include<cstdio>
using namespace std;
int main()
{
int n,*p;
scanf("%d",&n);
p=new int[n+];//申请连续的n+1个空间给指针a
for(int i=;i<=n;i++)
scanf("%d",&p[i]);
for(int i=;i<=n;i++)
{
p++;//由于p默认指向第0个元素,所以先++
printf("%d ",*p);
}
return ;
}
这里使用指针访问而不使用数组名访问,和上面的代码是等价的。当然你也可以写成这样:printf("%d ",*(p+i));在上面提到过,这几种写法是等价的。
8、动态数组与空间复杂度优化
前面扯了那么多指针的基本定义和写法,终于到了今天的正题了——利用指针建立动态数组。
我们给出一个情景:现在有一个巨大(行列<=10000000)但是稀疏(大部分元素是0)的矩阵,我们要对这个矩阵进行操作,怎么办呢?
显然,这样的代码是绝对行不通的。
#include<cstdio>
#define N 10000100
using namespace std;
int n[N][N];
如果这么写,你的空间复杂度是绝对过不了的。
我们要进行优化才行。
记得指针可以申请空间吗?我们可以利用这个特性,避免存储无效数据(0),我们为每一次输入的有效数据开一个新的内存单元,这样就不会爆内存啦!
我们看下面这个例题:
一本通例题8.7:
【问题描述】
矩阵可以认为是N*M的二维数组。现在有一个巨大但稀疏的矩阵。
N,M的范围是1<=N,M<=100000,有K个位置有数据,1<=K<=100000。
矩阵输入的方式是从上到下(第1行到第N行),从左到右(从第1列到第M列)扫描,记录有数据的坐标位置(x,y)和值(v)。这是按照行优先的方式保存数据的。
现在要求按照列优先的数据,即从左到右,从上到下扫描,输出有数据的坐标和位置。
【输入格式】
第1行:3个整数N,M,K,其中1<=N,M,K<=100000;下面有K行,每行三个整数:a,b,c,表示第a行第b列有数据c。数据在int范围内,保证是行优先的次序。
【输出格式】
1行,K个整数,是按照列优先次序输出的数
【样例输入】
4 5 9
1 2 12
1 4 23
2 2 56
2 5 78
3 2 100
3 4 56
4 1 73
4 3 34
4 5 55
【样例输出】
73 12 56 100 34 23 56 78 55
【样例解释】
0 | 12 | 0 | 23 | 0 |
0 | 56 | 0 | 0 | 78 |
0 | 100 | 0 | 56 | 0 |
73 | 0 | 34 | 0 | 55 |
对于这个矩阵,我们可以这样存:
73 | 12 | 34 | 23 | 78 |
—— | 56 | —— | 56 | 55 |
—— | 100 | —— | —— | —— |
—— | —— | —— | —— | —— |
注:标记“------”的都是没有使用的内存,这样我们就节省了11个内存单元,对于大数据的时候,我们还能节省更多的内存,保证不会超出空间限制。
这个思路的大体意思就是:忽略x的值,把第y列第一次输入的数据当做第y列的第一个数据,然后是第二个……
下面来看代码实现:
#include<cstdio>
using namespace std;
const int LP=;
int n,m,k;
int x[LP],y[LP],d[LP],c[LP];//记录数据,记录第n个数据在x行,y列,c是第y列的数据总数。
int *a[LP];//最多有LP列,所以我们开LP长度的指针数组
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=;i<=k;i++)
{
scanf("%d%d%d",&x[i],&y[i],&d[i]);//输入x,y,d
c[y[i]]++;//第y[i]列的数据个数++
}
for(int i=;i<=m;i++)
a[i]=new int[c[i]];//为每一列申请空间来存数据
for(int i=;i<=k;i++)
{
*a[y[i]]=d[i];//收集数据到第y列中
a[y[i]]++;//第y列的指针指向下一个地址,准备下一次收集
}
for(int i=;i<=m;i++)//列优先
{
a[i]-=c[i];//因为前面收集数据的时候每一列的指针都指向了该列的最后一个元素,所以要先减去该列的元素数,让它指向第一个元素
for(int j=;j<=c[i];j++,a[i]++)//从第1列开始输出,j用来统计输出到第i列第几个元素,如果输出到最后一个元素,跳出循环
printf("%d ",*a[i]);//指针每次+1,指向下一个元素并输出它
}
return ;
}
a[i]=new int c[[i]];这一句的意思是给a[i]这个指针新申请c[i]个空间,等同于我们开了LP个一维的指针数组,这些数组每一个都有一个专用的指针a[i],每个数组有c[i]个元素。
到这里,我们已经讲完了利用指针开动态数组数组的具体做法,这样可以很有效率的优化你的程序,赶紧用起来吧!!!
C++指针变量的基本写法的更多相关文章
- C语言指针变量作为函数参数
0x01 指针变量作为函数参数的作用是:将一个变量的地址传送到另一个函数中. 0x02 简单的例子:虽然都能实现功能,但意义不同. 正确的写法: #include <stdio.h> vo ...
- 分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)
BOOL型变量:if(!var) int型变量: if(var==0) float型变量: const float EPSINON = 0.00001; if ((x > ...
- C语言之指针变量
菜单导航 1.指针变量 2.指针和数组 3.常量指针和指向常量的指针 4.指针和字符串的关系 5.数组越界造成的访问不属于自己的内存空间现象 6.引用数据类型和基本数据类型,形参和实参 7.字符串和字 ...
- 【转】C语言中,为什么字符串可以赋值给字符指针变量
本文是通过几篇转帖的文章整理而成的,内容稍有修改: 一. C语言中,为什么字符串可以赋值给字符指针变量 char *p,a='5';p=&a; //显然 ...
- C语言中,为什么字符串可以赋值给字符指针变量
转载于:http://www.cnblogs.com/KingOfFreedom/archive/2012/12/07/2807223.html 本文是通过几篇转帖的文章整理而成的,内容稍有修改: 一 ...
- C语言指针篇(一)指针与指针变量
指针 1. 什么是指针? 2. 指针可不可怕? 3. 指针好不好玩? 4. 怎么学好指针? C语言是跟内存打交道的语言,指针就是内存地址.指针无处不在,指针并不可怕,相反,等你学到一定程 ...
- BOOL,int,float,指针变量 与“零值”比较的if语句
分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var) 解答: BOOL型变量:if(!var) int型变量: if(var==0) float型变量: ...
- C/C++中的 if(指针变量) 和 if(!指针变量)
目录 if(指针变量) 代码演示 if(指针变量) 解读代码 if(!指针变量) 解读代码 总结 替代方案.推荐写法!!!!! if(指针变量) 当把一个指针作为条件表达式时,所要判断的条件实际上就是 ...
- 全面总结sizeof的用法(定义、语法、指针变量、数组、结构体、类、联合体、位域位段)
一.前言 编译环境是vs2010(32位). <span style="font-size:18px;">#include<iostream> #inclu ...
随机推荐
- spring boot 整合 ehcache
1. 该说的话 每个人都应当学会独立地去思考.去寻找答案,而不是一味地伸手向他人索取所谓的标准答案. 首先,别成为"拿来主义"者,其次远离"拿来主义"的人. 2 ...
- 如何证明sleep不释放锁,而wait释放锁?
wait 加锁示例 public class WaitDemo { private static Object locker = new Object(); public static void ma ...
- 【Logisim实验】构建立即数-随机存储器-寄存器的传送
关于Logisim Logisim在仿真软件行列中算是比较直观的软件了,它能做的事情有很多,唯一不足的是硬件描述语言的支持,总体上来说适合比较底层的仿真,依赖于Hex值,通过线路逻辑设计能够较好的 关 ...
- 监督学习-KNN最邻近分类算法
分类(Classification)指的是从数据中选出已经分好类的训练集,在该训练集上运用数据挖掘分类的技术建立分类模型,从而对没有分类的数据进行分类的分析方法. 分类问题的应用场景:用于将事物打上一 ...
- 可能会用的到的JQ插件
├─lib │ jquery jQuery类库(v1.9.1) │ bootstrapSwitch 开关控件 │ Hui-iconfont_v1.0 阿里图标字体库(H-ui定制) │ font-aw ...
- map,reduce,filter基础实现
#coding=gbk from operator import add # 导入加法 # map 函数名 , 序列对象 print(list(map(str,range(5)))) print(li ...
- c++ 第一天 变量、判断、循环
C++介绍 语言的产生 C++ 由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的,由于C++ 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言 ,所以最初命 ...
- Python File flush() 方法
概述 flush() 方法是用来刷新缓冲区的,即将缓冲区中的数据立刻写入文件,同时清空缓冲区,不需要是被动的等待输出缓冲区写入.高佣联盟 www.cgewang.com 一般情况下,文件关闭后会自动刷 ...
- PHP strftime() 函数
------------恢复内容开始------------ 实例 根据区域设置格式化本地日期和时间: <?php echo(strftime("%B %d %Y, %X %Z&quo ...
- PHP preg_match() 函数
preg_match 函数用于执行一个正则表达式匹配.高佣联盟 www.cgewang.com 语法 int preg_match ( string $pattern , string $subjec ...