深入理解C/C++指针(二)

  对指针来次大的备忘吧!如果恰好帮助了您,不胜荣幸!文章名曰《深入理解C/C++指针(一)》、《深入理解C/C++指针(二)》,需要的博友可连撸效果更佳~~。在此,也感谢郭有强、谭浩强两位教授的C++书籍及众网友的精彩文章。(二)的目录如下:

  1. 指针数组
  2. 指向指针的指针
  3. 指针与函数
  4. const指针

1 指针数组
  二维数组指针变量( 单个变量→二维数组):int (*p)[列数](因指向,p=a;因变量p开辟了自己的地址,&p≠a/p(注意数组名即地址a=&a))。
  指针数组( 数组,其元素为一个个指针变量储存地址):int * pa[4] //(pa→pa[4]→*pa[4])。比如,指针数组指向二维数组:每个元素指向每行(pa[i]=a[i]);指针数组指向一组字符串:每个元素指向一个字符串。【普通数组存“值”,指针数组存“址”。指针数组&原数组是两个不同的数组,pa=&pa≠a=&a】
1.1 数值型指针数组
	int a = 1, b = 2, c = 3;
	int *p[3] = { &a,&b,&c }; //地址初始化
	cout << "数组元素是指针,装地址:" << p[0] << endl;
	cout << "数组元素指针指向的内容:" << *p[0] << "=" << *(*(p + 0) + 0) << endl;
总结。地址:p[i]及其变种;值:*p[i]及其变种;初始化:地址。

	int a[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int *pa[3] = { a[0],a[1],a[2] }; //指针数组:像数组一样初始化
	for (int i = 0;i < 3;i++)
		cout << pa[i] << "=" << a[i] << "," << *pa[i] << "=" << *(*(pa + i) + 0) << "=" << a[i][0] << endl;
	for (int i = 0;i < 3;i++)
	{
		for (int j = 0;j < 3;j++)
			cout << pa[i][j] << "=" << *(*(pa + i) + j) << "=" << a[i][j] << "   ";
		cout << endl;
	}
	cout << pa << "=" << &pa << "≠" << a << "=" << &a << endl; //两个数组各自的首地址
///
	int a[3][3] = { { 1,2,3 },{ 4,5,6 } ,{7,8,9} };
	int(*p)[3] = a; //二维数组指针变量
	for (int i = 0;i < 3;i++)
		cout << p[i] << "=" << a[i] << endl;
	for (int i = 0;i < 3;i++)
	{
		for (int j = 0;j < 3;j++)
			cout << p[i][j] << "=" << *(*(p + i) + j) << "=" << a[i][j] << "   ";
		cout << endl;
	}
	cout << p << "=" << a << "=" << &a << "≠" << &p << endl; //变量指向数组

  总结。只想默默说一句,指针数组与二维数组指针变量“何其相近”!但前者:可指向一系列变量、二维数组(即不仅仅指向二维数组),是一个数组(区别于普通数组它存地址);后者:仅能指向二维数组(专有),是一个变量。由于它们都是“新建”,都开辟了内存空间(&p≠a=&a、pa=&pa≠a=&a),且定义都需常量。总之:指针数组比二维指针变量更普适(对二维数组功能相当),但后者比前者节约内存空间(sizeof( T*p)<sizeof(T *p)×n(n为指针数组长度))。

1.2 字符串型指针数组(一个可列不等二维数组)
	char *name[] = { "BASIC","FORTRAN","C+3","C#","C@@" };//字符串初始化
	for (int i = 0;i < 5;i++)
	{
		cout << "name[" << i << "]的地址:" << name + i << endl; //由于name[i]就是地址,所以name+i是指向地址的地址(指向指针的指针)
		cout << "name[" << i << "]的值(指针→整体→字符串):" << name[i] << endl;
	}
	cout << "第二个元素地址:" << name + 1 << "=" << &name[1] << endl; //地址
	cout << "第二个元素内容(地址→整体作用→字符串):" << name[1] << "=" << *(name + 1) << endl; //FORTRAN
	cout << "name[3]→'C#',它的第二元素*(*(name+3)+1):" << *(name[3] + 1) << "=" << *(*(name + 3) + 1) << "=" << name[3][1] << endl; //#
	cout << "name[4][3]不存在(为空,程序未崩):" << name[4][3] << endl; //空

  本质为“可列不等的二维数组”,适应二维数组的那套指针理论。但注意:它的列址name[i](整体作用)代表某个字符串(行址name+i仍为地址→name[i]),而name[i][j](i,j可越界)代表某字符串的某个字符。

  附:利用指向字符串组的指针数组写排序算法

// 字符串数组(一组字符串)排序算法
void charSort(char *name[], int n)
{
	/*name[i]是指针,可整体表示字符串。时间复杂度:(n-1)+...
	(n-2)+...+2+1=(n-1)n/2=O(n²)。*/
	char *temp;
	int i, j, k;
	for (i = 0;i < n - 1;i++) //待比总个数循环
	{
		k = i; //最初:设待比较的为最小
		for (j = i + 1;j < n;j++) //与后面比较
			if (strcmp(name[k], name[j]) > 0) //后面如有更小的(交换)
				k = j;
		if (k != i) //后面确有更小的
		{
			//同之前“此轮中”暂定为最小的交换
			temp = name[i];
			name[i] = name[k];
			name[k] = temp;
		}
	}
}

2 指向指针的指针
2.1 指向单个变量时
	int **p1, *p2, n = 3;
	p2 = &n; p1 = &p2;
	cout << "*p2=n:" << *p2 << "=" << n << endl;
	cout << "*p1=p2:" << *p1 << "=" << p2 << endl;
	cout << "**p1=*(*p1)=*p2=n:" << **p1 << "=" << n << endl;

2.2 指向二维数组时
①a不能赋值给**p:
int a[3][4] = { 100,1,2,3,4,5,6,7,8,9,10,11 };
	int ** p = (int **)a; /*不做强制类型转换会报错 */
	cout << "p:" << p << ", a:" << a << ", &a[0][0]:" << &a[0][0] << endl;//相等 
	cout << "*p:" << *p << endl; //100(十六进制)
//cout << "**p:" << **p << endl; //错误:访问了100的空间(即:*(100))

  总结:少了一层,p直接指向a[0][0](本应指向“行址”级别)。

②指针数组过渡:
char *name[] = { "BASIC","FORTRAN","C+3","C#","C@@" };
	char **p = name; //p+i=name+i(name+i本身是二级指针,指向name[i])
	for (int i = 0;i < 5;i++)
	{
		for (int j = 0;j < strlen(p[i]);j++)
			cout << *(*(p + i) + j); //p[i][j]等
		cout << endl;
}

③单个指针变量过渡:
int a[2][3] = { 1,2,3,4,5,6 };
	int **p, *q;
	for (int i = 0;i < 2;i++)
	{
		q = a[i];p = &q;
		for (int j = 0;j < 3;j++)
			cout << *(*p + j) << " ";//p[0][j]
		cout << endl;
	}

  总结:将二维数组赋予指针的指针,需要借用“单个指针变量”、“指针数组”来过渡,直接赋值(T **p=a)是不可以的!

3 指针与函数
3.1 函数指针(指向函数的指针变量)
  目的:换用指针的方式去 调用函数。 函数类型(*变量名)(形参表) 函数类型(*变量名)(形参表)(仅用“(*变量名)”替代了函数原型声明中的函数名,其他一样),如 int (*p)(int,int);(p→*p→(*p)(int, int))。此外,可于定义时赋值:int (*p)(int, int)=func; //func不能带参数,函数名代表该函数的入口地址。
  函数指针可作为另一函数的形参(C规定:整个函数不能作为形参,但可作实参)。如double f1(double){f1的函数体}, double func(double a, double b, double (*p)(double)) {func的函数体},main中调用func(a,b,f1)
3.2 指针函数(返回指针值的函数)
  函数的返回值为指针型数据。类型 * 函数名(形参表)。(可一级、多级指针)
4 const指针
int a=1,b=3;

  (1)指向常量的指针变量:即不能通过指针刷新内容,const int * p=&a; //p=10; 错误,但p=&b和a=10都对
  (2)常指针:即不能改变指向,int * const p=&a;// p=&b;错误,但
p=10正确
  (3)指向常量的常指针:既然不能刷新内容、也不能改变指向,const int *const p=&a; //*p=10或p=&b都错,但a=10对
  固定a可用:const int a=1;


《深入理解C/C++指针(一)》整装完毕,等待您的检阅()~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C_xxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值