指针的概念
- 内存区的每一个字节有一个编号,这就是“地址”,它相 当于旅馆中的房间号
- 在地址所标识的内存单元中存放数据,这相当于旅馆房间 中居住的旅客一样
- 由于通过地址能找到所需的变量单元,我们可以说,地址 指向该变量单元
- 将地址形象化地称为“指针”
“存储单元的地址”和“存储单元的内容”
- 指向就是通过地址来体现的
- 由于通过地址能找到所需的变量单元,因此说,地址指向该变量单元
- 将地址形象化地称为“指针”。意思是通过它能找到以它为地址的内存单元
- 一个变量的地址称为该变量的“指针”
- 如果有一个变量专门用来存放另一变量的地址(即指 针),则它称为“指针变量”
- i_pointer就是一个指针变量。指针变量就是地址变量, 用来存放地址的变量,指針变量的值是地址(即指针)
- 定义指针变量的一般形式为: 类型 * 指针变量名;类型是为指针变量指定的“基类型”
合法的定义和初始化
float *pointer_3;
char *pointer_4;
int a,b;
int *pointer_1=&a,*pointer_2=&b;
赋值语句可为:
a=5; 等价于 *pointer_1=5;
术语
- 指针变量只能指向同一基类型的变量
- 用指针变量在屏幕上显示变量的地址值 指针变量指向的数据类型称为基类型
- & 取地址运算符。(&a是变量a的地址)
- * 指针运算符(“间接访问”运算符)【如果: int a=0; int *p=&a, 则在将来的运算中*p就代表a。】
- 引用指针所指向的变量的值 称为指针的解引用(Pointer Dereference)
- 空指针: 指针没有指向任何空间 | 空指针用常量NULL表示(NULL的值在C++中被定义0 )| 不能引用空指针指向的值
初始化指针变量
⚫ 指针在使用前必须初始化。
⚫ 和别的变量一样,定义指针不初始化 是一个比较普通的错误。
⚫ 没有初始化的指针可能指向任意地址, 对这些指针作操作可能会导致程序错误。
⚫ NULL是一个特殊指针值,称为空指针。它的值为0。它可被用来初始化一个指针,表示不指向任何地址。
指针和一维数组间的关系
一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址指针变量可以指向数组元素(把某一元素的地址放到一 个指针变量中)
所谓数组元素的指针就是数组元素的地址。
int a[10]={1,3,5,7,9,11,13,15,17,19};
int *p;
p=&a[0];
注意:数组名a不代表整个数组, 只代表数组首元素的地址。
“ p=a;”的作用是“把a数组的首 元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋给p ” 。
通过指针引用数组元素
引用一个数组元素,可用下面两种方法:
- 下标法,如a[i]或p[i]形式
- 指针法,如*(a+i)或*(p+i) 其中a是数组名,p是指向数组元素的指针变量,其初值p=a
指针和字符串
int main()
{
char *string=“I love China!”;
cout<<string<<endl;
return 0;
}
注意一定要在内存空间最后加一个'\0'。
指针与函数
充当形参
普通变量作函数参数—按值调用
形参(parameter)← 实参变量(variable)
指针作函数参数—按地址调用
指针形参(pointer parameter) ← &(variable)
ps:指针变量作函数参数->可以修改实参的值;
C++编译器都是将形参数组名作为指针变量来处理的
引用
引用(reference)是已存在变量的别名(alias),通过引用我们可以间接访问变量, 指针也能间接访问变量,但引用在使用上相对指针更安全。
- 引用的主要用途是为了描述函数的参数和返回值,特别是为了传递较大的数据变量。
- 对引用型变量的操作实际上就是对被引用变量的操作。
- 当定义一个引用型变量时, 必须用已存在的变量对其初始化,于是引用就被绑定在那个变量上,对于引用的改动就是对它所绑定的变量的改动,反之亦然。
数据类型 & 引用变量名 = 变量名;
1、数据类型应与被引用变量的类型相同;
2、&是引用运算符;
3、变量名为已定义的变量;
ex:
int x;
int & refx=x; refx是一个引用型变量,它被初始化为对整型变量x的引用
当定义一个引用变量后,系统并没有为它分配内存空间。
refx与被引用变量x具有相同的地址,即refx与x使用的是同一内存空间。
对引用变量值的修改就是对被引用变量的修改,反之亦然。
x=3;
cout<<refx; //结果为3
refx=5;
cout<<x; //结果为5
从物理实现上看,引用是一个隐性的指针。
引用封装了指针的特性,它不直接操作地址,不能由强制类型转换而得,因而具有较高的安全性。
也不容易产生由于使用指针而常常产生的那些不易觉察的错误。
注意!
1、定义引用时必须立即对它初始化,不能定义完成后再赋值。
int i;
int &j; "错误!!!"
j=i;
2、为引用提供的初始值可以是一个变量或另一个引用。(可以套娃)
3、引用不可重新赋值,不可使其作为另一变量的别名。
int i, k;
int &j=i;
j=&k; "错误"
C++引入引用的主要目的是将引用作为函数的参数。
实参必须是变量,而不能是一个表达式。
引用作为函数的返回值
函数返回值类型为引用型,在函数调用时,若接受返回值的是一个引用变量,相当于定义了一个对返回变量的引用。
若接受返回值的是一个非引用变量,函数返回变量的值赋给接受变量。
如果函数返回值类型为引用型,则要求返回值为左值。
这样,函数调用式可以当作左值。
int& max(int a[],int n) //求数组a[]中元素的最大值
{
int t=0;
for(int i=0;i<n;i++)
if(a[i]>a[t]) t=i;
return a[t];
}
int& sum(int a[ ],int n) //求数组a[]中元素的和
{
int s=0;
for(int i=0;i<n;i++)
s+=a[i];
return s;
}
int main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
int m2=max(a,10);
int &m3=max(a,10);
int &m4=sum(a,10);
cout<<"m2="<<m2<<endl;
cout<<"m3="<<m3<<endl;
cout<<"m4="<<m4<<endl;
m3+=10;
max(a,10)-=100;
cout<<sum(a,10)<<endl;
return 0;
}
常引用
const 数据类型& 引用变量 = 变量名;
定义一个常引用后,就不能通过常引用更改引用的变量的值。
常引用类型常用作函数的形参类型,把形参定义为常引用类型时。
这样在函数体内就不能通过形参改变实参的值,保证了函数调用时实参是“安全”的。
这样的形参称为只读形参。
注意:
形参为常引用类型时,实参可以是常量、变量表达式;
但如果为非常引用类型时,实参必须为左值。
对void fun(const int& x, int& y), 调用fun(100,200)是错误的,!
调用fun(100,a)是正确的【a为变量】。
动态内存分配
有时我们并不知道我们需要多大的数组直到程序开始运行。
因此希望能在程序中根据某一个当前运行值来决定数组的大小。
如要输入学生成绩存入数组,但是学生的数量需要在程序运行时输入,比如我们希望能这样:
int n;
cin>>n;
int scores[n];实际上不能这样。
C++中由new 和 delete两个运算符
运算符new用于进行内存分配:
- 申请动态变量:p = new type;
- 申请动态数组:p = new type[size];
- 申请动态变量并初始化:p = new type(初值);
运算符delete释放new分配的内存:
- 释放动态变量:delete p;
- 释放动态数组:delete [] p;
int main()
{
char *q;
q = new char[10];
strcpy(q, "abcde");
cout << q << endl;
delete []q;
return 0;
}
ps:new操作失败时,返回空指针。
int *p;
p = new int;
if(!p)
{
cout << "allocation failure\n";
return 1;
}
指针数组
类型名*数组名[数组长度];
- 指针数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活
- 可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串
- 由于各字符串长度一般是不相等的,所以比用二维数组节省内存单元
函数的指针
就是指向函数的指针,指向函数代码的起始地址。
"定义方法"
int isdigit(int n, int k);
{
...
}
int (*p)(int, int );
p=isdigit;
"使用方法"
a=isdigit(n,k);
a=p(n,k)
常见错误
- 指针p未赋值就使用*p
- 指针p的值为NULL却使用*p
- 指针p指向动态内存,但该内存已 被delete了,再使用*p
- 指针指向动态内存,但该内存被 delete两次
- 指针指向动态数组,但数组在操作时 曾经下标越界,然后delete时会崩溃
- 指针指向普通数组,delete时会崩溃