指针定义

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var-name;

在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */

所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

C和C++对空指针的定义稍有不同:

  • 在C语言中:#define NULL (void*)0
  • 在C++语言:#define NULL 0

数字0是唯一允许可以直接赋给指针的数值。

另外C++中类成员指针等类型是C语言中所没有的。

C++使用

通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。

      

指针的本质是变量,可以是各种数据类型,定义一个指针 "*ip",其中 "ip" 需要赋于一个地址(可以用 & 符号获取其他变量的地址再赋值给 ip),而 "*ip" 是一个具体的值,即读取地址后获得的值。

C++ 中允许声明指向函数的指针,被称为函数指针。

函数指针的声明类似于函数的声明,只不过将函数名变成了 (*指针名)。定义方式如下:

data_types (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);

这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。之后,我们就可以通过函数指针来调用函数。

空指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。

       

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,可以使用 if 语句,if(ptr)     /* 如果 ptr 非空,则完成 */;            if(!ptr)    /* 如果 ptr 为空,则完成 */。

如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

用 vs2013 可以看到,NULL 的定义就是:

define NULL 0

指针运算

指针是一个用数值表示的地址。因此,可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

常在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。递增变量指针,可顺序访问数组中的每一个元素。

同样地,对指针进行递减运算,即把值减去其数据类型的字节数。

指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:

                   

指针VS数组

指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。

由于一个数组名对应一个指针常量,只要不改变数组的值,仍然可以用指针形式的表达式。

char *names[MAX] 是指针数组, 它的本质是存储指针的数组, 既存储 char 类型的指针的数组, 数组内的每个元素都是一个指针指向一个存储 char 类型的地址。

C和C++中的运算符优先级顺序有细微差别:

int *ptr[3];

由于 C++ 运算符的优先级中,* 小于 [],所以 ptr 先和 [] 结合成为数组,然后再和 int * 结合形成数组的元素类型是 int * 类型,得到一个叫一个数组的元素是指针,简称指针数组。

int *(ptr[3]);

这个和上面的一样,优先级顺序是 * 小于 (),() 等于 []ptr 先和 [] 结合成为数组,然后再和 int * 结合形成数组的元素类型是 int * 类型,得到一个叫一个数组的元素是指针。

int (*ptr)[3];

这个就不一样了,优先级顺序是 * 小于 ()() 等于 []()[] 的优先级一样,但是结合顺序是从左到右,所以先是 () 里的 *ptr 结合成为一个指针,然后是 (*ptr)[] 相结合成为一个数组,最后叫一个指针 ptr 指向一个数组,简称数组指针。

指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。

     

三种传递方式

值传递:

形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参),参数的值只能传入,不能传出。当函数内部需要修改参数,并且不希望这个改变影响调用者时,采用值传递。

指针传递:

形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作

引用传递:

形参相当于是实参的"别名",对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

例子

1、char*a[]={"work","at","alibaba"};  char**pa=a;  pa++;  printf("%s",*pa);

对编译器来说没有数组这一概念,数组都被看成指针,所以a[ ]就是*a,那么就是**a换成了**pa,pa即是a,换个名字而已,根据数组的++,也就是取a[1][ ]的值,即“at”。

【C/C++】指针的更多相关文章

  1. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  2. enote笔记法使用范例(2)——指针(1)智能指针

    要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...

  3. C++虚函数和函数指针一起使用

    C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...

  4. C++11 shared_ptr 智能指针 的使用,避免内存泄露

    多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...

  5. c 数组与指针的使用注意事项

    数组变量和指针变量有一点小小的区别 所以把数组指针赋值给指针变量的时候千万要小心 加入把数组赋值给指针变量,指针变量只会包含数组的地址信息 而对数组的长度一无所知 相当于指针丢失了一部分信息,我们把这 ...

  6. Marshal.Copy将指针拷贝给数组

    lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...

  7. C++智能指针

    引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...

  8. EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针

    一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...

  9. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  10. 智能指针unique_ptr的用法

    unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...

随机推荐

  1. php 计算文件大小

    计算文件大小 主要计算文件的 size 大小,默认的为Bytes的,所以运用三元运算符,来进行转换. 转换成 Bytes->KB->MB->GB /** * @param $size ...

  2. js 时间格式化成字符串

    我用的简单的直接的字符串格式转化 function getTimeStr() { var date = new Date(); var year = date.getFullYear(); var m ...

  3. Python面向对象继承案例

    面向对象三大特性 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中 继承 实现代码的重用,相同的代码不需要重复的编写 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活 ...

  4. 换个语言学一下 Golang (11)——使用包和测试

    Go天生就是为了支持良好的项目管理体验而设计的. 包 在软件工程的实践中,我们会遇到很多功能重复的代码,比如去除字符串首尾的空格.高质量软件产品的特点就是它的部分代码是可以重用的,比如你不必每次写个函 ...

  5. 【MySQL】数据库事务深入分析

    一.前言 只有InnoDB引擎支持事务,下边的内容均以InnoDB引擎为默认条件 二.常见的并发问题 1.脏读 一个事务读取了另一个事务未提交的数据 2.不可重复读 一个事务对同一数据的读取结果前后不 ...

  6. Vue-cli3 中 通过在index.html添加的script js文件 如何在组件内使用不会 xxx is not defined错误

    以jQuery 为例 第一种方法 更改webpack配置信息 1.在vue.config.js中(如果没有 请在根目录新建)配置如下信息 // const webpack = require('web ...

  7. Django之 RESTful规范

    RESTful 规范 一.什么是RESTful REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为"表征 ...

  8. spring和springmvc

    1. 为什么使用Spring ? 1). 方便解耦,简化开发 通过Spring提供的IoC容器,可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合. 2). AOP编程的 ...

  9. Hibernate各种状态(瞬时状态、持久化状态、游离|托管状态)之间的转换

  10. JQuery-zTree.js使用范例

    JQuery-zTree.js使用范例 实现Tree树的插件很多,比如常见的UI:Layui.ElementUI.iView ... .这里我们介绍一个小巧的构建Tree树的插件 zTree.js z ...