指针是C/C++编程中的重要概念之一,也是最容易产生困惑并导致程序出错的问题之一。利用指针编程可以表示各种数据结构,通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯;指针能够灵活的操作内存,合理的操作内存能够使程序更高效。

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

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

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的理解(也是对于指针类型和指针所指向的类型的理解),一个指针包含两部分,地址和值,指针声明时声明的是一个地址变量(指针就是地址),在使用时使用的是指针所指向的值。或者说指针包含两个类型:指针类型和指针所指向的类型,声明时是声明指针类型,使用时是使用指针所指向的类型。

 int p[];//p先和[]结合,说明p是一个数组,再和int结合,所以p是一个int型数组

 int* p[];//优先级[]比*高,p是数组,再加上int*,可以称它为指针数组,数组的每一个元素的值都为指针(地址)

 int (*p)[];//*p可以看作是普通变量,就回到第三种情况(int p[3]),但是这里p是指针变量,它指向的是一个包含3个整型数值的数组。可以称它为数组指针,数组中每一个元素的值为普通整型值。

 int** p;//int*代表指针类型,*是指针类型,所以p是指向int型指针的指针,p指向的类型是int*类型(int型指针)

 int p(int);//这很明显是一个返回类型为int,并且带一个int型参数的函数

 int (*p)(int);//p是函数指针,指向的是返回值为int并且带一个int参数的函数。这个声明包含两部分:函数变量+函数地址变量(姑且把函数也看做是变量)

 int* (*p(int))[];//这个有点复杂,它仍然是一个函数指针。从*p(int)看,它是函数指针,带一个int参数;然后看[],说明函数返回值为数组,然后返回类型为int*。所以p是一个指向返回值为int*型指针数组,并且带一个int型参数的函数的指针
 

在平常使用中,我们只需要理解前面几个就可以了,太复杂的指针基本用不到,可读性也不好。

下面是一些指针的简单例子。

Demo1 &和*操作符

 #include
int main()
{
using namespace std;
int updates = ;
int *p_updates;
p_updates = &updates; cout<<"Values: updates = "<<updates;
cout<<", *p_updates = "<<*p_updates<<endl; cout<<"Address: &updates = "<<&updates;
cout<<",p_updates = "<<p_updates<<endl; *p_updates = *p_updates + ;
cout<<"Now updates = "<<updates<<endl;
}

Demo2

#include
int main()
{
using namespace std;
int higgens = ;
int* pt = &higgens;//是对pt进行赋值,而不是*pt。等价于int* pt; pt = &higgens; cout<<"Value of higgens = "<<higgens<<endl;
cout<<"Addnress of higgens = "<<&higgens<<endl;
cout<<"Value of *pt = "<<*pt<<endl;
cout<<"Value of pt = "<<pt<<endl; return ;
}
 

Demo3  new和delete操作符

 #include
int main()
{
using namespace std;
int* pt = new int;
*pt = ;
cout<<"int value = "<<*pt<<",and location = "<<pt<<endl;
double* pd = new double;
*pd = 5.234;
cout<<"double value = "<<*pd<<",and location = "<<pd<<endl;
delete pt;
delete pd;
return ;
}

Demo4 动态数组

 #include
int main()
{
using namespace std;
double* p3 = new double[];
p3[] = 0.2;
p3[] = 0.5;
p3[] = 0.8; cout<<sizeof(p3)<<endl;
cout<<"p3[1] is "<<p3[]<<".\n";
p3 = p3 + ;//增加一位地址,这里的一位的单位标准是按照数组的一个元素占内存的字节数。
cout<<"Now p3[0] is "<<p3[]<<" and p3[1] is "<<p3[]<<".\n";
cout<<sizeof(p3)<<endl;
p3 = p3 -;
delete[] p3;
return ;
}

Demo 5 数组和指针

 #include
int main()
{
using namespace std;
double wages[] = {10000.0,20000.0,30000.0};
short stacks[] = {,,};
//两种方式去获取数组的地址——数组名或对数组首元素进行求址运算
double* pw = wages;
short* ps = &stacks[]; cout<<"pw = "<<pw<<",*pw = "<<*pw<<endl;//打印出数组第一个元素的值和地址
pw = pw + ;
cout<<"add 1 to the pw pointer:\n";
cout<<"pw = "<<pw<<",*pw = "<<*pw<<"\n\n"; cout<<"ps = "<<ps<<",*ps = "<<*ps<<endl;//打印出数组第一个元素的值和地址
ps = ps + ;
cout<<"add 1 to the ps pointer:\n";
cout<<"pw = "<<ps<<",*ps = "<<*ps<<"\n\n"; //stacks[1] 等同于 *(stacks + 1)
cout<<"access two elements with array notation\n";
cout<<" stacks[0] = "<<stacks[]<<", stack[1] = "<<stacks[]<<endl;
cout<<"access two elements with pointer notation\n";
cout<<"*stacks = "<<*stacks<<",*(stacks + 1) = "<<*(stacks + )<<endl; cout<<sizeof(wages)<<" = size of wages array\n"; //24字节
cout<<sizeof(pw)<<" = size of pw pointer\n"; //4字节 return ;
}
 

Demo6 字符串和地址

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

Demo7 字符串和地址

 #include
#include
int main()
{
using namespace std;
char animal[] = "bear";
const char* bird = "wren";//bird存放字符串的地址
char* ps; cout<<animal<<" and ";
cout<<bird<<"\n"; cout<<"Enter a kind of animal:";
cin>>animal;
//cin>>ps;//没有为ps分配内存空间,这样做是错误的 ps = animal;//ps和animal指向同一个string和内存单元
cout<<ps<<"s!\n";
cout<<"Before using strcpy():\n";
cout<<animal<<" at "<<(int*)animal<<endl;//计算animal的地址,如果是char*类型,则打印出字符串
cout<<ps<<" at "<<(int*)ps<<endl; ps = new char[strlen(animal) + ];//分配新的内存空间
strcpy(ps,animal);//数组拷贝,如果这里把animal赋给ps,这样将改变ps的地址,我们将无法操作新分配的内存
cout<<"After using strcpy():\n"; //虽然animal和ps指向的是同一个string,但是它们的内存地址是不同的
cout<<animal<<" at "<<(int*)animal<<endl;
cout<<ps<<" at "<<(int*)ps<<endl; ps = animal; //这样是吧animal的地址给ps,改变了ps的地址
cout<<animal<<" at "<<(int*)animal<<endl;
cout<<ps<<" at "<<(int*)ps<<endl;
delete[] ps; return ;
}
 

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

 #include
struct inflatable
{
char name[];
float volume;
double price;
};
int main()
{
using namespace std;
inflatable* ps = new inflatable;//在运行时分配内存
cout<<"Enter name of inflatable item:";
cin.get(ps->name, );
cout<<"Enter volume in cubic feet:";
cin>>(*ps).volume;
cout<<"Enter price:$";
cin>>ps->price;
cout<<"Name:"<<(*ps).name<<endl;
cout<<"Volume:"<<ps->volume<<" cubic feet\n";
cout<<"Price:$"<<ps->price<<endl;
delete ps;
return ;
}

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

指针的值或者叫指针所指向的内存区或地址,是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在上例中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

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

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

指针本身所占有的内存区是指针本身占内存的大小,这个你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。

指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。

 

C++指针理解的更多相关文章

  1. [c++]this指针理解

    #include <iostream> using namespace std; /** * this 指针理解 */ class A{ int i; public: void hello ...

  2. 关于this指针理解

    1. this指针的用处: 一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果.this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将 ...

  3. 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

    浅谈 .NET 中的对象引用.非托管指针和托管指针   目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...

  4. c/c++指针理解

    指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占 ...

  5. C++ 结构体指针理解

    上一篇基础链接https://www.cnblogs.com/xuexidememeda/p/12283845.html 主要说一下链表里面双重指针 先说一下结构体 typedef struct LN ...

  6. C语言 指针理解

    1.指针 指针全称是指针变量,其实质是C语言的一种变量.这种变量比较特殊,通常他的值会被赋值为某个变量的地址值(p = &a),然后我们可以使用 *p 这样的方式去间接访问p所指向的那个变量. ...

  7. golang:指针理解总结

    指针的定义 指针是一个代表着某个内存地址的值.这个内存地址往往是在内存中存储的另一个变量的值的起始位置. go指针是提供操作数据的基本桥梁.因为go很多调用,往往复制一份对象,例如函数的参数,如果没有 ...

  8. c/c++ 指针理解(1)

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

  9. 【js】关于this指针-理解call、apply、bind

    首次讲解视频,听了一下,录音声音太小(暂不知道该怎么调节),老是咳咳,不太流畅.暂时不理想,日后继续努力.(能写出来还不够,还要会说出来) 首先,this指针只存在于函数(function)中.用于指 ...

随机推荐

  1. [UE4]使用另一个相机Scene Capture Component 2D当小地图

    挂一个相机(Scene Capture Component 2D)在人物角色的正上方,相机朝下,让UI上的某一块区域看到相机所显示的内容. 一.在人物角色正上方添加相机组件Scene Capture ...

  2. 00001 - Linux下 环境变量/etc/profile、/etc/bashrc、~/.bashrc的区别

    ①/etc/profile: 该文件登录操作系统时,为每个用户设置环境信息,当用户第一次登录时,该文件被执行.也就是说这个文件对每个shell都有效,用于获取系统的环境信息. # /etc/profi ...

  3. 02-创建String对象

    创建一个String对象实在是太简单了,就是因为简单,所以有很多java程序员做了好几年的开发,也没有注意这些小细节问题 String字符串的本质就是char数据对象, 那么char[0]数组当中的一 ...

  4. 表单(同步提交)和AJAX(异步提交)示范

    表单提交(同步提交) HTML文件: PHP文件: 这样就能接收到HTML里输入的内容,注意: FORM表头method为POST,PHP文件获取的方法就是$_POST,method为GET,PHP的 ...

  5. Spqrk笔记

    LSM:Least square method 最小二乘法 ALS:Alternating Least Squares 交替最小二乘法 http://blog.csdn.net/dreamer2020 ...

  6. KVM总结-KVM性能优化之磁盘IO优化

    前面讲了KVM CPU(http://blog.csdn.net/dylloveyou/article/details/71169463).内存(http://blog.csdn.net/dyllov ...

  7. 2-Zookeeper、HA安装

    1.Zookeeper安装 1.解压 zookeeper 到安装目录中/opt/app/zookeeper 中. 2.在安装目录下创建data和logs两个目录用于存储数据和日志: cd /opt/a ...

  8. linux安装python3 ,安装IPython ,安装jupyter notebook

    安装python3    下载到 /opt/中 1.下载python3源码,选择3.6.7因为ipython依赖于>3.6的python环境wget https://www.python.org ...

  9. spring事务管理实现原理-源码-传播属性

    转载请标识 https://me.csdn.net/wanghaitao4j https://blog.csdn.net/wanghaitao4j/article/details/83625260 本 ...

  10. python中logging模块的一些简单用法

    用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么了,但是当我需要看大量的地方或者在一个文件中查看的时候,这时候print就不大方便了,所 ...