C++指针
学习C++指针既简单又有意思。通过指针可以简化一些C++编程任务的执行,还有一些任务,例如动态内存分配,没有指针是无法执行的。因此学习指针是很有必要的。
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)
运算符访问的地址,它表示了在内存中的一个地址。
实例:
#include <iostream>
using namespace std;
int main(){
int var1;
char var2[10];
cout<<"the memory address of variable var1:"<<&var1<<endl;
cout<<"the memory address of var2:"<<&var2<<endl;
return 0;
}
结果显示:
通过上述案例,了解到了什么是内存地址以及如何访问,接下来看看指针是什么?
指针
指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对指针进行声明,指针变量声明一般形式:
type *var_name;
在这里,type
是指针的基本类型,必须是C++
支持的有效类型,var_name
是指针变量的名称。用来声明指针的星号*
与乘法中使用的*
是一样的,但是在这里就表示var_name
是一个指针变量。
以下是有效的指针声明:
int *p //一个整型的指针
double *p//一个double型的指针
float *p//一个浮点型的指针
char *ch//一个字符型的指针
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一不同的是指针所指向的变量或常量的数据类型不同。
C++中使用指针
使用指针会频繁进行以下操作,定义一个指针变量,把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符*来返回位于操作数所指定地址的变量的值。
实例:
#include<iostream>
using namespace std;
int main(){
int var = 20;//实际变量的声明
int *ip;//指针变量的声明
ip = &var;//在指针变量中存储var的地址
cout<<"Value of var:"<<var<<endl;
//输出在指针变量中存储的地址
cout<<"Address stored in ip pointer variable:"<<ip<<endl;
//访问指针中的地址的值
cout<<"Value of *ip variable:"<<*ip<<endl;
return 0;
}
结果输出:
C++Null指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个NULL值是一个良好的习惯。赋为NULL值的指针被称为空指针。NULL指针是一个定义在标准库中的值为零的常量。
实例:
#include<iostream>
using namespace std;
int main(){
int *ptr = NULL;
cout<<"Value of ptr:"<<ptr<<endl;
return 0;
}
结果输出:
在大多数的操作系统上,程序不允许访问地址为0的内存,因为该内存是操作系统保留的。然后内存地址0有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值)
,则假定它不指向任何东西。如果需要检查一个空指针,可以使用if语句,如下:
if(ptr){}//如果非空,则
if(!ptr){}//如果空,则
因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针,很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。
C++指针的算术运算
指针是一个用数值表示的地址。因此,您可以对指针执行算法运算。可以对指针进行四种算术运算:++、--、+、-
。假设ptr是一个指向地址100的整型指针,是一个32位的整数,对该指针执行下列的算术运算:ptr++
。
在执行完上述的运算之后,ptr将指向位置1004,因为ptr每增加一次,它都指向下一个整数的位置,即当前位置往后移4个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果ptr指向一个地址为1000的字符,上面的运算会导致指针指向位置1001,因为下一个字符位置是在1001。因为整型int是4个字节,字符char是1个字节。
- 递增一个指针
通常会使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10, 100, 200};
int *ptr;
//指针中的数组地址
ptr = var;
for(int i=0;i<MAX;i++){
cout<<"Address of var["<<i<<"]=";
cout<<ptr<<endl;
cout<<"Value of var["<<i<<"]=";
cout<<*ptr<<endl;
ptr++;//移到下个位置
}
return 0;
}
结果显示:
- 递减一个指针
同样地,对指针进行递减运算,即把值减去其数据类型的字节数。
实例:
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10, 100, 200};
int *ptr;
ptr = &var[MAX-1];//指针指向最后一个元素的地址
for(int i=MAX;i>0;i--){
cout<<"address of var["<<i<<"]=";
cout<<ptr<<endl;
cout<<"Value of var["<<i<<"]=";
cout<<*ptr<<endl;
ptr--;
}
return 0;
}
- 指针的比较
指针可以用关系运算符进行比较,如==、<和>。如果p1和p2指向两个相关的变量,比如同一个数组中的不同元素,则可对p1和p2进行大小比较
实例:
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10, 100, 200};
int *ptr;
ptr = var;//指针指向数组第一个元素地址
int i = 0;
while(ptr<=&var[MAX-1]){
cout<<"Address of var["<<i<<"]=";
cout<<ptr<<endl;
cout<<"Value of var["<<i<<"]=";
cout<<*ptr<<endl;
ptr++;
i++;
}
return 0;
}
结果显示:
C++指针VS数组
指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的,例如一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。
实例
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10, 100, 200};
int *ptr;
//指针中的数组地址
ptr = var;
for(int i = 0;i<MAX;i++){
cout<<"Address of var["<<i<<"]=";
cout<<ptr<<endl;
cout<<"Value of var["<<i<<"]=";
cout<<*ptr<<endl;
ptr++;
}
return 0;
}
结果显示:
然而指针和数组并不是完全互换的。
实例:
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10, 100, 200};
for(int i=0;i<MAX;i++){
*var = i;
cout<<"address of var["<<i<<"]=";
cout<<var<<endl;
//var++;//不正确
}
return 0;
}
结果显示:
指针数组
让数组存储指向int
或char
或其他数据类型的指针。下面是一个指向整数的指针数组的声明:int *ptr[MAX];
把ptr
声明为一个数组,由MAX
个整数指针组成。因此ptr
中的每一个元素,都是一个指向int
值的指针。
实例:
#include<iostream>
using namespace std;
const int MAX = 3;
int main(){
int var[MAX] = {10,100,200};
int *ptr[MAX];
for(int i=0;i<MAX;i++){
ptr[i] = &var[i];//赋值为整数的地址
}
for(int i = 0;i<MAX;i++){
cout<<"Value of var["<<i<<"]";
cout<<*ptr[i]<<endl;//取地址上的值
}
return 0;
}
结果显示:
也可以用一个指向字符的指针数组来存储一个字符串列表:
实例:
#include<iostream>
using namespace std;
const int MAX = 4;
int main(){
const char *names[MAX] = {"Zara Ali", "Hina Ali", "Nuha Ali", "Sara Ali"};
for (int i= 0;i<MAX;i++){
cout<<"Value of names["<<i<<"]=";
cout<<names[i]<<endl;//指向字符的指针
}
return 0;
}
结果输出:
C++指向指针的指针(多级间接寻址)
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置
一个指向指针的指针变量必须如下声明:int **var;
在变量名前放置两个星号。当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符。
实例:
#include<iostream>
using namespace std;
int main(){
int var;
int *ptr;
int **pptr;
var = 3000;
ptr = &var;//获取var的地址
pptr= &ptr;//使用运算符&获取ptr的地址
cout<<"value of var:"<<var<<endl;
cout<<"value available at *ptr:"<<*ptr<<endl;
cout<<"Value available at **pptr:"<<**pptr<<endl;
return 0;
}
结果输出:
C++传递指针给函数
C++允许传递指针给函数,只需要简单地声明函数参数为指针类型即可。
实例:
#include<iostream>
#include<ctime>
using namespace std;
void getSeconds(unsigned long *ptr);
int main(){
unsigned long sec;
getSeconds(&sec);
cout<<"Number of seconds:"<<sec<<endl;
return 0;
}
void getSeconds(unsigned long *ptr){
*ptr = time(NULL);//获取当前秒数
return;
}
能接受指针作为参数的函数,也能接受数组作为参数,
实例:
#include<iostream>
using namespace std;
double getAverage(int *arr, int size);
int main(){
int balance[5] = {1000,2,3,17,50};
double avg;
avg = getAverage(balance, 5);//传递一个指向数组的指针作为参数
cout<<"Average value is:"<<avg<<endl;
return 0;
}
double getAverage(int *arr, int size){
int i, sum = 0;
double avg;
for(int i = 0;i<size;i++){
sum += arr[i];
}
avg = double(sum)/size;
return avg;
}
结果显示:
C++从函数返回指针
C++允许从函数返回指针,为了实现此功能,必须声明一个返回指针的函数,如下:
int *myfun(){}
另外,C++不支持在函数外返回局部变量的地址,除非定义局部变量为static
变量。
实例:
#include<iostream>
#include<ctime>
using namespace std;
int *getRandom(){
static int r[10];
srand ((unsigned) time(NULL));
for(int i = 0;i<10;i++){
r[i] = rand();
cout<<r[i]<<endl;
}
return r;
}
int main(){
int *p;
p = getRandom();
for(int i = 0;i<10; i++){
cout<<"*(p+"<<i<<"):";
cout<<*(p+i)<<endl;
}
return 0;
}
结果显示: