CPP指针

本文详细介绍了C++中的指针概念,包括指针初始化、不同类型指针(如空类型指针和野指针)、指针常量和常量指针的区别,以及指针与数组、一维和二维数组的关系,函数指针、空指针调用函数和返回值,安全使用指针、内存分配与释放,以及引用的使用(左值引用和右值引用)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

更多信息 进群了解
QQ群:712333606

一、指针

1.1 指针初始化

//声明时初始化
int a=100;
int *p = &a;
//声明时不初始化,后期赋值
int a=100;
int *p = NULL;
p=&a;

//特别注意不能这样赋值,会发生错误,因此指针初始化最好加上NULL,减少直接赋值造成的危害
int *p;
*p = 100;

//正确的方式为
int k;
int *p = NULL;
p = &k;
*p = 100;

1.2 空类型指针

void *p;

void *p = NULL;
int *pl = NULL;
int a = 100;
pl = &a;
p = pl;
//转为int类型指针
cout<<*(int *)p<<endl;
//只能将对应类型的指针强转为对应的,不然会出现奇怪的异常

空类型指针可以接受任何类型的数据,可以将其转化为所对应的数据类型。

1.3 野指针

  没有初始化的指针变量称为野指针,没有初始化的指针虽然可以使用,但有一定错误危害(不合法的内存空间),为了防止这种危害,良好的编程习惯是在定义指针变量时初始化为NULL,由于NULL处禁止写入,所以一旦有错误,就可以将错误的危害降到最小。

1.4 指针常量

指针的指向不能变,指向的值可以变;

int i = 9;
int *const p = &i;
*p = 3;

1.5 常量指针

指针的指向可以变,指向的值不能变;

int i=9;
int const *p = &i;  //或者const int *p = &i;
//*p = 3;不能修改值

1.6 指向常量的指针常量

既不能修改指向,又不能修改指向的值

int i = 9;
int const *const p = &i; //或者 const int const *p = &i;

1.7 指针和一维数组

int a[10];
int *p;
p = &a[0];

将数组名赋值给整形变量

#include <iostream>
using namespace std;

int main()
{
	int *p = NULL;
	int a[10]={0};
	for(int i=0;i<10;i++)
	{
		a[i] = i;
	}
	p = a;
	for(i=0;i<10;i++,p++)
	{
		cout<<*p<<endl;
	}
	return 0;
}

根据数组名获取数据

cout<<*(a+5)<<endl;//相当于是a[5]
1.7.1 指针操作数组说明

*(p–)相当于a[i–],先对p进行 * 运算,再使p自减

*(++p)相当于a[++i],先对p自加,再进行 * 运算

*(–p)相当于a[–i],先对p自减,再进行 * 运算

*p++相当于a[i++],先 * 运算,再进行自加

1.8 指针和二维数组

int a[4][3];
int *p=NULL;
p = a[0];//a[0]是二维数组中第一个元素的地址
//对于二维数组
a+n  	代表第n行的首地址
&a[n]	代表第n行的首地址
&a[0][0]代表第0行的首地址,也代表第00列的首地址
a[0]+n  代表第0行第n个元素的地址
*(*(a+n)+m)代表第n行第m列元素
*(*(a[n]+m))代表第n行第m列元素

1.9 数组指针和指针数组

//定义一个数组指针,指向一个含有4个整型变量的数组
int (*b)[4];
//定义一个指针数组,存储指针的数组最多能存4个指针
int *c[4];

1.10 指针与字符数组

char *p;
//字符数组就是一个字符串,通过字符指针可以指向一个字符串
char *string="www.com";
//等价与下面的
char *string;
string = "www.com";

1.11 指针函数和函数指针

指针函数是返回值为指针的函数,函数指针是指函数类型的指针变量;

//指针函数,返回值为指针
int * swap(int a, int b);

//函数指针,指针类型的函数
int x=1,y=2;
int sum(int x,int y);	//定义一个函数
int *a(int ,int );		//定义一个函数指针
a = sum;				//让函数指针a指向函数sum
(*a)(x,y);				//调用函数指针指向的函数

1.12 空指针调用函数

  空指针可以指向任意类型函数或者将任意类型的函数指针赋值空类型指针。可以将空指针函数强转为其他函数

#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int plus(int b)
{
	return b+1;
}
int main()
{
	void* pV = NULL;					//定义一个空类型指针
	int result = 0;
	pV = plus;							//让指针指向函数plus
	cout<<"执行pV指向的函数:"<<endl;
  	result=((int(*)(int))pV)(10);	//将空类型指针pV强制转换返回值整型、参数整型的函数指针
	cout<<"result:"<<result<<endl;
	return 0;
}

1.12.1 指针做返回值

返回的确实是地址,但是地址上的值被清空了,所以返回的指针指向的值不是函数体原来的值。

1.13 指针数组

指针数组的数组名也是一个指针,代表该指针数组的起始地址。

int *p[4];
int a=1;
p[0]=&a;
//获取指针数组元素指向的值
cout<<**p<<endl;

1.14 安全使用指针

可参考以下案例

#include <iostream>
using std::cout;
using std::endl;
int* newPointerGet(int* p1)
{
	int k1 = 55;
	p1 =new int;					//变为堆内存。
	* p1 = k1;					//int型变量赋值操作
	return p1;
}
int* PointerGet(int *p2)
{
	int k2 = 55;
	p2 =&k2;  					//指向函数中定义变量所在的栈内存,此段内存在函数执行后销毁
	return p2;
}

int main()
{
	cout<<"输出函数各自返回指针所指向的内存的值"<<endl;
	int* p =NULL;
	p = newPointerGet(p);			//p具有堆内存的地址
	int* i=NULL; 
	i = PointerGet(i); 	 			//i具有栈内存地址,内存内容被销毁
	cout<<"newGet: "<<*p<<" , get: "<<*i<<endl;
	cout<<"i所指向的内存没有被立刻销毁,执行一个输出语句后:"<<endl;  
								//i仍然为55,但不代表程序不会对它进行销毁
	cout<<"newGet: "<<*p<<" , get: "<<*i<<endl;    //执行其他的语句后,程序销毁了栈空间
	delete p;  					//依照p销毁堆内存
	cout<<"销毁堆内存后:"<<endl;
	cout<<"*p:  "<<*p<<endl;
	return 0;
}
1.14.1 内存分配

栈区的缺点是无法改变占用的内存大小,堆区是一种动态分配的内存区域。

int *p = NULL;
p = new int;	//申请动态分配内存
*p = 100;
cout<<*p<<endl;
1.14.2 堆内存释放
int *p = NULL;
p = new int;
*p = 100;
cout<<*p<<endl;
delete p;		//释放堆内存
p =NULL;		//这一步是防止使用已销毁的内存。这一句不能和上面一句颠倒顺序,不然导致内存泄露

内存泄露是指,某块有数据的堆内存没有指针指向它,这块内存犹如丢失一样,称为内存泄露。

//例如下面就是内存泄露的案例
int *p = NULL;
p = new int;
*p = 100;
int k;
p = &k;
//自此p的指向就变到了k,100这块内存没有指针指向它,导致这块内存泄露
* * * 注意指针只能访问内存中数据,不能访问寄存器中数据 * * *

从寄存器中读取数据比从内存中读取更快

拓展:

结构体中使用的是malloc分配空间 和free释放内存

#include<stdio.h>
#include <stdlib.h>
int main()
{
	struct stu
	{
		int num;
		char* name;
		char sex;
		float score;
	}*ps;
	ps = (struct stu*)malloc(sizeof(struct stu));
	ps->num = 102;
	ps->name = "狗蛋";
	ps->sex = 'M';
	ps->score = 62.5;
	printf("Number=%d\nName=%s\n",ps->num,ps->name);
	printf("Sex=%c\nScore=%.2f\n", ps->sex, ps->score);
	free(ps);
	return 0;
}
其他案例

使用递归和栈来计算公式

#include <stdlib.h>
#include <stdio.h>

double f1(int n, int x)				/*自定义函数f1,递归的方法*/
{
	if (n == 0)
		return 1;					/*n为0时返回值为1*/
	else if (n == 1)
		return 2 * x;				/*n为1时返回值为2与x的乘积*/
	else
		return 2 * x * f1(n - 1, x) - 2 * (n - 1) * f1(n - 2, x);/*当n大于2时递归求值*/
}

double f2(int n, int x)			/*自定义函数f2,栈的方法*/
{
	struct STACK
	{
		int num;				/*num用来存放n值*/
		double data;			/*data存放不同n所对应的不同结果*/
	} stack[100];
	int i, top = 0;				/*变量数据类型为基本整型*/
	double sum1 = 1, sum2;		/*多项式的结果为双精度型*/
	sum2 = 2 * x;				/*当n是1的时候结果是2*/
	for (i = n; i >= 2; i--)
	{
		top++;					/*栈顶指针上移*/
		stack[top].num = i;		/*i进栈*/
	}
	while (top > 0)
	{
		/*求出栈顶元素对应的函数值*/
		stack[top].data = 2 * x * sum2 - 2 * (stack[top].num - 1) * sum1;		
		sum1 = sum2;				/*sum2赋给sum1*/
		sum2 = stack[top].data;		/*刚算出的函数值赋给sum2*/
		top--;						/*栈顶指针下移*/
	}
	return sum2;					/*最终返回sum2的值*/
}

void main()
{
	int x, n;						/*定义x、n为基本整型*/
	double sum1, sum2;				/*sum1、sum2为双精度型*/
	printf("请输入n:\n");
	scanf("%d", &n); 				/*输入n值*/
	printf("请输入x:\n");
	scanf("%d", &x);				/*输入x的值*/
	sum1 = f1(n, x);				/*调用f1,算出递归求多项式的值*/
	sum2 = f2(n, x);				/*调用f2,算出栈求多项式的值*/
	printf("用递归算法得出的函数值是:%.2f\n", sum1);	/*将递归方法算出的函数值输出*/
	printf("用栈方法得出的函数值是:%.2f\n", sum2);	/*将使用栈方法算出的函数值输出*/
}

1.15 引用

引用本质是指针常量 int * const

1.15.1 左值引用

c++11标准提出左值引用的概念,如果不加特殊声明,一般都是左值引用

引用实际是一个隐形指针,它为对象建立一个别名

int a =10;
int & b = a;	//左值引用
b = 2;
cout<<a<<endl;  //输出为2

(1) 一个引用被初始化后,无法使用它再去引用另一个对象。

(2) 引用变量只是其他对象的别名,对它的操作与对原来对象的操作具有相同作用

(3) 指针变量和引用的两点主要区别:

  • 指针是一种数据结构,而引用不是数据结构,指针可以转换为它指向变量的数据结构,以便赋值运算符两边的类型相匹配;而使用引用时,系统要求引用和变量的数据类型必须相同,不能进行数据类型转换。
  • 指针变量和引用变量都用来指向其他变量,但指针变量使用的语法更复杂;在定义了引用变量后,其使用方法与普通变量相同。

(4) 引用必须初始化

int a;
int b;
int &c;//编译无法通过
1.15.2 右值引用
类型 && i = 被引用的对象;

示例:

#include <iostream>
using namespace std;
int get()
{
    int i=4;
    return i;
}
int main()
{
    int && k = get()+4;
    //int & k = get() + 4;  出错
    k++;
    std::cout<<"k的值"<<k<<std::endl;
    return 0;
}
### C++指针与二维数组的关系及用法 在C++编程语言中,指针是一种非常强大的工具,用于间接访问内存中的数据。当涉及到二维数组时,理解如何利用指针对其进行操作是非常重要的。 #### 1. 使用一级指针操作二维数组 尽管通常认为需要使用二级指针来操作二维数组,但实际上可以通过将二维数组视为连续的一维数组来进行操作。这是因为二维数组在内存中是以行优先的方式存储的[^4]。下面是一个简单的例子: ```cpp #include <iostream> using namespace std; int main() { int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; int (*p)[4]; // 定义指向含有4个整数的一级指针 p = array; // 将指针初始化为指向array的第一行 for (int i = 0; i < 3; ++i) { for (int j = 0; j < 4; ++j) { cout << *((*p + i) + j) << " "; // 访问元素 } cout << endl; } return 0; } ``` 上述代码展示了如何定义一个指向具有固定列数的一维数组的指针,并将其用来遍历整个二维数组[^3]。 #### 2. 使用二级指针操作二维数组 如果希望更灵活地管理动态大小的二维数组或者模拟矩阵结构,则可以考虑采用二级指针的方法。这种方法涉及创建一个指向指针指针变量,并为其分配足够的空间以容纳每一行的地址[^2]。 以下是具体实现方式的一个实例: ```cpp #include <iostream> using namespace std; int main(){ const int rows = 3, cols = 4; // 动态分配二级指针表示的二维数组 int **matrix = new int*[rows]; for(int i=0;i<rows;++i){ matrix[i]=new int[cols]{}; } // 初始化并打印该二维数组的内容 for(int r=0;r<rows;r++){ for(int c=0;c<cols;c++) { matrix[r][c]=(r+1)*(c+1); cout<<matrix[r][c]<<" "; } cout<<"\n"; } // 清理堆上的资源 for(int k=0;k<rows;k++) delete [] matrix[k]; delete [] matrix; return 0; } ``` 此程序片段说明了如何手动控制内存分配以及释放过程,这对于大型项目尤其重要。 #### 3. 函数参数传递中的应用 当我们将二维数组作为参数传入函数时,除了传统的声明方法外,还可以选择使用指针形式来表达这种关系。这不仅简化了语法书写,而且有助于提高性能。 例如: ```cpp void printArray(const int(*arr)[COLS], size_t rowSize){ ... } // 或者等价于 void printArray(const int arr[][COLS], size_t rowSize){ ... } ``` 在这里,`const int(*arr)[COLS]` 表达了一个指向长度固定的整型数组的常量指针类型。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值