指针和引用
指针和指针变量
如果在程序中定义了一个变量,在编译时,系统会给这个变量分配内存单元,并根据程序中定义的变量类型,分配一定长度的内存空间,每个内存单元中存放着变量的值.
为了便于内存单元的存储,系统为每一个内存单元分配了一个地址.
在变量的生存期中,不管以后对变量如何赋值,其内存地址总是固定不变的
- 指针:反映内存单元的位置(地址),称为单元的指针.
- 指针变量:存放内存地址的变量
#include <iostream>
using namespace std;
int main()
{
int i = 5; // i是一个初值为5的整型变量
int *p = &i; // p是整型指针变量,&i是取变量i在内存中的地址
// p的值等于变量i在内存中的地址值
cout << p << endl;
return 0;
}
- 当指针变量的值是变量存储在内存中的地址时,称指针变量”指向”这个变量.
- 定义格式:
`<数据类型> *<指针变量名 1>[,<指针变量名 2>,…]; - 其中”*”是一个定义指针变量的说明符,它不是指定变量名的一部分.每个指针变量前面都需要这样的”*”来表明
- 定义一个指针后,系统也会给指针分配一个内存单元,但分配的空间大小都是相同的,因为指针变量的数值是某个变量的地址,而地址值的长度是一样的.
&和*运算符
- &(取地址运算符),*(取值运算符)
- &:只对变量进行操作,作用是取该变量的地址.*:作用是取指针或地址所指内存单元中存储的内容
- 使用指针变量前,一定要进行初始化或由确定的地址数值
- 给两个指针变量进行赋值,必须使这两个指针变量类型相同
指针运算
1.指针的算术运算
指针的运算主要指的是对指针加上或减去一个整数.
- 指针的这种运算的意义和通常情况下数值的加减运算的意义是不大一样的.
int *ptr;
若有ptr = ptr + n;
编译器会把指针ptr的值加上sizeof(int)*n
.ptr指向的存储单元向高地址方向移动了sizeof(int) *n字节
因此:
<指针变量> = <指针变量> + n;
//它是使指针变量指向的存储单元向高地址方向移动了sizeof(指针变量类型)*n
字节
<指针变量> = <指针变量> - n;
// 它是使指针变量指向的存储单元向低地址方向移动了sizeof(指针变量类型)*n
字节
2.指针的关系运算
两个指针的关系运算要根据两个指针变量值的大小来进行比较
- 实际运用:比较两个指针反映地址的前后关系或判断指针变量的值是否为0
#include <iostream>
using namespace std;
int main()
{
char a[] = "Chinese";
char *p1 = a, *p2 = a, temp;
while (*p2 != '\0')
p2++;
p2--; //p2指向a的最后一个元素
while (p1 < p2)
{
temp = *p1;
*p1 = *p2;
*p2 = temp;
p1++;
p2--;
}
cout << a << endl;
system("pause");
return 0;
}
指针和数组
- 数组中所有元素都是依次存储在内存单元中的,每个元素都有相应的内存地址
- C++规定数组名代表数组中下标为0的元素的地址,即数组的首地址
int a[5]; // a表示元素为a[0]的地址,a是一个地址(指针)常量
- 下标运算符
[]
具有以下含义:
a[i]=*(a+i)
因为a
是一个地址(指针),a+i
表示a[i]
的地址值,等价于&a[i]
所以a[i]=*(a+i)
- 指向数组的指针变量实际上也可以像数组变量那样使用下标,而数组变量又可像指针变量那样使用指针
若定义下列指针:
int *pi;
pi = a; // 等价于pi=&a[0];
*(pi+i) = 1; // 等价于a[i] = 1;
#include <iostream>
using namespace std;
int main()
{
int a[] = { 5,8,7,6,2,7,3 };
int y, *p = &a[1];
y = (*--p)++; // 取值运算符和前缀自减运算符具有相同的优先级,结合方向为从右至左
// 先算--p,也就是a[0]的地址,然后取出来,也就是说,(*--p)为5,赋值给y后
// 再自增,所以y为5
cout << y << endl;
system("pause");
return 0;
}
- 二维数组的指针
int a[2][3] = {{1,2,5},{7,9,11}};
其中a是数组名,a数组包含两个元素a[0]和a[1],而每个元素又是一个一维数组,例如a[0]有a[0][0],a[0][1],a[0][2]三个元素,它可以用一个指针来表示
int *p1,*p2;
p1=a[0];
p1=a[1];
数组名a代表整个二维数组的首地址,即指向一维数组的指针的一维数组,即a可以用指针的指针来表示
int **p;
p=a; // 其中p[0]或*p等价于p1或a[0] p[1]或*(p+1)等价于p2或a[1]
#include <iostream>
using namespace std;
int main()
{
int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
int y = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
y += (*(a + i))[j]; // *(a+i)代表a[i]
}
}
cout << y << endl;
system("pause");
return 0;
}
指针和函数
指针即可以作为函数的形参,又可以作为返回值
1.指针作为函数的参数
- 如果函数的某个参数是指针,对这一个函数的调用就是按地址传递的函数调用,简称传址调用.
- 由于函数形参和实参指针指向同一个地址,因此形参的改变必将影响实参.在实际运用中,函数可以通过指针类型的参数带回一个或多个值
#include <iostream>
using namespace std;
void swap(int *x, int *y);
int main()
{
int a = 7, b = 11;
swap(&a, &b);
cout << "a = " << a << ", b = " << b << endl;
system("pause");
return 0;
}
void swap(int *x, int *y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
cout << "x = " << *x << ",y = " << *y << endl;
}
###### 传递指针的函数调用实现过程如下:
1. 函数声明中指明指针参数,即示例中的void swap(int *x,int *y);
2. 函数调用中的实参中指明变量的地址,即示例中的swap(&a,&b);
3. 函数定义中对形参进行间接访问.对*x和*y的操作,实际上就是访问函数的实参变量a和b,通过局部变量temp的过度,使变量a和b的值被修改
2.返回指针的函数
函数可以返回指针,该指针指向一个已定义的任一类型的数据.
- 格式
<函数类型> *<函数名>(<形式参数表>){<函数体>}
:在函数名前加上一个”*”,表示函数返回的是一个指针,该指针所指向的数据类型由函数类型决定
#include <iostream>
using namespace std;
char *flip(char *str)
{
char *p1, *p2, ch;
p1 = p2 = str;
while (*p2 != '\0')
p2++;
p2--;
while (p1 < p2)
{
ch = *p2;
*p2 = *p1;
*p1 = ch;//交换字符
p1++;
p2--;
}
return str;
}
int main()
{
char str[] = "ABCDEFG";
cout << flip(str) << endl;
system("pause");
return 0;
}
3.指向函数的指针
与变量相似,每一个函数都有地址.
- 指向函数地址的指针称为函数指针.函数指针指向内存空间中的某个函数,通过函数指针可以调用相应的函数
- 定义:
<函数类型>(* <指针名>)(<参数表>);
如:int (*func)(char a,char b);
就是定义一个函数指针,int
为函数的返回类型,*
表示后面的func
是一个指针变量名,该函数具有两个字符型参数a和b - 由于函数名表示该函数的入口地址,因此可以将函数名赋给指向函数的指针变量.但赋给函数指针变量的函数的返回值类型与参数个数,顺序要和函数指针变量相同.
- 调用函数格式:
(*<指针名>)(<实数表>);
或<指针名>(<实数表>);
// 函数指针的使用
#include <iostream>
using namespace std;
double add(double x, double y)
{
return (x + y);
}
double mul(double x, double y)
{
return (x*y);
}
int main()
{
double(*func)(double, double);//定义一个函数指针变量
double a, b;
char op;
cout << "输入两个实数及操作方式,'+'表示加,'*'表示乘" << endl;
cin >> a >> b >> op;
if (op == '+')
func = add;//将函数名赋给指针
else
func = mul;
cout << a << op << b << "=" << func(a, b) << endl;//函数调用
system("pause");
return 0;
}
//函数指针变量用作函数的参数
#include <iostream>
using namespace std;
double add(double x, double y)
{
return (x + y);
}
double mul(double x, double y)
{
return (x*y);
}
void op(double(*func)(double, double), double x, double y)
{
cout << "x = " << x << ",y = " << y << ", result = " << func(x, y) << endl;
}
int main()
{
cout << "使用加法:";
op(add, 3, 7);
cout << "使用乘法:";
op(mul, 3, 7);
system("pause");
return 0;
}
// 函数指针数组的使用
#include <iostream>
using namespace std;
void add(double x, double y)
{
cout << x << "+" << y << "=" << x + y << endl;
}
void sub(double x, double y)
{
cout << x << "-" << y << "=" << x - y << endl;
}
void mul(double x, double y)
{
cout << x << "*" << y << "=" << x*y << endl;
}
void div(double x, double y)
{
cout << x << "/" << y << "=" << x / y << endl;
}
void(*func[4])(double, double) = { add,sub,mul,div };//函数指针数组定义和初始化
int main()
{
double x = 3, y = 7;
char op;
do {
cout << "+ ------- 相加\n"
<< "- ------- 相减\n"
<< "* ------- 相乘\n"
<< "/ ------- 相除\n"
<< "0 ------- 退出\n";
cin >> op;
switch(op)
{
case '+':func[0](x, y); break;
case '-':func[1](x, y); break;
case '*':func[2](x, y); break;
case '/':func[3](x, y); break;
case '0':return 0;
}
} while (1);
system("pause");
return 0;
}
new和delete
C++中,使用运算符
new
和delete
能有效,直接地进行动态内存的分配和释放
new
返回指定类型的一个指针,如果分配失败,则返回0;- ‘delete’释放
new
请求到的内存
// 系统自动根据double类型的空间大小开辟一个内存单元,并将地址放在指针p中
double *p;
p = new double;
*p=30.4;//将值存放在开辟的单元中
// 也可在开辟内存单元的时候,对单元里的值进行初始化
double *p;
p = new double(30.4);
// delete的作用是将p指针的内存单元释放,指针变量p仍然有效,它可以重新指向另一个内存单元
delete p;
注意:
1. new
和delete
必须配对使用.也就是说,用new
为指针分配内存后,当使用结束后,必须用delete
来释放已分配的内存
2. 运算符delete
必须用于 之前用new
分配的有效指针.
3. new
可以为数组分配内存,但当释放时,也可告诉delete
数组有多少个元素,如:
int *p;
p = new int[10];//分配整型数组的内存,数组中有10个元素
if(!p)
{
cout << "内存分配失败";
exit(1); //中断程序执行
}
for(int i=0;i<10;i++)
{
p[i]=i;
}
//...
delete [10]p;//告诉delete数组有多少个元素,或delete []p;
引用和引用传递
- 引用:C++提供的一个与指针密切相关的特殊数据类型,是一个变量的别名.系统不会为引用类型变量分配内存空间,只是使引用变量与其相关联的变量使用同一个内存空间
1.引用定义和使用
- 格式:
<数据类型> &<引用名> = <变量名>
或
<数据类型> &<引用名>(<变量名>)
其中变量名必须是一个已定义过的变量
int a = 3;
int &ra = a; //ra是一个引用,它是变量a的别名,所有对引用ra的操作,实际上是对被引用对象a的操作
//给引用重新赋值
#include <iostream>
using namespace std;
int main()
{
int a;
int &ra = a;
a = 5;
cout << "a = " << a << endl;
cout << "ra = " << ra << endl;
cout << "a的地址是: " << &a << endl;
cout << "ra的地址是: " << &ra << endl;
int b = 8;
ra = b;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "ra = " << ra << endl;
cout << "a的地址是: " << &a << endl;
cout << "b的地址是: " << &b << endl;
cout << "ra的地址是: " << &ra << endl;
system("pause");
return 0;
}
ra被重新赋值为变量b,但是ra与a的地址仍然相同,只不过他们的值都等于b的值
注意:
1. 指针是一个变量,可以把它再赋值成指向别处的地址,而引用一旦被初始化,其地址不会改变
2. 定义引用类型变量的时候,必须将其初始化.而且引用变量类型必须与为它初始化的变量类型相同
3. 若所引用类型变量的初始值是常数,则必须将该引用定义成const
类型
4. 可以引用一个结构体,但不能引用一个数组.这是因为数组是某个数据类型元素的集合,数组名表示该元素集合空间的起始地址,它自己不是一个真正的数据类型.
5. 引用本身不是一种数据类型,所以没有引用的引用,也没有指针的引用
2.引用传递
- 方式:在函数定义时将形参前加上引用运算符”
&
” - 引用作为函数参数进行传递,实际上传递的是实参的地址.也即是说,形参的任何操作都会改变相应的实参的数值
#include <iostream>
using namespace std;
void swap(int &x, int &y);
int main()
{
int a(7), b(11);
cout << "原来a = " << a << endl;
cout << "原来b = " << b << endl;
swap(a, b); // 实际上传递的是实参a,b的地址
cout << "交换后a = " << a << endl;
cout << "交换后b = " << b << endl;
system("pause");
return 0;
}
void swap(int &x, int &y)
{
int temp;
temp = x;
x = y;
y = temp;
cout << "x = " << x << endl;
cout << "y = " << y << endl;
}
/*
原来a = 7
原来b = 11
x = 11
y = 7
交换后a = 11
交换后b = 7
*/
- 引用还可以作为函数的返回值
#include <iostream>
using namespace std;
double area;
double &CalArea(double r)
{
area = 3.141593*r*r;
return area;
}
int main()
{
double c = CalArea(5.0);
double &d = CalArea(10.0);
cout << c << endl;
cout << d << endl;
system("pause");
return 0;
}
/*
78.5398
314.159
*/
* 绝对不要返回不在作用域内的变量的引用,因为一旦变量退出作用域,对它的引用也没有意义了.*