指针
指针的概念
指针就是把地址作为数据处理,用于
- 提供间接访问
- 允许函数之间共享内存空间
- 实现动态变量
指针变量:存储地址的变量。
变量的指针:当变量A存储变量B的地址时,那么A就是B的指针。
定义指针变量要告诉编译器该变量中存放的是一个地址,同时也需要知道指针指向的单元的数据类型。故指针的定义为:
类型标识符 *指针变量;
如何让指针指向某一变量?
用地址运算符 &
解决。如表达式 &x
返回的是变量 x 的地址,则有intp = &x;
&
运算符后面不能跟常量或表达式。如 &2
是没有意义的,&(m * n + p )
也是没有意义的。
如何通过指针变量处理和改变它所指向的单元的值?
用引用运算符 *
解决。如 *intp
表示的是 intp 指向的这个单元的内容。如:*intp = 5
等价于 x = 5
在对 intp 使用引用运算之前,必须先对 intp 赋值。
指针在使用前必须初始化,没有初始化的指针可能指向任意地址,对这些指针作操作可能会导致程序错误。
NULL是一个特殊指针值,称为空指针。它的值为0。它可被用来初始化一个指针,表示不指向任何地址。
指针运算和数组
数组名可以看成是常量指针,对一维数组来说,数组名是数组的起始地址,也就是第0个元素的地址,如执行了p=array,则p与array是等价的,对该指针可以进行任何有关数组下标的操作。
指针保存的是一个地址,地址是一个整型数,因此可以进行各种算术运算,但仅有加减运算是有意义的。
int a[10], *p;
p=a;
// solution1
for (int i=0; i<10; i++)
cout << p[i];
// solution2
for (int i=0; i<10; ++i)
cout << *(p+i);
// solution3
for (;p<a+10; ++p);
cout << *p ;
// 下面这种不行,因为a是常量,不能++
for (int i=0; i<10; ++i)
{
cout << *a ;
++a;
}
动态内存分配
动态内存分配与回收
type *p;
运算符new
用于进行内存分配:
- 申请动态变量:
p = new type;
- 申请动态数组:
p = new type[size];
- 申请动态变量并初始化:
p = new type(初值);
运算符delete
释放分配的内存:
- 释放动态变量:
delete p;
- 释放动态数组:
delete [] p;
动态分配的检查
new操作的结果是申请到的空间的地址;当系统空间用完时,new操作可能失败;new操作失败时,返回空指针。
//动态分配检查
int main()
{ int *p;
p = new int;
if(!p) {
cout << "allocation failure\n";
return 1;
}
// 或者使用assert宏
// assert宏在标准头文件cassert中;
//assert()有一个参数,表示断言为真的表达式,预处理器产生测试该断言的代码。如果断言不是真,则在发出一个错误消息后程序会终止。
// assert (p != 0);
*p = 20; cout << *p;
delete p; return 0;
}
内存分配的进一步介绍
静态变量:对全局变量和静态变量,编译器为它们分配空间,这些空间在整个程序运行期间都存在
自动变量:函数内的局部变量空间是分配在系统的栈工作区。当函数被调用时,空间被分配;当函数执行结束后,空间被释放
动态变量:在程序执行过程中需要新的存储空间时,可用动态分配的方法向系统申请新的空间,当不再使用时用显式的方法还给系统。这部分空间是从被称为堆的内存区域分配。
字符串与指针
char *String;
String = “abcde”;
字符串常量存储在内存中数据段的区域里;将存储字符串”abcde”的内存的首地址赋给指针变量String
;不能将String
作为strcpy或strcat的第一个参数。
可以通过下标读取字符串中的字符,但不可以通过下标变量赋值。
可以直接作为字符串操作函数的参数。但必须注意,如果该指针指向的是一个字符串常量时,则使用是受限的。如不能作为strcpy的第一个参数。
char *String, ss[ ] =“abcdef”;
String = ss;
将字符数组ss的起始地址存入String
char *String;
String = new char[10];
strcpy(String, “abc”);
动态变量存储在堆工作区 ;将存储字符串”aaa”的内存的首地址赋给指针变量String。
指针与函数
指针作为函数参数
用指针作为参数可以在函数中修改主调程序的变量值,即实现变量传递。
例如交换值函数
void swap(int *a, int *b){
int c;
c=*a; *a= *b; *b=c;
}
// 调用函数
swap(&x, &y);
利用指针参数作为输出
#include <iostream>
#include<math.h>
using namespace std;
int func(double a, double b, double c, double *x1, double *x2){
double delt = b*b-4*a*c;
if (delt>0){
double sdelt = sqrt(delt);
*x1 = (-b+sdelt)/(2*a);
*x2 = (-b-sdelt)/(2*a);
return 2;
}
else if(delt==0){
*x1 = -b/(2*a);
*x2=*x1;
return 1;
}
else{
return 0;
}
}
int main()
{
double px1,px2;
int result = func(2, 4, 2, &px1, &px2);
switch (result){
case 0:
cout<<"0 solve "<<endl;
break;
case 1:
cout<<"1 solve "<<px1<<endl;
break;
case 2:
cout<<"2 solves "<<px1<<","<<px2<<endl;
break;
}
return 0;
}
数组名作为函数参数
数组传递的本质是地址传递,因此形参和实参可以使用数组名,也可以使用指针。
如果传递的是数组
type func(type arr[], int size);
如果传递的是普通的指针
type func(type *p, int size);
C++将数组名作为参数传递处理成指针的传递。
//利用分治法获得数组最大值和最小值
#include <iostream>
#include<math.h>
using namespace std;
void func(int a[], int n,int *max_value, int *min_value){
switch (n){
case 1:
*min_value = *max_value = a[0];
return;
case 2:
if (a[0]>a[1]){
*max_value = a[0];
*min_value = a[1];
return;
}
else{
*max_value = a[1];
*min_value = a[0];
return;
}
default:
int max1,min1,max2,min2;
func(a, n/2, &max1, &min1);
func(a+n/2, n-n/2, &max2, &min2);
if (max1>max2){
*max_value = max1;
}
else{
*max_value = max2;
}
if (min1<min2){
*min_value = min1;
}
else{
*min_value = min2;
}
return;
}
}
int main()
{
int a[]={1,9,8,2,3,7,6,4,5,0};
int in,ax;
func(a, 10, &in, &ax);
cout<<in<<", "<<ax;
return 0;
}
返回指针的函数
当函数的返回值是指针时,返回地址对应的变量不能是局部变量。
//获取子字符串
#include <iostream>
#include<string.h>
#include<math.h>
using namespace std;
char *subString(char *s, int start, int end)
{
int len = strlen(s);
if (start < 0 || start >= len || end < 0 || end >= len || start > end) {
cout << "起始或终止位置错" << endl;
return NULL;
}
char *sub = new char[end - start + 2];
strncpy(sub, s + start, end - start +1);
sub[end - start +1] = '\0';
return sub;
}
int main()
{
char *s="hello, world!";
char *ss;
ss=subString(s, 1,3);
cout<<ss;
return 0;
}
引用与函数
引用的定义:给一个变量取一个别名,使一个内存单元可以通过不同的变量名来访问。
//j是i的别名,i与j是同一个内存单元。
int i;
int &j=i;
引用使用的注意事项:
- 定义引用时必须立即对它初始化,不能定义完成后再赋值。
- 为引用提供的初始值可以是一个变量或另一个引用。
- 引用不可重新赋值,不可使其作为另一变量的别名。
C++引入引用的主要目的就是将引用作为函数的参数。
指针参数
void swap(int *m, int *n)
{ int temp;
temp=*m;
*m=*n; *n=temp;
}
调用:swap(&x, &y)
引用参数
void swap(int &m, int &n)
{ int temp;
temp=m;
m=n; n=temp;
}
调用:swap(x, y)
注意:实参必须是变量,而不能是一个表达式。
C++中,函数参数一般都采用引用传递,好处是减少函数调用时的开销。如果在函数内不许改变参数的值,则参数用const限定,对非const的参数,实际参数不能是常量或临时量。
返回引用的函数是将函数用于赋值运算符的左边,即作为左值。
#include <iostream>
#include<string.h>
#include<math.h>
using namespace std;
int &index(int a[], int n){
return a[n];
}
int main()
{
int a[]={1,2,3};
index(a,1)=5;
cout<<*(a+1);
return 0;
}
指针数组与多级指针
指针数组
一个数组,如果他的元素均为指针,则称为指针数组。
一维指针数组的定义形式:
类型名 *数组名[数组长度];
字符串可以用一个指向字符的指针表示,一组字符串可以用一个指向字符的指针数组来表示。
用二分法查找某一个城市在城市表中是否出现
int binarySearch(char *cityTable[], int lh, int rh, char *cityName){
int mid, result;
if (lh <= rh){
mid =(lh+rh)/2;
result= strcmp(cityTable[mid], cityName);
if (result == 0){
return mid;
}
else if (result > 0){
return binarySearch(cityTable, lh, mid-1, cityName);
}
else{
return binarySearch(cityTable, mid+1, rh,cityName);
}
}
return -1; //没有找到
}
int main()
{
char *string[10] = {"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii","jjj"};
char tmp[10];
while (cin >> tmp){
cout << binarySearch(string, 0, 9, tmp) << endl;
}
return 0;
}
main函数参数
main函数有二个形式参数:int argc
, char *argv[]
argc
:参数的数目(包括命令名本身)
argv[]
:指向每个参数的指针,是一个指向字符串的指针数组
int main(int argc, char *argv[]){
...
}
多级指针
指针指向的内容还是一个指针,称为多级指针。
如有定义:char *string[10];
,string
是一个数组,数组元素可以通过指针来访问。如果p
是指向数组string
的某一个元素,那么p
指向的内容是一个指向字符的指针,因此p
就是一个多级指针。string
也是一个多级指针,不过是一个常指针。
// 按顺序输出指针数组元素
int main()
{
char *city[] = {"aaa", "bbb", "ccc", "ddd", "eee"};
char **p;
for (p=city; p<city+5; ++p)
cout << *p << endl;
return 0;
}