指针

static int i;
static int* ptr = &i;
  • 此处的*表示ptr是指针类型(地址类型),用来存放目标数据的地址
  • 其本身也有地址,所以又指向指针的指针;
  • *前面的 int 代表其指向的数据类型是 int 型,从目标i的起始单元地址取 int 数据类型字节长度的内容进行处理;
*ptr=3;
  • 此处的 * 表示指针运算,即寻址过程,按照地址寻找数据单元;

    其逆运算为 & 地址运算,即返回数据单元的起始地址.

指针变量的初始化

定义变量后不进行初始化,会默认存储垃圾数据;

指针变量必须存储合法取得的地址;

int a;  //1.
int *pa = &a;

1.用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致;

2.可以用一个已有合法值的指针去初始化另一个指针变量;(没找到例子)

3.不要用一个内部(局部)非静态变量去初始化 static 指针。(局部变量消亡后原本的地址就没有了意义,或者存储了其他数据)

指针变量的赋值

向指针变量赋的值必须是地址常量或变量,不能是普通整数,

例如:

1.通过地址运算“&”求得已定义的变量和对象的起始地址;

2.动态内存分配成功时返回的地址.

  • 允许定义或声明指向void类型的指针。该指针可以被赋予任何类型对象的地址,但只用来存放地址,不能进行指针运算.
void *general;

//void类型指针的使用

int main() {
//!void voidObject; 错,不能声明 void 类型的变量,编译器无法分配存储区域大小
void *pv; //对,可以声明void类型的指针
int i = 5;
pv = &i; //void类型指针指向整型变量
int *pint = static_cast<int *>(pv); //void指针转换为int指针
cout << "*pint = " << *pint << endl;
return 0;
}

P.S.空指针

int *p=0;
double *q=NULL; //这两种为旧时代的用法,有隐藏 BUG float *a=nullptr;//C++11标准后的安全空指针

指向常量的指针

指针存储的地址可以更改,但不能改变所指向的对象的值

int a;
const int *p1 = &a; //p1是指向常量的指针
int b;
p1 = &b; //正确,p1本身的值可以改变
*p1 = 1; //编译时出错,不能通过p1改变所指的对象

指针类型的常量

若声明指针常量,则指针本身的值不能被改变。

int a;
int * const p2 = &a;
p2 = &b; //错误,p2是指针常量,值不能改变

指针的算术运算

short a[4];
short* p=a; //数组名便是数组首地址a[0] *(p+2)等同于a[2]; p++后指针往后移动一个short类型长度,读取下一个short类型数据;
  • 运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置;
  • 当指针指向连续存储的同类型数据时,指针与整数的加减运和自增自减算才有意义。

    因为如果是单个变量,算术运算后移动了n个数据类型的长度,取到的是无意义数据.

指针类型的关系运算

  • 指向相同类型数据的指针之间可以进行各种关系运算;
  • 指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的;

P.S.可以和零之间进行等于或不等于的关系运算,来判断是不是空指针.

例如:p==0或p!=0

用指针访问数组元素

int a[10], *pa;
pa=&a[0]; 或 pa=a;

pa就是a[0],(pa+1)就是a[1],... ,*(pa+i)就是a[i];

a[i], *(pa+i), *(a+i), pa[i]都是等效的。
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };

1.
for (int i = 0; i < 10; i++)
cout << a[i] << " "; 2.
for (int *p = a; p < (a + 10); p++) //此处a为首地址,a+10此处运算类似指针的算术运算,是地址往后移动10个a类型的长度
cout << *p << " "; 3.
for (int i = 0; i < 10; i++)
cout << *(a+i) << " "; 4.
for (int *p = a,i=0; i<10; i++)
cout << p[i] << " ";

指针数组

int main() {
int line1[] = { 1, 0, 0 }; //矩阵的第一行
int line2[] = { 0, 1, 0 }; //矩阵的第二行
int line3[] = { 0, 0, 1 }; //矩阵的第三行 int *pLine[3] = { line1, line2, line3 }; //定义整型指针数组并初始化 //输出矩阵
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << pLine[i][j] << " "; //此处pLine[1]等价于数组名line1,所以可以套用“数组名+下标”的方式表示数组中的某一个数据,即pLine[0][1]等价于line1[1];
cout << endl;
}

指针数组与二维数组的显著区别在于:

  • 二维数组的每一个行都是等长的;
  • 而指针数组是用多个一维数组进行堆砌,形成一个类似二维数组的集合,每一行可以不等长;

以指针作为函数参数

为什么需要用指针做参数?

1.需要数据双向传递时(引用也可以达到此效果)

用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数据,实现数据的双向传递

2.需要传递一组数据,只传首地址运行效率比较高;

实参是数组名时,形参可以是指针

和引用一样,如果只想读取数据而不想让其更改数据,可以使用指向常量的指针

const int* p;

指针类型的函数

若函数的返回值是指针,该函数就是指针类型的函数

int* function();
  • 不要将非静态局部地址用作函数的返回值(非静态局部变量返回时已经消亡);
  • 返回的指针要确保在主调函数中是有效、合法的地址;

    比如:

1.主函数定义的数组;

2.在子函数中通过动态内存分配new操作取得的内存地址,但要记得在主函数中进行delete;

函数指针

函数为:
int example(int a) 指向该函数的指针为:
int (*function)(int) //名字可以随便起 p.s.与指针类型的函数区别在于:
将*和函数名包含起来的小括号()+后面小括号里的参数类型
int *function();
int* function() //返回int*,即int型指针的函数

指针保存内存地址;

函数的代码在内存中拥有地址;

所以可用指针存取函数代码首地址,并据此指向函数.

函数指针的典型用途——实现函数回调

int compute(int a, int b, int(*func)(int, int))
{ return func(a, b);} int max(int a, int b)
{ return ((a > b) ? a: b);} int min(int a, int b)
{ return ((a < b) ? a: b);} int sum(int a, int b)
{ return a + b;} res = compute(a, b, & max);//将函数代码首地址传给函数指针
res = compute(a, b, & min);
res = compute(a, b, & sum);

对象指针

Point a(5,10);
Piont *ptr;
ptr=&a; 对象指针名->成员名
例:ptr->getx() 相当于 (*ptr).getx();

this 指针

  • 指向当前对象自己;
  • 隐含于类的每一个非静态成员函数中;
  • 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。
例如:Point类的getX函数中的语句:
return x;
相当于:
return this->x; //指向调用该函数的类的实例化对象

动态内存分配

指针不可替代的作用

动态申请内存操作符 new

  • new 类型名T(初始化参数列表)
Point *ptr1 = new Point(1,2);
  • 在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以

    初值。
  • 结果值(不一定成功):成功:T类型的指针,指向新分配的内存;失败:抛出异常。

释放内存操作符 delete

释放指针p所指向的内存

分配和释放动态数组

写程序时不知道要用到的数据规模有多大时,可以动态创建数组,用完后主动释放;

new 类型名T [ 数组长度 ];

delete[] 数组名p

例子:
Point *ptr = new Point[2]; //创建对象数组
ptr[0].move(5, 10); //通过指针访问数组元素的成员,首地址名+下标
ptr[1].move(15, 20);
delete[] ptr; //删除整个对象数组

动态创建多维数组

new 类型名T[第1维长度][第2维长度]…;

例子1:
char (*fp)[3]; //去掉第一个[],留下剩下的值
fp = new char[2][3]; //fp获得第一行的首地址, fp+1 指向第二行的首地址 例子2:
int (*cp)[9][8] = new int[7][9][8]; for (int i = 0; i < 7; i++)
for (int j = 0; j < 9; j++)
for (int k = 0; k < 8; k++)
cout << cp[i][j][k] << " "; delete[] cp;

将动态数组封装成类(可用vector代替该功能)

  • 更加简洁,便于管理;
  • 可以在访问数组元素前检查下标是否越界
class ArrayOfPoints { //动态数组类
public:
ArrayOfPoints(int size) : size(size){ //构造函数
points = new Point[size]; //创建动态数组
} ~ArrayOfPoints() { //析构函数
cout << "Deleting..." << endl;
delete[] points;
} Point& element(int index) { //返回引用可以用来操作封装数组对象内部的数组元素,返回值则只是一份副本
assert(index >= 0 && index < size); //检查是否越界
return points[index];
} private:
Point *points; //指向动态数组首地址
int size; //数组大小
} int count;
cout << "Please enter the count of points: ";
cin >> count;
ArrayOfPoints points(count); //创建数组对象
points.element(0).move(5, 0); //对象.move()
points.element(1).move(15, 20);

智能指针(C++11)

- unique_ptr :不允许多个指针共享资源,指针地址不能被复制,但可以用标准库中的move函数转移到其他指针中,转移后原指针被清空.
- shared_ptr :多个指针共享资源
- weak_ptr :可复制shared_ptr,但其构造或者释放对资源不产生影响
- 仅作了解;

清华大学《C++语言程序设计基础》线上课程笔记04---指针的更多相关文章

  1. 清华大学《C++语言程序设计基础》线上课程笔记03---数据的共享和保护&数组

    数据的共享和保护 对象的生存期 static类型的局部变量,生存期在整个程序,局部可见. void example() { static a=1; int b=2 } 当调用完example函数后,b ...

  2. 清华大学《C++语言程序设计基础》线上课程笔记02---类与对象

    类与对象 public是类的对外访问接口: 类内初始值 在定义类时对数据成员写初始值,在创建对象的时候,会使用类内初始值初始化数据成员: class Clock { public: void show ...

  3. 清华大学《C++语言程序设计基础》线上课程笔记01---基础概念与一些注意事项

    使用除法的注意事项 double b = 4.0 * 1/239.0; 因为整数相除结果取整,如果参数写1/239,结果就都是0 浮点数注意事项 浮点数是近似存储,所以不能直接比较两个浮点数的大小, ...

  4. 清华大学《C++语言程序设计基础》线上课程笔记05---vector对象,对象的复制与移动,string类

    vector 对象 C++标准库中的一个类模板 封装任何类型的动态数组,自动创建和删除. 数组下标越界检查. 将动态数组封装成类的知识点中封装的ArrayOfPoints也提供了类似功能,但只适用于一 ...

  5. 清华大学《C++语言程序设计进阶》线上课程笔记06---继承、派生、多态性

    类的继承 保持已有类的特性而构造新类的过程称为继承; 实现设计与代码的重用. 在已有类的基础上新增自己的特性而产生新类的过程称为派生 当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程 ...

  6. 软件工程线上课程(C语言实践篇)学习心得总结

    林牧 + 原创作品转载请注明出处 + <软件工程(C编码实践篇)>MOOC课程http://mooc.study.163.com/course/USTC-1000002006 软件工程的理 ...

  7. C语言程序设计基础-第1周作业-初步

    1.安装带有计算机术语的翻译软件 2.在自己电脑上安装C编译器,windows系统建议安装dev-c++,其他系统自行查找. 3.加入课程小组,有任何疑问可以在小组中提问:https://group. ...

  8. 欢迎参加MVP主讲的Windows 10开发线上课程

    博客地址:http://blog.csdn.net/FoxDave Windows 10 Developer Readiness - Powered by MVPs - 由微软最有价值专家(MVP)主 ...

  9. C语言程序设计基础知识点概括

    C语言程序设计基础知识点概括 C语言程序设计基础知识点1.函数是C语言的基本构成单位.main函数是C语言程序的唯一入口.2.C语言程序开发过程. 编译过程:将以.c或.cpp结尾的源程序文件经过编译 ...

随机推荐

  1. 【Leetcode】【Medium】Validate Binary Search Tree

    Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...

  2. mysql实现‘主从复制’

    mysql主从复制(超简单) 怎么安装mysql数据库,这里不说了,只说它的主从复制,步骤如下: 首先准备多台服务器,其中一台作为主服务器,从服务器数量自定. 1.主从服务器分别作以下操作: 主服务器 ...

  3. Java Web技术经验总结

    接口的权限认证,使用拦截器(HandlerInterceptorAdapter),参考:第五章 处理器拦截器详解——跟着开涛学SpringMVC.注意:推荐能使用servlet规范中的过滤器Filte ...

  4. 获取DataTable某一列的所有值

    /// <summary> /// 获取某一列的所有值 /// </summary> /// <typeparam name="T">列数据类型 ...

  5. IOS ASI (第三方请求)

    什么是ASI全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大基于底层的CFNetwork框架,运行效率很高可惜作者早已停止更新,有一些潜在的BUG无人去解决很多公司的旧项目里面 ...

  6. 本地测试时修改localhost为自己网站的域名的方法(转载)

    做网站的,在本地测试时,所用的地址基本上都是localhost 或者直接用IP地址:127.0.0.1 如果仅仅是用来测试网站内部的程序代码之类的当然没问题,但是如果我们还要测试网站上添加的广告或者统 ...

  7. 2019.1.3 Mac安装免费版StarUml3.0.2 &&&Xmind思维导图 &&Google浏览器***版

    下载StarUml http://staruml.io/ 好像需要FQ 附赠一个 链接:https://pan.baidu.com/s/1_pa9LwopowhOTum5g89zZQ 密码:fxtc ...

  8. Spring(十八)之页面重定向

    首先说明,该示例的maven依赖可以复用Spring(十七)之表单处理还有 还有就是对应的web.xml和servlet.xml文件都能复用,不必再次修改. 说到重定向不得不提到一个转发.这里概述一下 ...

  9. Spring Boot Web Error Page处理

    spring Boot默认是whitelabel error page. 其实我们可以自己处理,由于时间有限,所以就简单说明一下方法. 首先配置 @Configuration public class ...

  10. FactoryBean的实现原理与作用

    FactoryBean与BeanFactory: 这俩货在拼写上很是相似,很多同学在看IOC源码或者其他地方并不能分清有啥区别,前面的IOC源码中我简单说过,现在统一简单来讲一下: FactoryBe ...