一、 指针的概念
1.指针含义: 指针就是内存地址。
2.定义:
l 指针是一个变量,其内容为内存地址。
l 指针定义与其他普通变量类似,凡是声明变量的地方,就可声明指针变量。
l 指针变量只是分配了分配了存放地址的空间,但未具体将某个确切地址存入其中。
类型 *指针名;
类型:int,float,char,double……
3.建立指针:
int *iptr,*iptr2;
int a=180; int b=120;
iptr1=&a; //iptr1的值就是a的地址。
iptr2=&b; iptr1=iptr2;
4. 间接引用指针操作符:*
b=*iptr1;
5. 指针变量的地址:
int m=150; int *ptr=&m;
cout<<m<<endl; cout<<ptr<<endl;
cout<<&m<<endl; cout<<*ptr<<endl;
cout<<&ptr<<endl;
6. 指针的初始化:
定义时初始化
运行时初始化
7.指针数组
l 指针的运算(+/-)是以指针的类型展开的。
l 数组名本身就是数组的起始地址。
l 数组名初始化指针:
int a[20];
int *ptr=a; 或 int *ptr=&a[20]; int *ptr=:a;
l 指针与数组的等价操作:
a[i]=*(a+i)=ptr[i]=*(ptr+i);
&a[i]=a+i=ptr+i=&ptr[i];
注意:sizeof(a)与sizeof(*a)的区别!
l 数组名是指针常量----不是左值
int a[200];
a++; //error!
二、 堆内存
1.堆内存是程序运行时临时申请的内存空间,是动态空间。
2.malloc()与free()函数
void *malloc(size);
void *free(void *);
int *ptr;
ptr=(int *)malloc(1000);
…
free(ptr);
1. new与delete操作符
l new分配单个变量-----可提供初始值
int *pi;
pi=new int; pi=new int(100);
l new分配数组-----不能提供初始值
new type[size];
int *pi; pi=new int[100];
l 分配多维数组:
指针p定义为: type (*p)[m][n]…[s];
p=new type[size][m][n]…[s];
其中,m,n…s为整常数,size为整形表达式,该语句建立了有size个元素,
每个元素类型为type[m][n]…[s]的数组,new 返回指向第一个元素的指针。
delete p;
new分配的地址不能改变,将用于delete的释放。
注意:申请内存后,一定要进行是否成功判断。一般申请不到时,返回空指针NULL。
用完内存后,一定要释放所申请的空间。
例:
#include <iostream.h>
//#include <malloc.h>
void main()
{
int arraysize;
int *array;
cout<<"input a number of array:/n";
cin>>arraysize;
array=new int[arraysize])
if((array==NULL)
{
cout<<"can't allocate more memory, return./n";
return;
}
for (int count=0;count<arraysize;count++)
{
array[count]=count*2;
cout<<array[count]<<" ";
}
cout<<endl;
delete[]array;
}
三、 const指针(了解)
1. 指向常量的指针
const int a=28; const int b=36;
const int *pi=&a;
pi=&b; //OK! *pi=56; //error!
pi b
7800:0100(pi地址):7800:0120->7800:0120(b地址):36
pi指向的对象值不能被修改,pi本身可以被修改。
2. 指针常量
指针前加上const
int *const pi;
pi本身不能被修改,pi指向的对象值可以被修改。
3. 指向常量的指针常量
const int a=5;
const int * const pi=&a; //pi和*pi都不能被修改!
四、 字符指针
1. 字符数组和字符串常量------常量指针
字符串常量存放于data区的const区中。
“china”≠ “china”, 两者是地址的比较,要用strcmp()比较!
2. 字符指针
字符串常量,字符数组名,字符指针均属于同一数据类型!
char *ptr=”hello”;
cout<<ptr<<endl; hello
cout<<ptr+1<<endl; ello
cout<<*ptr<<endl; h
3. 字符串比较
l 两个字符串常量的比较是地址的比较,两个数组名的比较也是地址的比较。
l 字符串内容的比较函数
int strcmp(char *str1,char *str2);
int =0: str1=str2;
>0: str1>str2;
<0: str1<str2;
4. 字符串赋值
l 不能对字符数组赋一个字符串
l char *strcpy(char *dest,char *src);
l 数值型数组的赋值函数
memcpy(int *ary1,int *ary2,int n_size);
五、 关于NULL指针和void指针
l NULL是一个指针值,任何指针都可以赋予该值。
l void 只表示一个地址,但不确定该地址起始的类型长度,void 不能运算。
l void *是一种类型,是一种无任何类型的指针。
六、 指针与函数
1. 用指针给函数传递参数
int i , j;
void display( int * a , int *b );
…
i=5; j=10;
display( &i , &j );
2. 传递数组给形参指针
void sum1(int ary1[ ],int n); //编译器将int ary1[]解释为int *类型的指针
void sum2(int *ary1 , int m);
…
int a[10]={……};
sum1(a,10); sum2(a,10);
void sort1( char c1[ ] , int i );
void sort2( char *c2 , int j );
char name[100]={……}; //等价于 char *name=”……”;
sort1(name,100);
sort2(name,100);
3. 返回指针的函数
可以返回堆地址,全局/静态变量地址,但不能返回局部变量地址!
int *fun()
{ int a[10];
…
return a; }
void main()
{ int *p;
p=fun(); } //ERROR!
七、 命令行参数
main()由操作系统调用,返回到操作系统。
void main(int argc,char *argv[ ]);
argc:指出命令行上的参数个数;
argv:指针数组,其每个元素分别指向这些参数;
八.指针常用于以下情形:
1.函数参数
用指针作为函数参数有两个好处:1)可以避免按值传递参数带来的空间浪费,2)可以通过指针间接修改形式参数所指向的地址空间。
2.字符串
就是字符指针(和字符数组一样),字符串非常常用。
3. 动态空间申请
很多时候,要使用的内存空间在设计程序无法知道(如需要用户输入),需要在运行过程中,通过new来动态申请空间,new返回的就是指针。动态申请的空间可以是简单数据类型,也可以是数组、结构或是类等复杂数据类型。
4. 链表结构
指针通常和结构一起使用,组成链表、树、图等各种复杂的数据结构。这些结构对于描述各种现实问题非常有用。
(二)引用
一、 引用的声明
类型 变量名;
类型 & 引用名;
exp: int a; int &b=a;
1. 引用不是用作函数参数或返回值时,声明必须初始化。
2. 引用不是变量,对引用的存储都是对它引用的变量的存储。
3. 引用的初始化值必须是一个变量。
l 如果是对一个常量的引用,编译器要建立临时变量:
const int num=50;
int & ref=num;
cout<<ref; ref=ref+50;
cout<<ref<<endl; cout<<num<<endl;
l 如果声明的引用类型和初始化类型不一致,则编译器同样要建立和
引用类型一致的变量:
float real=50.3;
int &ri=real;
ri=ri+5;
cout<<real<<end; cout<<ri<<endl;
4. 不能声明引用的引用,也不能声明元素类型为引用的数组或指向引用的指针。
5. 可声明对指针的引用。
二、 引用参数
void fn(int &,float &);
l 效果同指针作参数一样,传递的是实参的地址,如果形参在fn()内发生改变,
则实参也根着改变。
l 通过引用作参数,一个函数可以改变另一个函数内的变量。通过引用可以
使一个函数返回多个值。
三、 返回引用的函数
#include<iostream.h>
float temp;
float fn1(float r)
{ temp=r*r*3.14;
return temp; }
float & fn2(float r)
{ temp=r*r*3.14;
return temp; }
void main()
{ float a=fn1(5.0);
float &b=fn1(5.0); //waring!
float c=fn2(5.0);
float &d=fn2(5.0);
cout<<a<<endl; cout<<b<<endl;
cout<<c<<endl; cout<<d<<endl;
}
四、 返回调用作左值
#include<iostream.h>
int a[ ]={2,4,6,8,10,12};
int& index(int i);
void main()
{
index(3)=16;
cout<<index(3); //out 16!
}
int &index(int i)
{ return a[i]; }
注意:返回引用的函数调用作左值时,不能返回函数内的自动变量;
int & fun()
{ int a; …… return a; } //error!
五、 返回堆中变量的引用
int *ipt=new int;
if(ipt==NULL)
{ cout<<”error memory allocation !”; return 1; }
int &IN_t=*ipt;
…
l 不能直接从堆中获得变量空间来初始化引用。
l 释放堆空间有两种方法:
delete &IN_t; 或 delete ipt;
六.引用总结:
引用最大的作用就是作为函数的参数与返回值。
1.引用作为函数参数,有以下好处:
l 引用参数传递的就是实在参数本身,而不是实在参数的一个副本,这样减少了建立
副本的消耗。
l 引用作为参数还可以作为函数返回值的一种有效途径。可以通过将引用定义为是
const引用来限制在函数中对引用所代表的实在参数做修改。
2.引用作为返回值,有以下好处:
l 函数返回的是所返回的变量或对象本身,而不需要建立临时的对象。这样减少了程
序运行的开销;
l 返回一个引用使得一个函数调用的表达式可以成为一个左值表达式,可以为其赋值。
5. 引用的作用和指针类似,但是它摆脱了使用指针可能带来的程序错误,也提高了程
序的可读性和可理解性。推荐在能使用引用的地方尽量不要用指针。
七.有关函数调用的三种方法:
1.传值调用
#include<iostream.h>
void swap(int x,int y);
void main()
{
int a=5,b=6;
cout<<"a="<<a<<",b="<<b<<endl;
swap(a,b);
cout<<"a="<<a<<",b="<<b<<endl;
}
void swap(int x,int y)
{
int temp=x;
x=y;
y=temp;
}
2.传址调用(用指针)
#include<iostream.h>
void swap(int *x,int *y);
void main()
{
int a=5,b=6;
cout<<"a="<<a<<",b="<<b<<endl;
swap(&a,&b);
cout<<"a="<<a<<",b="<<b<<endl;
}
void swap(int *x,int *y)
{
int temp=*x;
*x=*y;
*y=temp;
}
3.传址调用(用引用) // commented by mien , 这种方式同指针,也会改变原有变量得值
#include<iostream.h>
void swap(int &x,int &y);
void main()
{
int a=5,b=6;
cout<<"a="<<a<<",b="<<b<<endl;
swap(a,b);
cout<<"a="<<a<<",b="<<b<<endl;
}
void swap(int &x,int &y)
{
int temp=x;
x=y;
y=temp;
}