一、取地址运算符&(内存地址)
C++编译的程序占用的内存分为以下几个部分:
1.栈区:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。与其它分区不同,变量的地址是按照申请顺序降序排列的。
2.堆区: 由new指令申请,由delete指令释放。申请后不释放可能会造成内存资源的浪费。需要指出,对于用指针申请的对内存空间,指针本身存入栈,指针指向的空间则存储在堆中。
3.全局(静态)变量区:全局变量和静态(由static修饰)变量的存储是放在一块的。从程序开始运行时写入变量值,运行结束前释放变量。
4.程序代码区:用于存放函数体的二进制代码。
另外,还有专门用于存放常量的区域。
下面编写程序进行测试,证明以上几种变量分区不同。
#include <iostream> using namespace std;
int x,y,z;
void f1(){};
void f2(){};
void main()
{ int a,b,c;
cout<< "局部变量的地址:" <<endl;
cout<<&a<< " " <<&b<< " " <<&c<<endl; //地址降序
int *m = new int [3], *n = new int , *l = new int , *k = new int ;
cout<< "指针所指向的内存的地址:" <<endl;
cout << &(*n) << " " << &(*l) << " " << &(*k) << endl; //指针指向堆空间地址,地址降序
cout<< "指针指向空间的地址:" <<endl;
cout <<&(m[0])<< " " <<&(m[1])<< " " <<&(m[2])<< endl;
static int w;
cout<< "全局(局部)变量的地址:" <<endl;
cout<<&x<< " " <<&y<< " " <<&z<< /*" "<<&w<<*/ endl;
cout<< "函数代码的地址:" <<endl;
cout<<&main<< " " <<&f1<< " " <<&f2<<endl;
} |
程序运行结果:
由程序运行结果可以看出:
1. 四种变量的地址相互相差很大,而本身相差很小,说明被分配在了不同的内存空间。
2. 指针和变量的地址是降序排列的,说明被分配在了栈区。
3. 全局变量和局部变量的地址相邻,说明二者被安排在了同一分区。
注意:不要把取地址运算符&和应用运算符&搞混,前者放在变量定义中的变量的前面,而后者放在函数原型定义中的类型名后面。
二、指针变量
我们要想更好的利用上面提到的地址,就必须设定一个变量来保存地址。像用变量来保存int、float等类型的值一样,地址也可以用类似的方法保存。
定义:用来保存地址的变量叫做指针变量(pointer variable)或者简称指针。
如:int* ptr;星号表示指向谁,这个声明定义了一个指向整型数据的指针变量。这个变量中保存整型数据的地址:ptr=&variable;
1) 访问指针指向的变量。
*ptr就表示了ptr中的地址上存储的值。变量名前面的星号表示间接引用运算符(dereference operator)或者叫做取内容运算符(contents operator)。
2)void指针
指针保存的变量必须和指针的类型保持一致,如不能将一个float类型的地址赋给一个指向int的指针。
float flovar=98.6;
int * ptr=&flovar; //错误:不能将一个float类型的地址赋给一个指向int的指针
|
但是有一个通用的指针可指向任何数据类型,这类指针成为void:如void* ptr;
三、指针和数组
首先来看看数组表示法和指针表示法访问同一数组元素:
#include <iostream> using namespace std;
int main()
{ int intarray[5]={31,54,36,47,38};
for ( int j=0;j<5;j++)
{
cout<<intarray[j]<<endl //数组表示法
<<*(intarray+j)<<endl; //指针表示法
}
return 0;
} |
指针表示法:数组名就是数组的首地址,在这里+j就是数组第j+1个元素的地址。再加上取内容运算符*就得到了地址上储存的数据了。
考虑:为什么系统能知道+j就跳到j+1个元素的地址呢,在这里地址明明四个字节四个字节的增长的?
答:还记得前面讨论void指针的时候说的吗,在指针说明的时候必须包含指针所指向的数据类型,因为编译器要知道一个指针是int类型的还是float类型的,这样它才能在访问数组元素的时候执行真确的算术运算。
1) 指针常量和指针变量
表达式intarray是系统分给数组空间的首地址,数组将一直保持着这个地址知道程序结束。intarray是一个指针常量,不能进行++或者--运算,就和不能说7++一样。只有变量才可以进行这样的运算。
四、指针和函数
1)首先,同样进行引用传递参数和指针传递参数的对比:
#include <iostream> using namespace std;
void funcref( double &);
void funcptr( double *);
int main()
{ double var=10.0;
cout<< "前var=" <<var<<endl;
funcref(var);
cout<< "引用var=" <<var<<endl;
funcptr(&var);
cout<< "指针var=" <<var<<endl;
return 0;
} void funcref( double & v)
{ v *= 3.14;
} void funcptr( double * ptr)
{ *ptr*=2;
} |
在这里,指针作为参数和应用一样,他传递的不是变量本身,而是变量的地址。(引用是原始变量的一个别名,指针是原始变量的地址)
因为ptr中包含的是var的地址,所以任何对*ptr的操作实际上都是对var的操作。
2)排序数组元素
冒泡法排序:
#include<iostream> using namespace std;
int main()
{ void order( int *, int );
const int N=10;
int array[N]={1,3,234,23,54,656,76,878,87,57};
cout<< "比较前:" ;
for ( int j=0;j<N;j++)
cout<<*(array+j)<< " " ;
order(array,N);
cout<< "\n比较后:" ;
for ( int j=0;j<N;j++)
cout<<*(array+j)<< " " ;
cout<<endl;
return 0;
} void order( int * ptr, int n)
{ void bijiao( int *, int *);
for ( int j=0;j<n-1;j++)
{
for ( int i=j+1;i<n;i++)
{
bijiao(ptr+j,ptr+i);
}
}
} void bijiao( int * m, int * n)
{ int tem;
if (*m>*n)
{
tem=*m;
*m=*n;
*n=tem;
}
} |
五、指针和C类型字符串
1)字符串常量指针:char str1[]=”Defined as an array”;(c类型字符串)
char* str2=”Defined as an array”;(指针类型字符串)
2)看个程序:
#include<iostream> using namespace std;
int main()
{ void copystr( char *, const char *);
char * str1= "hah is hahah!" ;
char str2[80];
copystr(str2,str1);
cout<<str2<<endl;
return 0;
} void copystr( char * dest, const char * src)
{ while (*src)
{
*dest++=*src++;
}
*dest= '\0' ;
} |
※注意※=====const的使用================================================
(http://bbs.chinaunix.net/viewthread.php?tid=683333&extra=&page=1)
关键问题点:const 属于修饰符 ,关键是看const 修饰的位置在那里
1、const int *a
这里const 修饰的是int,而int定义的是一个整值
因此*a 所指向的对象 值 不能通过 *a 来修改,但是 可以重新给 a 来赋值,使其指向不同的对象
eg:
const int *a = 0;
const int b = 1;
int c = 1;
a = &b //ok! 额外:注意不能通过a 来修改 b值
a = &c //ok! 额外:虽然c本身不是一个常量
*a = 2 //erro! 为题就在这里,不能修改通过 *a 所指向的对象值,最后赋值得对象是c,因此不能通过*a 来修改c值。
2、int *const a
这里const修饰的是 a ,a代表的是一个指针地址
因此不能赋给a其他的地址值,但可以修改a指向的值
这有点和cont int *a相反的意味,例子就不说了
3、至于int const *a 和 const int *a 的意义是相同的 他们两个的作用等价
补充:
4、const int * const a
这个代表a所指向的对象的值以及它的地址本身都不能被改变
5.const int a = 10 和 int const a = 10有什么区别?
这应该没区别,指针的话有区别
对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const。
关于const的点滴补充:
1、const 对象的地址只能赋值给指向const 对象的指针
2、指向const 对象的指针可以 被赋 以 一个非const 对象的地址
3、指向const 的指针常被用作函数的形式参数,保证被传递给函数的实际对象在函数得实际对象在函数中不会被修改
4、常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误(上面都是在说明const得问题,所以没有赋值,实际语句中要赋值的)
3)指针数组和数组指针
==================指针数组,数组指针==================================
对于理解指针数组和数组指针也比较有用,如:
--------------指针数组(首先是个数组)--------------------
int *p[10];//指针数组,含有10个指针元素
也就是说每一个元素都是指针。先是解析[]表示它是一个数组,然后*表示指针,int表示为int型指针,即表示定义一个指针数组,含有10个int类型指针元素。
--------------数组指针(首先是个指针)--------------------
int (*p)[10];//数组指针,这个指针能够用来指向含有10个元素的整数数组。
先是解析(),括号里表示这个是个指针,然后[]表示数组,即表示定义了一个指向数组的指针。数组指针。