【C/C++】指针
指针定义
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
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++】指针的更多相关文章
- TODO:Golang指针使用注意事项
TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...
- enote笔记法使用范例(2)——指针(1)智能指针
要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...
- C++虚函数和函数指针一起使用
C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- c 数组与指针的使用注意事项
数组变量和指针变量有一点小小的区别 所以把数组指针赋值给指针变量的时候千万要小心 加入把数组赋值给指针变量,指针变量只会包含数组的地址信息 而对数组的长度一无所知 相当于指针丢失了一部分信息,我们把这 ...
- Marshal.Copy将指针拷贝给数组
lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...
- C++智能指针
引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...
- EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针
一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- 智能指针unique_ptr的用法
unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...
随机推荐
- centos 7 重新设置分区大小
一.基础概念Cent0S 7默认启用LVM2(Logical Volume Manager),把机器的一块硬盘分为两个区sda1和sda2,其中分区sda1作为系统盘/boot挂载,少量空间:sda2 ...
- (二)咋使用VUE中的事件修饰符
1,stop修饰符:阻止事件冒泡 首先我们要明确H5的事件是从内向外进行冒泡的,写一个简单的DEMO 当我们点击按钮时,事件从内向外冒泡,依次触发绑定的事件,控制台信息如下 现在我们在click后面添 ...
- Mysql系列(四) —— MySQL的Charset和Collation
本文转载自:再见乱码:5分钟读懂MySQL字符集设置 一.内容概述 在MySQL的使用过程中,了解字符集.字符序的概念,以及不同设置对数据存储.比较的影响非常重要.不少同学在日常工作中遇到的" ...
- WPF USB设备采集开源工具介绍
最近项目需要需要试试采集电脑USB 设备信息 找到了 开源 示例 代码非常好 公共库也很友好 可能知名度太低 才4star https://github.com/vurdalakov/usbd ...
- 轨迹条(Trackbar)
1.创建 createTrackbar(轨迹条名称,窗口名称,滑动条的初始位置,滑动条的最大值,回调函数XXX,不懂) 其中回调函数为 void XXX(滑动条的位置,用户数据)在c++中函数名为指向 ...
- WindowsXP序列号产生原理(椭圆曲线法)
WindowsXP序列号产生原理(椭圆曲线法) 来源 https://blog.csdn.net/zhiyuan411/article/details/5156330 参考 https://www. ...
- java8使用stream的collect进行list转map注意事项
1.创建Person类 package com.xkzhangsan.normal.collectors; public class Person { private Integer id; priv ...
- Go的基础类型
1. 命名 Go语言中的函数名.变量名.常量名.类型名.语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母.数字或 ...
- spring框架学习(一)——IOC/DI
什么是Spring框架: Spring是一个基于IOC和AOP的结构J2EE系统的框架: IOC 反转控制 是Spring的基础,Inversion Of Control,简单说就是创建对象由以前的程 ...
- Python语言控制运算的优先级
Python语言碰上计算式同时出现在一个指令内时,除了括号"(".")"最优外,其余计算优先次序如下: 次方(**). 乘法.除法.求余数(%).求整数(//) ...