本文主要总结了常规的指针和引用的用法以及在不用情况下的应用,供读者学习参考,转载须经本人许可。
指针是一个值为内存地址的变量(或数据对象),内存地址与操作系统与关,为内存随机分配地址;拿到变量的内存地址后可对其对象直接操作。
数据类型 * 指针变量名;
int* ptr num;
char* ptr name;
float* money_ptr;
double* p price;
1.int* p的写法偏向于地址,即p就是一个地址变量,表示一个十六进制地址
2.int *p的写法偏向于值,p是一个整型变量,能够表示一个整型值
(建议两者相结台进行理解)
3、声明中的* 号 和使用中的*号含义完全不同
指针名 = &变量名;
int num=1024;
int* ptr_num;
//取num变量的地址赋值给 ptr_num
ptr_num=#
*指针名=值;
int num = 1024;
int * ptr_num;
ptr_num = #
*ptr_num=111;
//修改num变量的值为111
#include
using namespace std;
void main(){
double num = 1021.2;
double * ptr_num = #
cout<<"num的值为:"<<num<<endl;
cout<<"num的地址为:"<<&num<<endl;
cout<<"ptr_num的值为:"<<ptr_num<<endl;
cout<<"ptr_num指向内存的值为:"<<*ptr_num<<endl;
cout<<"ptr_num的地址为:"<<&ptr_num<<endl;
}
空指针不指向任何对象,在试图使用一个指针之前可以首先检查是否为空。
用法
int* ptr1 = nullptr;//等价于int* ptr1=0;
int* ptr2 = 0;//直接将ptr2初始化为字面常量0
一种特殊的指针类型,可以存放任意对象的地址。
double objnum = 3.12;
double *ptr_obj = & objnum;
//cout<
void *vptr_obj = & objnum;//可指向任意类型
cout<<"地址匹配的结果为:"<<(ptr_bj == vptr_obj)<<endl;
*vptr_obj = 111;//报错原因:无法实现间接赋值,地址指向的内容类型无法确定。
1.void* 指针存放一个内存地址,无法实现间接赋值,地址指向的内容类型无法确定。
2、void *类型指针一般用来:拿来和别的指针比较、作为函数的输入和输出,或者赋值给另一个void *指针。
1.指针同样是一个变量,只不过该变量中存储的是另一个对象的内存地址
2.如果一个变量存储另一个对象的地址,则称该变量指向这个对象
3.指针变量可以赋值,指针的指向在程序执行中可以改变(指针p在执行中某时刻指向变量x,在另一时刻也可以指向变量y)
1、指针变量的命名别和其他变量的命名规则一样
2、指针不能与现有变量同名
3、指针可指向任何基本数据类型、数组和其他所有高级数据结构的地址
4、若指针已声明为指向某种类型数据的地址,则它不能用于有储其他类型数据的地址
5、应为指针指定一个地址后,才能在语句中使用指针
引用是对象的别名,必须在定义的时候初始化。
int value = 1021;
int& revalue = value; //正确
int& revalue;//报错原因:未初始化
const int& invalue = 1000;//使用const进行对常量的引用
注意事项
1.引用并非对象,只是为一个已经存在的对象起的别名。
2、引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起:
int& ref value=10;∥/错误,使用const对常量引用
3、引用必须初始化,所以使用引用之前不需要测试其有效性因此使用引用可能会比使用指针效率高。
1.两者之间的关系引用对指针进行了简单封装,底层仍然是指针。
2.获取引用地址时,编译器会进行内部转换。
数组:
存储在一块连续的内存空间中。
数组名就是这块连续内存空间的首地址。
double score[]{11,22,33,44,55};
double *ptr_score = score;
cout<
64位操作系统中,指针的存放位数是8个字节;而32位操作系统中,指针的存放位数是4个字节。
指针的递增和递减(++、— —)
int i;
double score[5]{98,21,22,43,11};
double *ptr_score;
ptr_score = score;
for(i=0;i<5;i++){
cout<<"当前指针的指向的数值:"<<*ptr_score++<
注意
一个类型为T的指针的移动sizeof(T)为移动单位!!!
1、在运行阶段为一个int值分配未命名的内存。
2、使用指针来访问(指向)这个值(右->左)。
/*
ptr_int-栈区 new int在堆区分配了一块int型空间
*/
int* ptr_int=new int;
/*
当ptr_int++移动时, new int的起始分配的内存空间产生内存泄漏
*/
ptr_int++;
1.与new配对使用。
delete ptr_int;
2.不要释放已经释放的内存。
3.不能释放自定义变量分配的内存。
int ptr_int = 10;
delete ptr_int;//报错原因:不能释放自定义变量分配的内存。
int* intAarry = new int[10];
delete [] intArray;
关于new和delete使用的视则:
1、不要使用 delete释放不是new分配的内存。
2、不要使用 delete释放同一内存两次。
3、如果使用new[ ]为数组分配内存,则对应delete[ ]释放内存。
4、对空指针(nullptr、Null)使用delete是完全的。
1、由编译器自动分配释放,一般存放函数的参数值、局部变量的值等。
2、操作方式类似数据结构中的栈先进后出。
1、一般由程序员分配释放,若程序不放,程序结束时可能由操作系统回收。
2、注意:与数据结构中的堆是两回事,分配方式类似链表。
1、全局变量和静态变量是存储在一起的,程序结束后由系统释放。
1、常字符串就放在这里,程序结束由系统放。
1、存放函数体的二进制代码。
参考代码的分区如下:
int num1= 0;//全局初始化区
int *ptr1;//全局未初始化
int main(){
//栈区
int num2;
//栈区
char str[]="我爱你";
//栈区
char * ptr2;
//“我爱你”在常量区,ptr3在栈区
char * ptr3 ="我爱你";
//全局(静态)初始化区
static int num3= 1024;
//分配的内存在堆区
ptr1 = new int[10];
ptr2 = new char[20];
//注意:ptr1和ptr2本身是在栈区中的
delete [] ptr1;
delete [] ptr2;
return 0;
}
向函数传递参数时,复制传递传入的实参的地址。
void swap1(int * a,int * b){
int *tem;
tem=a;
a=b;
b=tem;
}//交换指针内存放的变量地址
void swap2(int * a,int * b){
int tem;
tem=*a;
*a=*b;
*b=tem;
}//交换指针指向的变量数值
int main() {
int a=0,b=1;
cout<<"swap1交换前的变量a,b地址:"<<&a<<"\t"<<&b<<endl;
swap1(&a,&b);
cout<<"swap1交换后的变量a,b地址:"<<&a<<"\t"<<&b<<endl;
cout<<"swap1交换后:"<<a<<"\t"<<b<<endl;
cout<<"swap2交换前的变量a,b地址:"<<&a<<"\t"<<&b<<endl;
swap2(&a,&b);
cout<<"swap2交换后的变量a,b地址:"<<&a<<"\t"<<&b<<endl;
cout<<"swap2交换后:"<<a<<"\t"<<b<<endl;
return 0;
}
注意事项:
使用指针传递参数时,是无法通过改变指针指向的变量对应的内存地址修改变量的值。而需要通过改变指针指向的变量的值才可以达到修改变量对应的值的目的。
向函数传递数组时,形参:const int array[]等价于 const int * array。(禁止函数修改传入的数组的值)
void print(const int array[],int len){
for(int i =0;i<len;i++){
cout<<array[i]<<"\t";
}
cout<<endl;
}
/*
void print(const int array[],int len)
<==>void print(const int* array,int len)
*/
int main(){
int arr[]={1,2,3,4,5,6};
print(arr,6);
return 0;
}
向函数传递二维数组时,形参:double array(*ptr)[5]等价于double array二维数组表示形式。
void show(double (*arr)[5],int len) {
for(int i=0;i<len;i++) {
for(int j=0; j<5; j++) {
cout<<*(*(arr+i)+j)<<",";
}
cout<<endl;
}
}
int main() {
double powers[3][5]= {
{
1,2,3,4,5
},
{
5,4,3,2,1
},
{
7,6,5,4,7
}
};
show(powers,3);
return 0;
}
函数的地址是存储其机器语言代码的内存开始地址(可以在不同的时间使用不同的函数),在实际的应用中,可作为参数进行传递返回值等使用。
//函数原型
double sum(double,double);
//函数指针声明
double (*ptrsum)(double,double);
用例1:
double sum(double a,double b){
return a+b;
}
int main() {
double a=0,b=1;
double (*ptrsum)(double ,double );//函数指针的声明
ptrsum = sum;//函数指针的初始化
cout<<ptrsum(3,4)<<endl;
return 0;
}
1、该语句声明了一个指针 ptrsum,指向一个函数。
2.double* ptrsum(double, double)不是函数指针,而是:声明了一个函数 ptrsum,返回 double*类型。
3.先定义函数原型,再定义函数指针。
传递形参的时候,使用&引用作为实参的别名,直接传递参数。
void swap3(int & a,int & b){
int tem;
tem=a;
a=b;
b=tem;
}//交换指针指向的变量数值
int main() {
int a=0,b=1;
cout<<"swap3交换前:"<<a<<"\t"<<b<<endl;
swap3(a,b);
cout<<"swap3交换后:"<<a<<"\t"<<b<<endl;
return 0;
}
函数可以不返回值,默认返回传入的引用对象本身。
int &sum(int &num1,int& num2){
num1++;
num2++;//将num2实体值返回
}
int main() {
int a=2,b=9;
int& result = sum(a,b);
cout<<"计算结果为:"<<result<<endl;
return 0;
}
返回引用时,要求函数参数中包含被返回的引用对象。
int &sum(int &num1,int& num2){
num1++;
num2++;
return num1; //将num1实体值返回
}
int main() {
int a=2,b=9;
int& result = sum(a,b);
cout<<"计算结果为:"<<result<<endl;
return 0;
}
将返回类型修改为 const &function(),用于返回值的禁止修改。
const int &sum(int &num1,int& num2){
num1++;
num2++;
return num2; //将num2实体值返回
}
int main() {
int a=2,b=9;
int result = sum(a,b);
cout<<"引用的地址为:"<<&sum(a,b)<<","<<"a的地址:"<<&a<<","<<"b的地址为:"<<&b<<endl;
cout<<"计算结果为:"<<result<<endl;
return 0;
}
数组是不能使用引用的方式传递参数的,必须通过指针进行数组的传递。
不允许将函数的局部变量作为引用类型返回,否则会发生数据篡改的情况。(局部变量的空间被其他类型变量占用!)
int &sum(int a,int b){
int &x=a;
x+=b;
return x;
}
void add(int a, int b){
a+=1920;
b+=100;
}
int main() {
int a=2,b=9;
int &result = sum(a,b);
add(a,b);
cout<<"计算的结果为:"<<result<<endl;
return 0;
}