C++学习目录链接:
C++学习笔记目录链接(持续更新中)


文章目录

  • 一、变量和指针
    • 1.指针的声明
    • 2.指针的赋值
    • 3.关于指针使用的说明
    • 4.指针运算符和取地址运算符
    • 5.指针运算
  • 二、指针和数组
    • 1.数组的存储
    • 2.指针与一维数组
    • 3.指针与二维数组
    • 4.指针与字符数组
  • 三、指向函数的指针
  • 四、引用
    • 1.使用引用传递参数
    • 2.使用指针传递参数
    • 3.数组作为函数参数
  • 五、指针数组

一、变量和指针

系统的内存就像是带有编号的小房间,如果想使用内存就需要得到房间号。如图所示,定义一个整型变量i,它需要4个字节,所以编译器为变量i分配了编号从4001到4004的房间,每个房间代表一个字节。

各个变量连续地存储在系统的内存中,如图所示,两个整型变量i和i存储在内存中。

在程序代码中通过变量名来对内存单元进行存取操作,但是代码经过编译后已经将变量名转换为该变量在内存的存放地址,对变量值的存取都是通过地址进行的。例如语句“i+j;” 的执行过程是根据变量名与地址的对应关系,找到变量i的地址4001,然后从4001开始读取4个字节数据放到CPU的寄存器中,再找到变量j的地址4005,从4005开始读取4个字节的数据放到CPU的另一个寄存器中,通过CPU的加法中断计算出结果。
    在低级语言的汇编语言中都是直接通过地址来访问内存单元,而在高级语言中才使用变量名访问内存单元,C语言作为高级语言却提供了通过地址来访问内存单元的方法,C++语法也继承了这一特性。
    由于通过地址能访问指定的内存存储单元,可以说地址“指向”该内存单元,例如房间号4001指向系统内存中的一个字节。地址可以形象地称为指针,意思是通过指针能找到内存单元。一个变量的地址称为该变量的指针。如果有-一个变量专门用来存放另一个变量的地址,它就是指针变量。在C++语言中有专门用来存放内存单元地址的变量类型,就是指针类型。
    指针是一种数据类型,通常所说的指针就是指针变量,它是一个专门用来存放地址的变量,而变量的指针主要指变量在内存中的地址。变量的地址在编写代码时无法获取,只有在程序运行时才可以得到。

1.指针的声明

声明指针的一般形式如下

数据类型标识符*指针变量名

int *P_iPoint;//声明一个整数型指针
float *a,*b;//声明两个浮点型指针

2.指针的赋值

指针可以在初始化时赋值,也可以在后期赋值。
在初始化时赋值

int i=100;//声明一个变量
int *a=&i;//声明一个指针变量,并赋值

后期赋值

int i=100;
int *a;
a=&i;

3.关于指针使用的说明

(1)指针变量名是p,而不是*p。
p=&i的意思是取变量i的地址赋给指针变量p。.
下面的实例可以获取变量的地址,并将获取的地址值输出,。

/* 输出变量的地址*/
#include <iostream>
using namespace std;
void main()
{int i=100;
int *a;
a=&i;
printf("%d\n",a);
}

    实例可以通过printf函数直接将地址值输出。由于变量是由系统分配空间,所以变量的地址不是固定不变的。

(2)指针变量不可以直接赋值。例如:

int i=100;
int *a;
a=i;

编译不能通过,有“error C2440: '= : cannot convert from ‘const int’ to 'int '”错误提示。
如果强行赋值,使用指针运算符
提取指针所指变量时会出错。例如:

int i=100;
int *a;
a=(int*)i;//强行将100转换成指针变量赋值
printf("%d\n",a); //正确运行,显示100
printf("%d\n",*a);//出错,没有显示数据

算例:通过指针来比较两个数的大小

/* 比较两个数的大小*/
#include <iostream>
using namespace std;
void main()
{int *p1,*p2;//定义两个指针变量
int *p;//定义一个临时指针变量
int a,b;
cout<<"请输入一个数:"<<endl;
cin>>a;
cout<<"请输入另一个数:"<<endl;
cin>>b;
p1=&a;p2=&b;//指针赋值
if (a<b)
{p=p1;
p1=p2;
p2=p;
}
cout<<"较大值:"<<*p1<<endl;
cout<<"较小值:"<<*p2<<endl;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}

4.指针运算符和取地址运算符

*和&是两个运算符,*是指针运算符,&是取值运算符。
如图所示,变量i的值为100, 存储在内存地址为4009的地方,取地址运算符&使指针变量p得到地址4009。
如图所示,指针变量存储的是地址编号4009,指针通过指针运算符可以得到地址4009所对应的数值。.

#include <iostream>
using namespace std;
void main()
{int a=100;
int *p;
p=&a;//指针变量取值
cout<<"p ="<<p<<endl;
cout<<"*p ="<<*p<<endl;
}

指针运算符和取地址运算符的说明
声明并初始化指针变量时同时用到了*和&这两个运算符。例如:

int a=100;
int *p=&a;//声明一个指针变量并初始化

&p 和&a的区别
&和的运算符优先级别相同,按自右而左的方向结合,因此&p 是先进行运算,p 相当于变量a;再进行&运算,&p 就相当于取变量a的地址。&a 是先计算&运算符,&a就是取变量a的地址;然后进行运算,&a 就相当于取变量a所在地址的值,实际就是变量a。

5.指针运算

指针变量存储的是地址值,对指针作运算就等于对地址作运算。下面通过实例来使读者了解指针的运算。

#include <iostream>
using namespace std;
void main()
{int a=100;
int *p=&a;//声明一个指针变量并初始化
cout<<"p的地址:"<<p<<endl;
p--;
cout<<"p--的地址:"<<p<<endl;
p++;
cout<<"p++的地址:"<<p++<<endl;}

指针进行一次加 1运算,其地址值并没有加1,而是增加了4,这和声明指针的类型有关。p++是对指针作自加运算,相当于语句“p=p+1",地址是按字节存放数据,但指针加1并不代表地址值加1个字节,而是加上指针数据类型所占的字节宽度。要获取字节宽度需要使用sizeof 关键字,
例如整型的字节宽度是sizeof(int), sizeof(int)的值为 4;双精度整型的字节宽度是sizeof(double),其值为8。将实例中的int指针类型改为double,看看运行结果,代码如下:


二、指针和数组

1.数组的存储

数组,作为同名、同类型元素的有序集合,被顺序存放在一块连续的内存中,而且每个元素存储空间的大小相同。数组中第一个元 素的存储地址就是整个数组的存储首地址,该地址放在数组名中。对于一维数组而言,其结构是线性的,所以数组元素按下标值由小到
大的顺序依次存放在一块连续的内存中。 在内存中存储一维数组如图所示。对于二维数组而言,用矩阵方式存储元素,在内存中仍然是线性结构。

2.指针与一维数组

系统需要提供一定 量连续的内存来存储数组中的各元素,内存都有地址,指针变量就是存放地址的变量,如果把数组的地址赋给指针变量,就可以通过指针变量来引用数组。引用数组元素有两种方法:下 标法和指针法。通过指针引用数组,就要先声明一个数组,再声明一个指针。例如:

int a[10];
int *p;

然后通过&运算符获取数组中元素的地址,再将地址值赋给指针变量。例如:
把a[0]元素的地址赋给指针变量p,即p指向a数组的第0号元素,

p=&a[0];

通过指针变量获取数组中的元素

#include <iostream>
using namespace std;
void main()
{int i,a[10];
int *p;
//利用循环,分别为10个元素赋值
for(i=0;i<10;i++)a[i]=i;
//将数组中的10个元素输出到设备
p=&a[0];
for(i=0;i<10;i++,p++)
cout<<*p<<endl;}

如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。
p+i和a+i是a[i]的地址。a代表首元素的地址,a+i 也是地址,对应数组元素a[i]。
*(p+i) *(a+i)是p+i或a+i所指向的数组元素,即a[i]。

程序中使用指针获取数组首元素的地址,也可以将数组名赋值给指针,然后通过指针访问数组。实现代码如下

#include <iostream>
using namespace std;
void main()
{int i,a[10];
int *p;
//利用循环,分别为10个元素赋值
for(i=0;i<10;i++)a[i]=i;
//将数组中的10个元素输出到设备
p=a;
for(i=0;i<10;i++,p++)
cout<<*p<<endl;}

3.指针与二维数组

可以将一维数组的地址赋给指针变量,同样也可以将二维数组的地址赋给指针变量,因为一维数组的内存地址是连续的,二维数组的内存地址也是连续的,所以可以将二维数组看作是- -维数组。二维数组中各元素的地址如图7.24所示。

使用指针遍历二维数组

#include <iostream>
using namespace std;
void main()
{int i,a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};int *p;p=a[0];for(i=0;i<sizeof(a)/sizeof(int);i++,p++){cout<<"地址:";cout<<a[i];cout<<"是";cout<<*p<<endl;}
}

程序中通过*p对二维数组中的所有元素都进行了引用,如果想对二维数组中某一行中的某一列元素进行引用,就需要将二维数组不同行的首元素地址赋给指针变量。
(1) a+n表示第n行的首地址。
(2) &a[0][0]既可以看作数组0行0列的首地址,也可以看作二维数组的首地址。&a[m][n]就是第m行n列元素的地址。
(3) &a[0]是第0行的首地址,当然&a[n]就是第 n行的首地址。
(4) a[0]+n 表示第0行第n个元素的地址。
(5) *(*(a+n)+m)表示第 n行第m列元素。
(6) *(a[n]+m)表示第n行第m列元素。

#include <iostream>
using namespace std;
void main()
{int i,j,a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};for(i=0;i<3;i++)for(j=0;j<4;j++)cout<<*(*(a+i)+j)<<endl;
}

4.指针与字符数组

字符数组是一一个一维数组, 使用指针同样也可以引用字符数组。引用字符数组的指针为字符指针,字符指针就是指向字符型内存空间的指针变量,其一般的定 义语句如下:

char *p;

字将数组就是一个字符串, 通过字符指针可以指向一个字符串。 例如,语句

char *p="www.baidu.com";

等价于

char *p;
p="www.baidu.com";

示例:连接两个字符串

#include <iostream>
using namespace std;
void main()
{char str1[10],str2[5],*p1,*p2;p1=str1;p2=str2;cout<<"请输入第一个字符串:"<<endl;gets(str1);cout<<"请输入第二个字符串:"<<endl;gets(str2);while(*p1!='\0')p1++;while(*p2!='\0')*p1++=*p2++;*p1='\0';//添加结束符号cout<<"打印拼接后的字符串";puts(str1);
}

三、指向函数的指针

指针变量也可以指向一个函数。一个函数在编译时被分配给一个入口地址,这个函数入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。一个函数可以返回一个整型值、 字符值、实型值等,也可以返回指针型的数据,即地址。其概念与之前类似,只是返回的值的类型是指针类型而已。返回指针类型的值的函数简称为指针函数。
定义指针函数的一般形式为:

类型名*函数名(参数表列);

例如,定义一个具有两个参数和-一个返回值的函数的指针,其代码如下:

int *a(int x,int y);

示例:利用指针进行平均值计算

#include <iostream>
#include<iomanip>//omanip的作用:主要是对cin,cout之类的一些操纵运算子,比如setfill,setw,setbase,setprecision等等。它是I/O流控制头文件,就像C里面的格式化输出一样.
using namespace std;
int avg(int a,int b);//函数的声明
void main()
{int iWidth,iLenght,iResult;iWidth=10;iLenght=30;int(*Pfun)(int,int);//定义函数指针Pfun=avg;iResult=(*Pfun)(iWidth,iLenght);cout<<iResult<<endl;
}int avg(int a,int b)
{return (a+b)/2;
}

四、引用

引用实际上是一种隐式指针, 它为对象建立一个别名,通过操作符&来实现。&是取地址操作符,通过它可以获得地址
引用的形式如下:

数据类型&表达式;

例如

int a;
int &ia=a;//引用
ia=2;

定义了一个引用变量ia,它是变量a的别名,对ia的操作与对a的操作完全一样。 ia=2 把2赋给a,&ia返回a的地址。执行ia=2和执行a=2等价。

使用引用的说明:
(1)一个C++引用被初始化后,无法使用它再去引用另一个对象,它不能被重新约束。
(2)引用变量只是其他对象的别名,对它的操作与原来对象的操作具有相同作用。
(3)指针变量与引用有两点主要区别:一是指针是一种数据类型, 而引用不是一个数据类型,指针可以转换为它所指向变量的数据类型,以便赋值运算符两边的类型相匹配;而在使用引用时,系统要求引用和变量的数据类型必须相同,不能进行数据类型转换。二是指针变量和引用变量都用来指向其他变量,但指针变量使用的语法要复杂- 些;而在定义了引用变量后,其使用方法与普通变量相同。
例如:

int a;
int *ia=&a;
int &ia=a;

引用应该初始化,否则会报错
如下面算例编译器会报出“references must be initialized" 这样的错误,造成编译不能通过。

int a;
int b;
int &ia;

下面通过一个算例感受下引用

#include <iostream>
using namespace std;
void main()
{int a;int &ref_a=a;//引用a=100;cout<<"a="<<a<<endl;cout<<"ref_a="<<ref_a<<endl;a=2;cout<<"a="<<a<<endl;cout<<"ref_a="<<ref_a<<endl;int b=20;ref_a=b;cout<<"a="<<a<<endl;cout<<"b="<<b<<endl;cout<<"ref_a="<<ref_a<<endl;
}

1.使用引用传递参数

在C++语言中,函数参数的传递方式主要有两种,分别为值传递和引用传递。所谓值传递,是指在函数调用时,将实际参数的值赋值一份传递到调用函数中,这样如果在调用函数中修改了参数的值,其改变不会影响到实际参数的值。而引用传递则恰恰相反,如果函数按引用方式传递,在调用函数中修改了参数的值,其改变会影响到实际参数。

#include <iostream>
using namespace std;
swap(int &a,int &b)
{   int tmp;tmp=a;a=b;b=tmp;
}void main()
{int x,y;cout<<"请输入两个整数x,y:"<<endl;cin>>x;cin>>y;if(x<y){swap(x,y);}cout<<"x="<<x<<endl;cout<<"y="<<y<<endl;
}

2.使用指针传递参数

指针变量可以作为函数参数。使用指针变量传递参数和使用引用传递参数方式的执行效果相同,可以对同一个函数使用两种方式传递参数进行对比,观察程序的执行结果。为了使读者更好地了解指针作为参数进行传递的操作,下面通过指针传递参数实现变量值的交换功能。

#include <iostream>
using namespace std;
swap(int *a,int *b)
{   int tmp;tmp=*a;*a=*b;*b=tmp;
}void main()
{int x,y;int *ix,*iy;cout<<"请输入两个整数x,y:"<<endl;cin>>x;cin>>y;ix=&x;iy=&y;if(x<y){swap(ix,iy);}cout<<"x="<<x<<endl;cout<<"y="<<y<<endl;
}

3.数组作为函数参数

一、数组名作为函数的参数,传递的是一个地址(或常量指针)

#include<stdio.h>
void arr(int a[])//数组名作为函数的参数,传递的是一个地址(或常量指针)
{int i;for(i=0;i<5;i++){printf("%d\n",a[i]);}
}
int main()
{int a[] = {1,2,3,4,5};arr(a);return 0;}}

例子:输出平均值

#include <iostream>
using namespace std;int average(int array[10]) {int i, sum = 0;for(i = 0; i < 10; ++i) {sum += array[i];}return sum / 10;
}int main() {int i, score[10];cout << "请输入10个成绩:";for (i = 0; i < 10; i++) {cin >> score[i];}cout << "平均成绩是:" << average(score) << endl;return 0;
}

五、指针数组

数组中的元素均为指针变量的数组称为指针数组,- -维指针数组的定义形式为:

类型名*数组名[数组长度];
例如:int *P[4];

指针数组中的数组名也是一一个指针变量,该指针变量为指向指针的指针。
例如:

int *p[4];
int a=1;
*p[0]=&a;

p是一个指针数组,它的每一个元素是一个指针型数据(其值为地址),指针数组p的第-一个值是变量a的地址。指针数组中的元素可以使用指向指针的指针来引用。例如: .int *(*p);*运算符表示p是一个指针变量,*(*p)表 示指向指针的指针,*运算符的结合性是从右到左,因此“int *(*p);”可写成“int **p;"。指向指针的指针获取指针数组中的元素和利用指针获取- -维数组中 的元素方法相同,如图所示。

第一次进行指针*运算获取到的是一个地址值,再进行一次指针*运算,就可以获取到具体值。


作者:电气-余登武

C++学习笔记7[指针]相关推荐

  1. 梓益C语言学习笔记之指针

    梓益C语言学习笔记之指针 一.32位平台下,地址是32位,所以指针变量占32位,共4个字节 二.内存单元的地址即为指针,存放指针的变量称为指针变量,故:"指针"是指地址,是常量,& ...

  2. C/C++学习笔记之指针体系

    铁律1:指针是一种数据类型 1)指针也是一种变量,占有内存空间,用来保存内存地址 测试指针变量占有内存空间大小 2)*p操作内存 在指针声明时,*号表示所声明的变量为指针 在指针使用时,*号表示 操作 ...

  3. C++学习笔记——智能指针

    c++里不像java等语言设置了垃圾回收的功能,但是c++通过它的王牌指针实现了智能指针这一解决办法. 目录 异常 1.异常 2.异常的使用 3.异常的规则 4.异常安全 智能指针 概念 原理 aut ...

  4. c++学习笔记之指针

    1.声明指针 如果声明多个指针,每个都必须加*,如int *aPtr, *bPtr 变量名最好以Ptr结尾,表明是指针变量 把指针初始化NULL和0是等价的 2.地址运算符和间接运算符 地址运算符&a ...

  5. Boost学习笔记-智能指针

    1.  智能指针 scoped_ptr 只在作用域内生效,离开作用域既释放资源,不能复制和赋值.类似于标准库的auto_ptr,但它相对于auto_ptr的优势在于,他的要求更严格,使用起来更安全.a ...

  6. Rust 学习笔记——智能指针

    https://doc.rust-lang.org/book/ch15-00-smart-pointers.html 智能指针是一种想指针一样的行为且具有其他能力的数据结构(Smart pointer ...

  7. C语言学习笔记--数组指针和指针数组

    C 语言中的数组有自己特定的类型,数组的类型由元素类型和数组大小共同决定.(如 int array[5]类型为 int[5]) 1.定义数组类型 C 语言中通过 typedef 为数组类型重命名:ty ...

  8. go学习笔记-语言指针

    语言指针 定义及使用 变量是一种使用方便的占位符,用于引用计算机内存地址.取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址. 一个指针变量指向了一个值的内存地址.类似于变量和常量, ...

  9. C 学习笔记 —— 高级指针话题

    文章目录 高级声明 函数指针 函数指针的声明 赋值 调用 函数指针作为参数传递 实例(转移表+函数传参) 回调函数 命令行参数 字符串常量 高级声明 int f; //整型变量 int *f; //指 ...

最新文章

  1. Weex 版扫雷游戏开发
  2. 《MonkeyRunner原理剖析》第九章-MonkeyImage实现原理 - 概览
  3. Project Euler 1-25
  4. makefile 学习(一)
  5. 可重入锁(递归锁) 互斥锁属性设置
  6. Java中通过代理对类进行修改
  7. c++ boost多线程学习(一)
  8. java重_重拾JAVA,重识JAVA(一)
  9. foreach循环符合就不往下走了_游泳池循环方式及循环周期
  10. TokenInsight:反映区块链行业整体表现的TI指数较昨日同期下降3.29%
  11. 【PTA】520 钻石争霸赛 2021,119分
  12. p40_数据交换方式
  13. HDU 4540 威威猫系列故事——打地鼠(DP)
  14. html 滑动返回顶部,返回页面顶部的几种方式总结
  15. EL表达式和JSTL标签库
  16. PDF文件怎么转换成CAD格式?教你几种转换方法
  17. C语言实现24点游戏算法
  18. 缩减Centos7xfs磁盘空间
  19. 微信中怎么打开apk下载链接 微信跳转打开外部浏览器打开apk文件
  20. SIP与P2P的技术携手创造奇迹?

热门文章

  1. cdh jar包 sqoop2_安装sqoop1.4.6-cdh5.5.2
  2. oracle 按月累计求和,SQL Cumulative Sum累积求和
  3. SpringMVC获取请求参数-基本类型
  4. mybatis方法传入多参数
  5. SpringAOP中通过JoinPoint获取值,并且实现redis注解
  6. MyBatis Plus——忽略某个实体类属性和数据库表字段之间的映射关系
  7. Soldier and Traveling
  8. CG CTF RE Hello,RE!
  9. Fibonacci in the Pocket
  10. Little Sub and Triples