指针(视频学习结合一本通)视频为北大c++指针(程序设计与算法一)

有了指针,就有了自由访问内存空间的手段。


指针的基本概念:

每个变量都被存放在从某个内存地址开始的若干字节中。

指针也叫作指针变量,大小为4个字节(64位计算机为8个字节)的变量,其内容代表一个内存地址。

通过指针,能够对该指针指向的内存空间进行读写。

如果把内存的每一个字节都想象成一个房间,那么内存地址相当于房间号,而指针里存放的就是房间号。

指针变量的赋值、定义:

类型说明符 *变量名

int *p; p是一个指针,变量p的类型是int*。

char *c;c是一个指针,变量c的类型是char*。

float *d;d是一个指针,变量d的类型是float*。

如何访问int型变量a前面一个字节?

int a;

char*p=(char*)&a;

--p;

printf(“%c”,*p);//可能会导致运行出错

*p=‘A’;//可能会导致运行出错(可能不让访问)

(以上方法是错误的)

int *p1,*p2;int n=4;

char *pe1,*pe2;

p1=(int*)100;

p2=(int*)200;

cout<<"1)"<<p2-p1<<endl;

通过指针访问其指向的内存空间:

int *p=(int*)40000;

*p=5000;

int n=*p;

若干=sizeof(int),因为int*p。

指针定义的总结:

T*p;T可以是任何类型,如float,int char。

p的类型是T*。

*p的类型是T。

通过表达式*p可以读写从地址p开始的sizeof(T)个字节。

*是间接引用运算符。

指针用法:

char ch=‘A’;

char *pc=&ch;

&取地址符

指针的作用:

不需要通过变量就可以直接对内存空间进行操作。通过指针,程序能访问的内存空间就不仅限于变量所占有的内存空间。

指针的互相赋值:

不同类型的指针,如果不经过强制类型转换,不可以相互赋值。

int*pn,char*pc,char c=0x65;

pn=pc;//类型不匹配,编译出错

pn=*c;//类型不匹配,编译出错

pn=(int*)&c;

int n=*pn;//n值不确定

*pn=0x12345678;//编译没毛病,但运行会错误,可能会导致程序的崩溃

指针的运算:

1.两个同类型的指针变量可以比较大小(比较地址的大小)

地址p1<地址p2   ==   p1<p2   值为真

地址p1=地址p2   ==   p1==p2   值为真

地址p1>地址p2   ==   p1>p2   值为真

2.两个同类型的指针变量,可以相减

两个T*类型的指针p1和p2

p1-p2=(地址p1-地址p2)/sizeof(T)

3.指针变量加减一个整数的结果是一个指针

p:T*类型的指针

n:整数类型的变量或常量

p+n:T*类型的指针,指向地址:地址p+n*sizeof(T)

n+p,p-n,*(p+n),*(p-n)含义自明

4.指针变量可以自增自减

T*类型的指针p指向地址n

p++,++p:p指向n+sizeof(T)

p--,--p:p指向n-sizeof(T)

5.指针可以用下标运算符“[  ]”进行运算

p是一个T*类型的指针,n是整数类型的常量或变量。

p【n】=*(p+n)

空指针:

地址0不能访问。指向地址0的指针就是空指针。

可以用“NULL”关键字对任何类型的指针进行赋值,NULL实际上就是整数0。

int *p1=NULL;char *pc=NULL;int *p2=0;

指针可以作为条件表达式使用,如果指针的值为NULL,则相当于假,反之为真。

if(p)等价于if(p!=NULL)。

指针作为函数参数:

#include<bits/stdc++.h>

using namespace std;

void swap(int *p1,int *p2)//p1 形参

{

int temp=*p1;

*p1=*p2;

*p2=temp;

}

int main(){

int n=3,m=4;//n实参

swap(&n,&m);

cout<<m<<' '<<n<<endl;

return 0;

}

指针和数组:

数组的名字是一个指针常量,指向数组的初始地址。

T a[n];

a的类型是T*

可以用a给T*类型的指针赋值

a是编译时其值就已经确定了的,不能够对a进行赋值

作为函数形参时,T*p和T p[ ]是等价的

void(int T*p){cout<<sizeof(p)};

void(int T p[ ]){cout<<sizeof(p)};

代码:

#include<bits/stdc++.h>

using namespace std;

int main(){

int a[200];int *p;

p=a;//p指向数组a的初始地址,亦指p指向了a[0]

*p=10;//使得a[0]=10

*(p+1)=20;//a[1]=20

p[0]=30;//p[i]和*p[i+1]是等效的,使得a[0]=30

p[4]=40;//a[4]=40

for(int i=0;i<10;i++)//对数组a的前十个元素进行赋值

*(p+i)=i;

++p;//p指向a[1]

cout<<p[0]<<endl;//输出1      p[0]等效于*p,p[0]即是a[1]

p=a+6;//p指向a[6]

cout<<*p<<endl;//输出6

return 0;

}

翻转

代码:

#include<bits/stdc++.h>

using namespace std;

void reverse(int *p,int size){

for(int i=0;i<size;i++){

int temp=p[i];

p[i]=p[size-i];

p[size-i]=temp;

}

}

int main(){

int a[5]={1,2,3,4,5};

reverse(a,sizeof(a/sizeof(int));

for(int i=0;i<5;i++)

cout<<*(a+i)<<',';

return 0;

}

动态数组:

指针可以动态申请空间,如果一次申请多个变量空间,系统给的地址是连续的,就可以当做数组使用,这就是传说中的动态数组的一种。

#include<bits/stdc++.h>

using namespace std;

int n;

int *a;

int main(){

cin>>n;

a=new int[n+1];

for(int i=1;i<=n;i++)

cin>>a[i];

for(int i=2;i<=n;i++)

a[i]+=a[i-1];

for(int i=1;i<=n;i++)

cout<<a[i]<<' ';

return 0;

}

动态数组的优点:

在OI中,对于大数据可能超空间的情况是比较纠结的事,用小数组只能得部分分,大数组可能爆空间同时又爆零,使用动态数组可以在确保小数据没问题的前提下,尽量满足大数据的需求。

指针和字符串:

字符串常量的类型是char*

字符数组名的类型是char*,就是一个地址。

注意:可以使用字符数组名或者字符指针输出一个字符串,而面对一个数值型数据是不能企图用数组名输出它的全部元素的。

如:

int i[10];

……

printf("%d\n",i);

代码:

#include<bits/stdc++.h>

using namespace std;

int main(){

char*p="please input your name:\n";

cout<<p;

char name[20];

char*pname=name;

cin>>pname;

cout<<"your name is "<<pname;

return 0;

}

字符串指针作函数参数:

将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数或用指向字符的指针变量做参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。

例题:

输入一个长度最大为100的字符串,以字符数组的方式存储,再将字符串倒序储存,输出倒序储存后的字符串(这里以字符指针为函数参数)

#include<bits/stdc++.h>

using namespace std;

void swapp(char &a,char &b){

char t;

t=a;

a=b;

b=t;

}

void work(char *str){

int len=strlen(str);

for(int i=0;i<=len/2;i++)

swapp(str[i],str[len-i-1]);

}

int main(){

char s[110];

char *str=s;

gets(s);

work(str);

printf("%s",s);

return 0;

}

字符串操作函数:


转自视频(侵删)

void指针:

void*p;

可以用任何类型的指针对void指针进行赋值或初始化

double d=1.54;

void*p=&d;

void*p1;

p1=&d;

因sizeof(void)没有定义,所以对于void*类型的指针p,*p无定义,++p,--p,p+=n,p-=n,p+n,p-n等均无定义。

内存操作库函数memset:

头文件cstring中声明:

void*memset(void*dest,int ch,int n);

将从dest开始的n个字节,都设置成ch,返回值是dest,ch只有最低的字节起作用。

内存操作库函数memcpy:

头文件cstring中声明:

void*memcpy(void*dest,void*src,int n);

将地址src开始的n个字节,拷贝到地址dest。返回值是dest。

注意!有缺陷,在dest区间和src区间有重叠时可能出问题!

试手:

1.输入n个数,使用指针变量访问输出。

#include<bits/stdc++.h>

using namespace std;

int a[111],n;

int main(){

cin>>n;

for(int i=1;i<=n;i++)

cin>>a[i];

int *p=&a[1];

for(int i=1;i<=n;i++){

cout<<*p<<' ';

p++;

}

return 0;

}

2.无类型指针运用:

#include<bits/stdc++.h>

using namespace std;

int a=10;

double b=3.5;

void *p;

int main(){

p=&a;

cout<<*(int *)p<<endl;

p=&b;

cout<<*(double*)p<<endl;

return 0;

}

3.多重指针:

#include<bits/stdc++.h>

using namespace std;

int a=10;

int *p;

int **pp;

int main(){

p=&a;

pp=&p;

cout<<a<<' '<<*p<<' '<<**pp;

return 0;

}

多重指针除了可以多次间接访问数据,OI上主要的应用是动态的多维数组。

4.访问数组

#include<bits/stdc++.h>

using namespace std;

int main(){

int a[5],i,*pa=a;

for(i=0;i<5;i++)

cin>>a[i];

for(i=0;i<5;i++)

cout<<*(pa+i);

return 0;

}

5.行列转换问题

描述:矩阵可以认为是N*M的二维数组,现在有一个巨大但稀疏的矩阵。

N,M范围是:1<=N,M<=100000,有K个位置有数据,K的数据范围是1<=K<=100000。

矩阵输入的方式是从上到下,从左到右扫描,极路由数据的坐标位置和值,这是按照行优先的方式保存数据的。现在要求按照列优先的方式输出数据,即从左到右,从上到下扫描,输出有数据的坐标和值。

输入格式:

第一行,三个整数N,M,K;下面有K行,每行三个整数:a,b,c,表示的a行b列有数据c,数据在int范围内,保证是行优先的次序。

输出格式:

一行,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

样例解释:


分析:

由于N,M可能会很大,直接开二维数组太大,不可行。解决问题的方法有很多种,下面的程序使用了指针和动态数组,根据每一列的实际数据个数来申请该列的空间,使每列的数组长度不同。算法是O(N+M+K)的时间复杂度(即程序的运算量)。

参考程序:

#include<cstdio>

using namespace std;

const int LP=100001;

int n,m,k;

int x[LP],y[LP],d[LP];

int c[LP];//每列的数据个数

int *a[LP];//每列一个指针,准备申请数组

/*这里固定大小为LP的一个指针数组,占用空间为5*4*LP字节,约2M

还可以在输入n,m后,在申请动态数组,当n,m较小时,占用更小的空间

a[i]表示第i列的指针*/

int main() {

scanf("%d%d%d",&n,&m,&k);

for(int i=1; i<=k; i++) {

scanf("%d%d%d",&x[i],&y[i],&d[i]);//x [i]和y[i]是第i个数据所在的行号和列号

c[y[i]]++//统计c数组中每列数据的个数;

}

for(int i=1; i<=m; i++)

a[i]=new int [c[i]];//第i列指针申请数组空间

for(int i=1; i<=k; i++) {//收集k个数据到相应的列中

*a[y[i]]=d[i];//数据放在相应列的数组中,也可以写成a[y[i]][0]=d[i]

a[y[i]]++;//数组指针移到下一个位置

}

for(int i=1; i<=m; i++) {//列优先

a[i]-=c[i];//指针回到每列的前面

for(int j=1; j<=c[i]; j++,a[i]++)

printf("%d ",*a[i]);

}

return 0;

}

说明:特别的,可以把指针当数组名用。

指针与函数:

编写一个函数,将三个整型变量排序,并将三者中的最小值赋给第一个变量,次小值赋给第二个变量,最大值赋给第三个变量。

#include<bits/stdc++.h>

using namespace std;

void swap(int *x,int*y){

int t=*x;

*x=*y;

*y=t;

}

void sort(int *x,int *y,int *z){

if(*x>*y)swap(x,y);

if(*x>*z)swap(x,z);

if(*y>*z)swap(y,z);

}

int main(){

int a,b,c;

cin>>a>>b>>c;

sort(&a,&b,&c);

cout<<a<<' '<<b<<' '<<c;

return 0;

}

函数返回指针:

一个函数可以返回整数值,字符值,实型值等,也可以返回指针联系的数据(即地址)。

定义:

类型名*函数名(参数列表);

int *a(int a,int b)

a是函数名,调用它后得到一个指向整型数据的指针(地址)

注意:在*a的两侧没有括号;在a的两侧分别为*运算符和()运算符,由于()的优先级高于*,因此a先与()结合。在函数前面有一个*,表示此函数是返回指针类型的函数。最前面的int表示返回的指针指向整型变量。

例题:

编写一个函数,用于在一个包含N个整数的数组中找到第一个质数,若有则返回函数的地址,否则返回NULL(空指针)。

#include<bits/stdc++.h>

using namespace std;

int n,a[10001];

bool isprime(int n) {

if(n<2)return false;

if(n==2) return true;

for(int i=2; i<=sqrt(n); i++)

if(n%i==0)

return false;

return true;

}

int *find() {

for(int i=1; i<=n; ++i)

if(isprime(a[i]))

return &a[i];

return NULL;

}

int main() {

cin>>n;

for(int i=1; i<=n; i++)

cin>>a[i];

int *p=find();

if(p!=NULL)

cout<<p<<endl<<*p<<endl;

else

printf("can't find!");

return 0;

}

函数指针和函数指针数组:

使用函数指针调用函数示例:

#include<bits/stdc++.h>

using namespace std;

int t(int a){

return a;

}

int main(){

cout<<t<<endl;//显示函数地址

int(*p)(int a);//定义函数指针变量p

p=t;//将t的地址赋给函数指针p

cout<<p(5)<<","<<(*p)(10)<<endl;

//输出p(5)是C++的写法,(*p)(10)是兼容C

return 0;

}

函数指针的基本操作:

1.声明函数指针

注意:一定要这样声明!int (*fp)(int)

2.获取函数的地址

3.使用函数指针来调用函数

使用typedef定义函数指针示例:

#include<bits/stdc++.h>

 using namespace std; 

int sum(int a,int b){ 

 return a+b; 

typedef int (*LP)(int,int); 

int main(){ 

 LP p=sum;

 cout<<p(2,5); 

 return 0;

 }

在软件开发编程中,函数指针的一个广泛应用是菜单功能函数的调用,通常选择菜单的某个选项都会调用相应的功能函数,而且有些软件的菜单会根据情况发生变化(上下文敏感)如果使用switch/case或if语句处理起来会比较复杂,不利于代码的维护,可以考虑使用函数指针数组方便灵活的实现。

结构体指针:

当一个指针变量用来指向一个结构体变量时,称之为结构体指针。

定义:

结构体名*结构体指针变量名

char stu {

char name[20];

char sex;

float score;

} *p;

char stu {

char name[20];

char sex;

float score;

} ;

stu *p;

结构体指针变量必须要赋值后才能使用,赋值是把结构体变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。

如p=&stu是错误的;

引用结构体指针变量指向的结构体变量的成员的方法如下:

1.指针名->成员名

2.(*指针名).成员名

示例:

#include<bits/stdc++.h>

using namespace std;

struct stu{

char name[20];

char sex;

float score;

}s[3]={{"xiaoming",'f',356},

{"xiaoliang",'f',350},{"xiaohong",'m',0}};

int main(){

stu*p;

printf("Name      Sex    Score\n");

for(p=s;p<s+3;p++)

printf("%-9s%3c%7d\n",p->name,p->sex,p->score);

return 0;

}

这里p++起到了移动指针的作用

自引用结构:

自引用结构是实现动态数据结构的基石,包括动态的链表,堆,栈,树,无不是自引用结构的具体实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容