终于来到了指针,有点小激动啊(泪流满面)!!!
和以往不同,在正式学习之前,我们先来看两个小程序:
程序清单4.14 address.cpp #include <iostream> int main() { using namespace std; int donuts=6; double cups=4.5; cout<<"dounuts value = " <<donuts; cout<<" and donuts address = "<<&donuts<<endl; cout<<"cups value = "<<cups; cout<<" and cups address = "<<&cups<<endl; return 0; }
运行结果:
第二个程序:
程序清单4.15 pointer.cpp #include <iostream> int main() { using namespace std; int updates=6; //declare a variable int *p_updates; //declare pointer to an int p_updates=&updates; //assign address of int to pointer //express values two ways cout<<"Values: updates = "<<updates; cout<<", *updates = "<<*p_updates<<endl; //express address two ways cout<<"Address: &updates = "<<&updates; cout<<", p_updates = "<<p_updates<<endl; //use pointer to change value *p_updates = *p_updates + 1; cout<<"Now updates = "<<updates<<endl; return 0; }
运行结果:
从4.14我们可以知道使用地址运算符(&)可以取得变量的地址,而4.15则说明这种地址可以存放在另外一种变量——指针里,指针的值是变量的地址,通过解除引用运算符(*)可以得到变量的值。关于指针的介绍,我们下面开始,上框架图:
语法是这样的:
typeName * pointerName;
指针变量,即pointerName需要4个字节的存储空间,那为什么需要typeName呢?这是因为pointerName是指向一个typeName类型的变量,而不同类型的typeName变量使用的字节数不同,存储值时使用的内部格式也不一定相同。因此,指针声明一定要指定typeName.
举个例子,如4.15中的int *p_updates;p_updates是指针占4个字节,而*p_updates是个int类型的变量,如果声明的是char *p_updates,则*p_updates是个char类型的变量,和int类型所占空间显然不同。
指针的初始化极为重要,使用指针错误往往是因为没有初始化造成的,比如:
long fellow; //create a pointer-to-long *fellow=223323; //place a value in never-never land
看起来没错,编译器也不会报错,但是程序运行不了。这种错误往往会导致一些最隐匿、最难以跟踪的bug。错误就在于我们没有初始化fellow的地址,它可能指向任何地方。
多说无益,看个示例:
#include <iostream> int main() { using namespace std; int higgens=5; int * pt=&higgens; cout<<"Value of higgens = "<<higgens <<"; Address of higgens = "<<&higgens<<endl; cout<<"Value of *pt = "<<*pt <<"; Value of pt =" <<pt<<endl; return 0; }
运行结果:
警告:一定要在对指针引用解引用运算符(*)之前,将指针初始化为一个确定的、合适的地址,这是关于使用指针的金科玉律。
其实赋值没什么好讲的,无非就是取变量的地址赋给指针,但有一个让我觉得很有趣的赋值方式,是这样的;
int * pt; pt=0xB8000000; //type mismatch pt=(int*)0xB8000000; //type now match
语法是这样的:
typeName * pointer_name = new typeName;
看个简单的程序:
#include <iostream> int main() { using namespace std; int nights=1001; int * pt=new int; *pt=1001; cout<<"nights value = "; cout<<nights<<": location "<<&nights<<endl; cout<<"int "; cout<<"value = "<<*pt<<": location = "<<pt<<endl; double *pd = new double; *pd=10000001.0; cout<<"double " ; cout<<"value = "<<*pd<<": location = "<<pd<<endl; cout<<"location of pointer pd: "<<&pd<<endl; cout<<"size of pt = "<<sizeof(pt); cout<<": size of *pt"<<sizeof(*pt)<<endl; cout<<"size of pd"<<sizeof(pd); cout<<": size of *pd"<<sizeof(*pd)<<endl; return 0;
运行结果:
这些地址都是变量或者指针的首地址,关于为什么只显示3个字节和书本上显示了4个字节的疑问,我暂时还不知道。
需要注意的是:变量nights和pd的值都存储在被称为栈(stack)的内存区域中,而new从被称为堆(heap)或自由存储区(free store)的内存区域分配内存。
一般new和delete需要成对使用,就像C语言的malloc和free一样,delete将new申请的内存归还给内存池。
使用方式如下:
int *ps=new int; delete ps;
delete只会释放ps指向的内存,并不会删除指针ps本身,可以将ps重新指向另一个新分配的内存块。只能使用delete来删除new申请的内存,然而,delete对空指针式安全的。
还需要注意的是:delete并不是针对使用new的指针,而是针对new的内存,如:
int * ps=new int; int *pq=ps; delete pq;
这里需要先知道两个概念:静态联编(static binding)和动态联编(dynamic binding).
静态联编:在编译时就事先为数组分配好内存。
动态联遍:在运行时创建数组,分配内存。
数组的动态联编是通过new 和delete来完成的,我们来看一个程序:
#include <iostream> int main() { using namespace std; double * p3=new double[3]; //space for three double p3[0]=0.2; p3[1]=0.5; p3[2]=0.8; cout<<"p3[1] is "<<p3[1]<<".\n"; p3+=1; cout<<"Now p3[0] is "<<p3[0]<<" and "; cout<<"p3[1] is "<<p3[1]<<".\n"; p3-=1; delete [] p3; return 0; }
程序清单4.19 addpntrs.cpp
#include <iostream> int main() { using namespace std; double wages[3]={10000.0,20000.0,30000.0}; short stacks[3]={3,2,1}; //Here are two ways to get the address of an array double * pw=wages; //name of an array = address short * ps=&stacks[0]; //or use address operator //with array element cout<<"pw = "<<pw<<", *pw = "<<*pw<<endl; pw+=1; cout<<"add 1 to the pw pointer:\n"; cout<<"pw = "<<pw<<", *pw = "<<*pw<<"\n\n"; cout<<"ps = "<<ps<<", *ps = "<<*ps<<endl; ps+=1; cout<<"add 1 to the ps pointer:\n"; cout<<"ps = "<<ps<<", *ps = "<<*ps<<"\n\n"; cout<<"access two elements with array notaion\n"; cout<<"stacks[0] = "<<stacks[0] <<", stacks[1] = "<<stacks[1]<<endl; cout<<"access two elements with pointer notation\n"; cout<<"*stacks = "<<*stacks <<", *(stacks+1) = "<<*(stacks+1)<<endl; cout<<sizeof(wages)<<" = size of wages array\n"; cout<<sizeof(pw)<<" = size of pw pointer\n"; return 0; }
运行结果:
C++将数组名解释为第一个元素的地址,即:
wages=&wages[0]=address of first element of array
#include <iostream> int main() { using namespace std; short tell[5]={1,2,3,4,5}; cout<<tell<<endl; cout<<&tell<<endl; short *p=tell+1; short *q = (short*)(&tell+1); cout<<*(p-1)<<'\t'<<*(q-1)<<endl; return 0; }
很神奇?具体解答:http://blog.csdn.net/sszgg2006/article/details/8008164
指针变量加1,其增加的值等于指向的类型占用的字节数。通常使用数组表示法时,C++都执行下面的转换:
arrayname[i] becomes *(arrayname + i) pointername[i] becomes *(pointername + i)
如下:
pointername=pointername+1; //valid arrayname=arrayname+1; //not allowed
24=size of wages array 4=size of pw pointer
程序清单4.20 ptrstr.cpp
//using pointers to strings #include <iostream> #include<cstring> int main() { using namespace std; char animal[20]="bear"; //animal holds bear const char *bird="wren"; //bird holds address of string char * ps; cout<<animal<<" and "; //display bear cout<<bird<<"\n"; // cout<<ps<<endl; //may display a garbage, may cause a crash cout<<"Enter a kind of animal: "; cin>>animal; ps=animal; cout<<ps<<"!\n"; cout<<"Before using strcpy(): \n"; cout<<animal<<" at "<<(int*)animal<<endl; cout<<ps<<" at "<<(int*)ps<<endl; ps=new char[strlen(animal)+1] ; strcpy(ps,animal); cout<<"After using strcpy(): \n"; cout<<ps<<" at "<<(int*)ps<<endl; delete []ps; return 0; }
一般说来,如果给cout提供一个指针,它将打印地址。但如果是char*,则cout则显示指向的字符串。要显示字符串的地址,则必须强制转换成另外一种类型,如int*.此外,在cout和大多数的C++表达式中,char数组名、char指针以及用括号括起来的字符串常量都被解释为字符串第一个字符的地址。
在使用new分配字符串内存的时候注意为空字符分配一个字节的内存。
ps=new char[strlen(animal)+1] ;
程序清单4.21 newstrct.cpp
//using new with a structure #include<iostream> struct inflatable { char name[20]; float volume; double price; }; int main() { using namespace std; inflatable * ps =new inflatable; cout<<"Enter name of inflatable item: "; cin.get(ps->name,20); cout<<"Enter volume in cubic feet: "; cin>>(*ps).volume; cout<<"Enter price: $"; cin>>ps->price; cout<<"Name: "<<(*ps).name<<endl; cout<<"Volume: "<<ps->volume<<" cubic feet\n"; cout<<"Price: $"<<ps->price<<endl; delete ps; return 0; }
自动存储:函数内部定义的常规变了使用自动存储空间,被称为自动变量(automatic variable),即在函数被调用是产生,调用结束时消亡。通常存储在栈中。
静态存储:整个程序执行期间都存在的存储方式。两种使变量成为静态的:函数外面定义和使用关键字static.以后还会详细介绍。
动态存储:内存池,new和delete操作。