小言_互联网的博客

C++硬核:指针和引用的常规用法以及应用总结

454人阅读  评论(0)

C++硬核:指针和引用的常规用法以及应用总结

本文主要总结了常规的指针和引用的用法以及在不用情况下的应用,供读者学习参考,转载须经本人许可。

●指针的用法

指针( pointer)简介:

​ 指针是一个值为内存地址变量(或数据对象),内存地址与操作系统与关,为内存随机分配地址;拿到变量的内存地址后可对其对象直接操作

基本用法:

数据类型 * 指针变量名;

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<iostream>
using namespace std;
void main(){
    double num = 1021.2;
    double * ptr_num = &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;
}

空指针( null pointer)

​ 空指针不指向任何对象,在试图使用一个指针之前可以首先检查是否为空

用法

int* ptr1 = nullptr;//等价于int* ptr1=0;
int* ptr2 = 0;//直接将ptr2初始化为字面常量0

void *指针(任意指针)

​ 一种特殊的指针类型,可以存放任意对象的地址

double objnum = 3.12;
double *ptr_obj = & objnum;

//cout<<bool
void *vptr_obj = & objnum;//可指向任意类型
cout<<"地址匹配的结果为:"<<(ptr_bj == vptr_obj)<<endl;

*vptr_obj = 111;//报错原因:无法实现间接赋值,地址指向的内容类型无法确定。

void* 指针的注意事项:

​ 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<<sizeof(score)<<"\t"<<sizeof(ptr_score)<<endl; 
/*
	输出结果为:40	8
*/

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++<<endl;//这里的指针是可以越界的,指向其他的内存地址
}

注意

一个类型为T的指针的移动sizeof(T)为移动单位!!!

Ⅱ.动态分配内存

a.使用new分配内存:

​ 1、在运行阶段为一个int值分配未命名的内存。
2、使用指针来访问(指向)这个值(右->左)。

/*
ptr_int-栈区		  new int在堆区分配了一块int型空间
*/
int* ptr_int=new int;
/*
当ptr_int++移动时, new int的起始分配的内存空间产生内存泄漏
*/
ptr_int++;

b.使用delete释放内存:

​ 1.与new配对使用。

delete ptr_int;

​ 2.不要释放已经释放的内存。

​ 3.不能释放自定义变量分配的内存。

int ptr_int = 10;
delete ptr_int;//报错原因:不能释放自定义变量分配的内存。

c.使用new创建动态分配的数组:

int* intAarry = new int[10];

d.使用delete[ ]释放内存:

delete [] intArray;

关于new和delete使用的视则:
1、不要使用 delete释放不是new分配的内存。

​ 2、不要使用 delete释放同一内存两次。

​ 3、如果使用new[ ]为数组分配内存,则对应delete[ ]释放内存。

​ 4、对空指针(nullptr、Null)使用delete是完全的。

补充:程序的内存分配知识

栈区(stack):

1、由编译器自动分配释放,一般存放函数的参数值、局部变量的值等。

2、操作方式类似数据结构中的栈先进后出。

堆区(heap):

1、一般由程序员分配释放,若程序不放,程序结束时可能由操作系统回收。

2、注意:与数据结构中的堆是两回事,分配方式类似链表。

全局区(静态区 static):

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;
}

Ⅲ.函数传递数组的指针

1.传递实参的内存地址:

向函数传递参数时,复制传递传入的实参的地址。

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;
}

注意事项:

使用指针传递参数时,是无法通过改变指针指向的变量对应的内存地址修改变量的值。而需要通过改变指针指向的变量的值才可以达到修改变量对应的值的目的。

2.指针等价于传递数组的首地址:

向函数传递数组时,形参: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;
}

3.指针等价于二维数组的一维下标:

向函数传递二维数组时,形参: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;
}

Ⅳ.函数指针(Function Pointer)

​ 函数的地址是存储其机器语言代码的内存开始地址(可以在不同的时间使用不同的函数),在实际的应用中,可作为参数进行传递返回值等使用。

函数指针的声明:

//函数原型
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.先定义函数原型,再定义函数指针。

●引用在函数中的应用

1.使用引用的方式传递参数:

传递形参的时候,使用&引用作为实参的别名,直接传递参数。

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;
}

2.引用有默认返回的特性:

函数可以不返回值,默认返回传入的引用对象本身。

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;
}

3.返回引用的函数参数要求:

返回引用时,要求函数参数中包含被返回的引用对象。

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;
}

4.返回引用值的禁止修改:

将返回类型修改为 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;
}

5.数组无法用引用方式传递函数参数:

数组是不能使用引用的方式传递参数的,必须通过指针进行数组的传递。

6.不可以将局部变量作为函数的引用返回类型:

不允许将函数的局部变量作为引用类型返回,否则会发生数据篡改的情况。(局部变量的空间被其他类型变量占用!)

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;
}

转载:https://blog.csdn.net/acceptedday/article/details/107755008
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场