C语言指针入门(二)

1. 二级指针

二级指针就是指针的指针,即存放指针变量地址的一个变量。

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;
	int** pp = &p;
	printf("%d\n", a);
	printf("%d\n", *p);
	printf("%d\n", **pp);
    //打印结果全为10
	printf("%p\n", a);
	printf("%p\n", *p);
	printf("%p\n", **pp);
    //上面打印结果为 000000000000000A

	printf("%p\n", p);
	printf("%p\n", * pp);
    //上面打印结果为 000000E9FCFBF954

	printf("%p\n", pp);
    //上面打印结果为 000000E9FCFBF978
}

我们可以看到,p存储的是变量a的地址,而p本身也是个变量自然也会有地址。

我们在这里用pp来存储指针变量p的地址,上篇文章我们讲过指针运算符*,指针变量p存储a的地址那*p就等同于a,由此可得*pp等同于p的地址,**pp等同于*p的地址等同于a的地址。而pp单独一个地址。

总结 : 二级指针是用来存放一级指针变量的地址的一个变量

2.  指针数组与数组指针

2.1 概念

这个可能是一个容易混淆的地方,我们只要记住指针数组与字符数组,整形数组一样本质上都是数组 字符数组存放字符,整形数组里存放整形数据,那指针数组里自然是存储指针啦 继续往后推,数组也分各种数据类型,以下是三种类型指针数组的定义方式

int *parr1[4];//存放四个整形地址
double *parr2[4];//存放四个double型数据地址
char *parr3[4];//存放四个char类型的地址

而数组指针,就像整型指针,字符指针一样本质上都是指针 整形指针指向整形变量的指针,数组指针是指向数组的指针 以下是三种类型数组指针的定义方式注意区分

int (*parr1)[4];//指向一个整型数组,该数组有4个元素
double (*parr2)[4];//指向一个double型数组,该数组有4个元素
char (*parr3)[4];//指向一个char型数组,该数组有4个元素
2.2 决定因素

我们上篇文章讲到:在定义时*标志这个变量是指针变量但是*优先级较低,所以结合的顺序决定了是指针数组还是数组指针。

1. 指针数组:parr1先与[]结合就说明这个变量是个数组,再与*结合说明是一个指针,所以是指针数组

2.数组指针:parr1先与*结合就说明这个变量是一个指针,再与[]结合说明是一个数组,所以是数组指针

2.3 实践

先来看指针数组的打印输出

	int a = 10, b = 10, c = 10;
	int* parr1[10] = { &a,&b,&c };
	for (int i = 0; i < 3; i++)
	{
		printf("%d ", *parr1[i]);
	}
	printf("\n");
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };
	int* parr2[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d ", parr2[i][j]);
			//printf("%d ", *(parr2[i] + j));//与上一行功能相同
		}
		printf("\n");
	}

我们的parr1由于存储的是地址所以第i个数据需要解引用才可转化成指针变量所指向的值。

而parr2则是一个二维数组的模拟,我们之前提过,数组名代表数组的首元素地址,自然就可以被指针数组存储,(但是要注意,parr2与二维数组有本质上的区别:二维数组在内存中的存储一定是连续的,但parr2存储的是三个一维数组的首元素地址,三个一维数组大概率不连续),我们之前说过parr1[1]等价于*(parr1+1);那parr2[i][j]其实就等价于*(*(parr2+i)+j); 这点与二维数组也是一样的

接下来 来看数组指针的打印输出

	char(*p1)[6];
	char ch[6] = "ctrcv";
	p1 = &ch;
	printf("%s\n", *p1);
	printf("%s\n", p1);
    printf("%\n", p2[0]);
	int(*p2)[5];
	int arr[5] = { 1,6,8,2,4 };
	p2 = &arr;
	printf("%p\n", p2);//整个数组的地址,一般使用数组首地址表示的
	printf("%p\n", *p2);//首元素地址
	printf("%p\n", p2[0]);//等价于*(p+0)也是首元素地址

	printf("%d\n", **p2);//*p为首元素地址再解引用一次就是首元素的值
	printf("%d\n", *p2[0]);//等价于*(*p+0)也是首元素的值

	printf("%d\n", *p2[1]);//等价于*(*(p+1))也就是说跨越了一个数组的距离造成越界
	printf("%d\n", *(*(p2 + 1)));//与上相同

	printf("%d\n", *(*p2+1));//访问了数组的第二个元素
	printf("%d\n", *(*p2+2));//访问第三个元素

因为字符串知道首地址即可打印所以上述三种形式均可打印字符串

而为什么指针数组要用二维数组的方式改变呢? 我们推回去即可,p2==&arr,&arr是数组地址所以p2是数组地址,*p2==*&arr,我们之前提过*&相互抵消了,所以*&arr==arr即首元素地址,以此类推**p2==*arr,因为arr[1] ==*(arr+1) 那*p[1]==*(*(&arr+1)),所以就会往前跨越一个数组的距离

3. const在指针上的应用

3.1 概念

const 是常量限定符,告诉编译器此变量不可修改可以我们对不应该修改的变量进行变动

3.2 实践
	const int num = 0;
	//num = 1;错误
	const int* p = & num;
	int** pp =(int **) &p;
	int b = 1;

	printf("%d\n", *p);
	**pp = 66;
	printf("%d\n", num);
	printf("%d", *p);

上述代码,我们将num变为了常量,不可以直接改变但是可以通过指针来改变,   我们不用上述代码改变还有一种改变方式如下

 //int *p=(int *)&num

//int** pp =&p;

3.3 定义指针变量时的细节
	int n = 3;
	int m = 1;
	const int * p1 = &n;
	//*p1 = 4;错误
	p1 = &m;
	printf("%d\n", *p1);
	int* const p2 = &n;
	//p2 = &m;错误
	*p2 = 4;
	const int* const p3 = &n;
	//*p1 = 4;错误
	//p2 = &m;错误

由上可得

1.const放在*左边,指针变量指向的对象的值不可以通过指针对象来改变,但指针变量本身的值可以改变

2.const放在*右边,指针变量的值不可以改变,但是指针变量指向对象的值可以通过指针对象解引用赋值改变

3. *左右两边都有就全不可以改变

4. 字符数组指针

4.1

我们来了解一下一些易错的点

	const char* p = "abcd";
	printf("%s", p);
	//*p = 'w'; 错误

我们可以通过const来将常量字符串来赋值给指针变量p,p存储的是第一个字符的地址 

4.2 小检测
	const char* p1 = "abcdef";
	const char* p2 = "abcdef";
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	if (p1 == p2)//两个都是常量字符串第一个字符的地址
		printf("1");
	else
		printf("0");
	printf("\n");
	if (arr1 == arr2)//两个数组的首地址
		printf("1");
	else
		printf("0");

让我们想一想结果分别是什么呢?

答案揭晓~ 第一个输出1 ,第二个输出0

很简单,常量字符串在内存中的位置肯定是相同的,而两个数组的首地址肯定创建在内存中不同的地方

这篇文章就到这里啦,希望可以帮到你❥(^_-)

(づ ̄3 ̄)づ╭❤~爱你们

  • 41
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值