指针 (整理版)

指针是C/C++编程中的重要概念之中的一个,也是最easy产生困惑并导致程序出错的问题之中的一个。

利用指针编程可以表示各种数据结构,通过指针可使用主调函数和被调函数之间共享变量或数据结构。便于实现双向数据通讯。指针可以灵活的操作内存,合理的操作内存可以使程序更高效。

1.指针的概念

本质上讲指针也是一种变量,普通的变量包括的是实际的数据,而指针变量包括的是内存中的一块地址,这块地址指向某个变量或者函数,指针就是地址。指针是一个指示器,它告诉程序在内存的哪块区域能够找到数据。

2.指针的内容

指针的内容包括4部分:指针的类型,指针所指向的类型,指针的值,指针本身所占有的内存区。在初学指针时,指针的类型和指针所指向的类型是极easy搞混淆的,弄清楚这些概念。有助于我们正确的使用指针。

3.指针的类型和指针所指向的类型

从语法上讲,指针的类型是指把指针声明语句中的指针名字去掉所剩下的部分。

指针指向的是一块内存区域,指针所指向的类型取决于这块内存在编译时是什么类型,比方一个int*类型指针所指向的类型是int。

以下我来就一些样例来对这两个概念进行说明。

  1. int p;//这不过一个普通的变量
  2. int* p;//int*也表示一种数据类型:int指针类型。所以p的类型为:int*类型,p所指向的类型为int型

到这里,略微暂停一下。教大家一种怎样看待指针类型和指针所指向的类型的方法。

(我自己的理解)

就上面这个int*p样例来说。它能够写成int* p,也能够写成int
*p。

第一种的理解偏向于地址。就是p是一个地址变量。p表示一个十六进制地址;另外一种的写法偏向于值。*p是一个整型变量,它可以表示一个整型值。

这两种写法都正确,仅仅是理解上不同,可是我觉得,在理解指针类型和指针所指向的类型这两个概念时,全然能够把它们两个结合起来。

想想我们使用指针的步骤:声明指针,为指针赋值。然后使用指针所指向的值。

我们都知道指针是一种复合数据类型。它必须和主要的类型结合才干构成指针类型。

那么int*就是一种复合的数据类型——整型指针类型。

这样就好解释第一种写法了,在声明时,int* p,直接声明p变量为整型指针类型,这是第一步。

第二步就是为指针赋值了,p是指针类型,它存放的是地址,这里如果我这样为它赋值:p = &普通变量;(比方int a = 5;p=&a;)。

第三步使用指针。在C++ Primer中具体的解释了*是解除引用的运算符(我的理解是地址解析运算符),假设p是地址,那么*p就是实际的值(比方上面相应的*p = 5)。对于刚開始学习的人来说。在理解它的含义时,全然能够跨过这一步,上面说了在声明指针时int*
p和int *p这两种写法都能够。在声明时我偏向第一种理解,在使用时我偏向另外一种理解:毕竟我们使用的是值,而*p就是这个值。

我的结论:对于int* p和int *p的理解(也是对于指针类型和指针所指向的类型的理解),一个指针包括两部分。地址和值。指针声明时声明的是一个地址变量(指针就是地址),在使用时使用的是指针所指向的值。或者说指针包括两个类型:指针类型和指针所指向的类型。声明时是声明指针类型,使用时是使用指针所指向的类型。

  1. int p[3];//p先和[]结合,说明p是一个数组,再和int结合。所以p是一个int型数组
  2. int* p[3];//优先级[]比*高,p是数组,再加上int*,能够称它为指针数组。数组的每个元素的值都为指针(地址)
  3. int (*p)[3];//*p能够看作是普通变量,就回到第三种情况(int
    p[3]),可是这里p是指针变量,它指向的是一个包括3个整型数值的数组。能够称它为数组指针,数组中每个元素的值为普通整型值。
  4. int** p;//int*代表指针类型,*是指针类型,所以p是指向int型指针的指针。p指向的类型是int*类型(int型指针)
  5. int p(int);//这非常明显是一个返回类型为int,而且带一个int型參数的函数
  6. int (*p)(int);//p是函数指针,指向的是返回值为int而且带一个int參数的函数。这个声明包括两部分:函数变量+函数地址变量(姑且把函数也看做是变量)
  7. int* (*p(int))[3];//这个有点复杂。它仍然是一个函数指针。从*p(int)看,它是函数指针,带一个int參数;然后看[],说明函数返回值为数组,然后返回类型为int*。

    所以p是一个指向返回值为int*型指针数组,而且带一个int型參数的函数的指针

在寻常使用中。我们仅仅须要理解前面几个就能够了,太复杂的指针基本用不到,可读性也不好。

以下是一些指针的简单样例。

Demo1 &和*操作符

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. int updates = 6;
  6. int *p_updates;
  7. p_updates = &updates;
  8. cout<<"Values: updates = "<<updates;
  9. cout<<", *p_updates = "<<*p_updates<<endl;
  10. cout<<"Address: &updates = "<<&updates;
  11. cout<<",p_updates = "<<p_updates<<endl;
  12. *p_updates = *p_updates + 1;
  13. cout<<"Now updates = "<<updates<<endl;
  14. }

Demo2

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. int higgens = 5;
  6. int* pt = &higgens;//是对pt进行赋值。而不是*pt。

    等价于int*
    pt; pt = &higgens;
  7. cout<<"Value of higgens = "<<higgens<<endl;
  8. cout<<"Addnress of higgens = "<<&higgens<<endl;
  9. cout<<"Value of *pt = "<<*pt<<endl;
  10. cout<<"Value of pt = "<<pt<<endl;
  11. return 0;
  12. }

Demo3  new和delete操作符

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. int* pt = new int;
  6. *pt = 1001;
  7. cout<<"int value = "<<*pt<<",and
    location = "<<pt<<endl;
  8. double* pd = new double;
  9. *pd = 5.234;
  10. cout<<"double value = "<<*pd<<",and
    location = "<<pd<<endl;
  11. delete pt;
  12. delete pd;
  13. return 0;
  14. }

Demo4 动态数组

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. double* p3 =  new double[3];
  6. p3[0] = 0.2;
  7. p3[1] = 0.5;
  8. p3[2] = 0.8;
  9. cout<<sizeof(p3)<<endl;
  10. cout<<"p3[1] is "<<p3[1]<<".\n";
  11. p3 = p3 + 1;//添加一位地址。这里的一位的单位标准是依照数组的一个元素占内存的字节数。
  12. cout<<"Now p3[0] is "<<p3[0]<<"
    and p3[1] is "<<p3[1]<<".\n";
  13. cout<<sizeof(p3)<<endl;
  14. p3 = p3 -1;
  15. delete[] p3;
  16. return 0;
  17. }

Demo 5 数组和指针

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. double wages[3] = {10000.0,20000.0,30000.0};
  6. short stacks[3] = {3,2,1};
  7. //两种方式去获取数组的地址——数组名或对数组首元素进行求址运算
  8. double* pw = wages;
  9. short* ps = &stacks[0];
  10. cout<<"pw = "<<pw<<",*pw
    = "<<*pw<<endl;//打印出数组第一个元素的值和地址
  11. pw = pw + 1;
  12. cout<<"add 1 to the pw pointer:\n";
  13. cout<<"pw = "<<pw<<",*pw
    = "<<*pw<<"\n\n";
  14. cout<<"ps = "<<ps<<",*ps
    = "<<*ps<<endl;//打印出数组第一个元素的值和地址
  15. ps = ps + 1;
  16. cout<<"add 1 to the ps pointer:\n";
  17. cout<<"pw = "<<ps<<",*ps
    = "<<*ps<<"\n\n";
  18. //stacks[1] 等同于 *(stacks + 1)
  19. cout<<"access two elements with array notation\n";
  20. cout<<" stacks[0] = "<<stacks[0]<<",
    stack[1] = "<<stacks[1]<<endl;
  21. cout<<"access two elements with pointer notation\n";
  22. cout<<"*stacks = "<<*stacks<<",*(stacks
    + 1) = "<<*(stacks + 1)<<endl;
  23. cout<<sizeof(wages)<<"
    = size of wages array\n";    //24字节
  24. cout<<sizeof(pw)<<"
    = size of pw pointer\n";            //4字节
  25. return 0;
  26. }

Demo6 字符串和地址

  1. #include
  2. int main()
  3. {
  4. using namespace std;
  5. char flower[10] = "rose";
  6. //字符串发送地址给cout对象。
  7. //在cout和多数C++表达式中,char数组名、指向char的指针以及用引號括起来的字符串常量都被解释为字符串第一个字符的地址
  8. cout<<flower<<"s are red.\n";
  9. return 0;
  10. }

Demo7 字符串和地址

  1. #include
  2. #include
  3. int main()
  4. {
  5. using namespace std;
  6. char animal[20] = "bear";
  7. const char* bird = "wren";//bird存放字符串的地址
  8. char* ps;
  9. cout<<animal<<" and ";
  10. cout<<bird<<"\n";
  11. cout<<"Enter a kind of animal:";
  12. cin>>animal;
  13. //cin>>ps;//没有为ps分配内存空间。这样做是错误的
  14. ps = animal;//ps和animal指向同一个string和内存单元
  15. cout<<ps<<"s!\n";
  16. cout<<"Before using strcpy():\n";
  17. cout<<animal<<" at "<<(int*)animal<<endl;//计算animal的地址,假设是char*类型,则打印出字符串
  18. cout<<ps<<"
    at "<<(int*)ps<<endl;
  19. ps = new char[strlen(animal)
    + 1];//分配新的内存空间
  20. strcpy(ps,animal);//数组拷贝,假设这里把animal赋给ps,这样将改变ps的地址。我们将无法操作新分配的内存
  21. cout<<"After using strcpy():\n";
  22. //尽管animal和ps指向的是同一个string。可是它们的内存地址是不同的
  23. cout<<animal<<" at "<<(int*)animal<<endl;
  24. cout<<ps<<"
    at "<<(int*)ps<<endl;
  25. delete[] ps;
  26. return 0;
  27. }

Demo8 动态结构 ->指向操作符

  1. #include
  2. struct inflatable
  3. {
  4. char name[20];
  5. float volume;
  6. double price;
  7. };
  8. int main()
  9. {
  10. using namespace std;
  11. inflatable* ps = new inflatable;//在执行时分配内存
  12. cout<<"Enter name of inflatable item:";
  13. cin.get(ps->name,
    20);
  14. cout<<"Enter volume in cubic feet:";
  15. cin>>(*ps).volume;
  16. cout<<"Enter price:$";
  17. cin>>ps->price;
  18. cout<<"Name:"<<(*ps).name<<endl;
  19. cout<<"Volume:"<<ps->volume<<"
    cubic feet\n";
  20. cout<<"Price:$"<<ps->price<<endl;
  21. delete ps;
  22. return 0;
  23. }

4.指针的值(或称指针所指向的内存区)

指针的值或者叫指针所指向的内存区或地址,是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。

在32位程序里,全部类型的指针的值都是一个32位整数,由于32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址開始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX。就相当于说该指针指向了以XX为首地址的一片内存区域。我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个全然不同的概念。

在上例中,指针所指向的类型已经有了,但因为指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

以后,每遇到一个指针。都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

5.指针本身所占有的内存区

指针本身所占有的内存区是指针本身占内存的大小,这个你仅仅要用函数sizeof(指针的类型)測一下就知道了。

在32位平台里,指针本身占领了4个字节的长度。

指针本身占领的内存这个概念在推断一个指针表达式是否是左值时非常实用。

时间: 2024-11-05 14:42:32

指针 (整理版)的相关文章

个项目涉及到的50个Sql语句(整理版)

/*标题:一个项目涉及到的50个Sql语句(整理版)作者:爱新觉罗.毓华(十八年风雨,守得冰山雪莲花开)时间:2010-05-10地点:重庆航天职业学院说明:以下五十个语句都按照测试数据进行过测试,最好每次只单独运行一个语句.问题及描述:--1.学生表Student(S#,Sname,Sage,Ssex) --S# 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别--2.课程表 Course(C#,Cname,T#) --C# --课程编号,Cname 课程名称,T# 教师

翻新并行程序设计的认知整理版(state of the art parallel)

近几年,业内对并行和并发积累了丰富的经验,有了较深刻的理解.但之前积累的大量教材,在当今的软硬件体系下,反而都成了负面教材.所以,有必要加强宣传,翻新大家的认知. 首先,天地倒悬,结论先行:当你需要并行时,优先考虑不需要线程间共享数据的设计,其次考虑共享Immutable的数据,最糟情况是共享Mutable数据.这个最糟选择,意味着最差的性能,最复杂啰嗦的代码逻辑,最容易出现难于重现的bug,以及不能测试预防的死锁可能性.在代码实现上,优先考虑高抽象级别的并行库(如C++11的future,PP

LAMP搭建--未整理版

[[email protected] ~]#yum search  关键字   //安装过程中提示少哪个程序就搜关键字找包名 [[email protected] httpd-2.2.25]# ./configure --prefix=/usr/local/httpd --enable-so --enable-rewrite --enable-cgi --enable-charrset-lite --enable-ssl [[email protected] ~]#useradd -M -s /

一个项目涉及到的50个Sql语句(整理版)

/* 标题:一个项目涉及到的50个Sql语句(整理版) 说明:以下五十个语句都按照测试数据进行过测试,最好每次只单独运行一个语句. */ --1.学生表Student(S,Sname,Sage,Ssex) --S 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表 Course(C,Cname,T) --C --课程编号,Cname 课程名称,T 教师编号--3.教师表 Teacher(T,Tname) --T 教师编号,Tname 教师姓名 --4.成绩表

Chrome 快捷键 整理版

Chrome浏览器对Web前端开发调试非常的好用,有时候我就在想如果Chrome和MyEclipse这样的IDE工具一样,有很多快捷键该有多好.那样开发起来真是事半功倍,比如一个很简单的切换Sheet页签功能,相信很多朋友还是需要的. 今天随手一查,还真有,豆瓣上的这篇文章不错,收藏起来: Chrome 快捷键 整理版 http://www.douban.com/group/topic/13802153/

寿星天文历Java封装整理版

由于生活和工作的原因,"寿星天文历"我一直没有动,长时间的丢弃后,当重新拾起时,比较费劲.编程就是这样,思维的火花只在当初的那一瞬,一旦熄灭,重新再点燃断掉的思维是很困难的.因为人的"忘记"能力,真的是挺强的,有时回顾或维护以前的代码时,常常会感叹道:这是我写的吗?够牛逼,看不懂!呵呵,这时候注释的作用的凸显出来了,尽管如此有时仅仅靠注释找以前的思路也是很困难. 跑题了,那么,首先对于等着"寿星天文历"封装整理版代码的各位,说声抱歉.这回整理的代

任正非讲话稿 PDF整理版

任正非讲话稿 PDF整理版 任正非思想之路 这里收录了任正非讲话稿400余篇,从1994年到2018年,从深圳.中国到东南亚.非洲.欧洲.美洲,从研发.市场.服务到财经.人力资源.战略.内控与公共关系,从交换机.通讯设备.移动终端到人工智能.物联网,从2G.3G到4G.5G,从物理学.化学.数学到心理学.哲学,从……到……,穿越时空看华为大幕如何徐徐展开,观任正非大家成长进化,向优秀学习,与时代共舞. PDF截图如下: 下载链接: 点击下载 感谢: HuijieL 关于我 私人博客 技术微信公众

Hadoop每日一讨论整理版

这是我在几个QQ群发起的Hadoop每日一讨论小活动,每天中午2点左右发出一个关于Hadoop的知识片段,在此做一个整理. [每日一讨论]之计算框架(2013-5-21) 就计算框架而言,Hadoop目前比较成熟的只有离线计算框架MapReduce(通常运行时间在1min以上),以及构建在MapReduce之上支持sql的Hive.随着发展,实时计算(通常运行时间在0~5s)有了需求,于是诞生了仿照Google dremel实现的Apache Drill和Cloduera impala,Twit

endnote的安装和使用必备的几个步骤(简单有效整理版)

endnote:文献检索和管理工具 一 准备工作 endnote大客户版/破解版获取地址:http://blog.sina.com.cn/s/blog_6de000c20101n7ac.html 之所以要装大客户版是因为endnote是付费软件,但是这个版本是他提供给大客户的,大客户已经破解了的版本.功能并无二义. 如何安装:http://www.howsci.com/endnote-install.html 将endnote的library装到网盘中(为了数据安全,推荐安装):http://j