C语言的深入——指针,这次一定搞定你(2)

引言:上篇博文,我们针对C语言的灵魂-指针进行了深度探究,为了巩固所学,推出这篇博文,目的在于加深理解和应用所学。
作者:小 琛
欢迎转载,请标明出处

数组和sizeof()

前面我们谈到过,sizeof()是一个可以用来计算数组长度的运算符,而数组是一个和指针密切练习的东西,接下来我们看关于它的题目

#include <stdio.h>
int main()
{
	int a[] = { 1, 2, 3, 4 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));
	return 0;
}

结果如下:
在这里插入图片描述
我们先仅仅看一道这类题目,进行分析讲述。
1、sizeof() 的结果等于对象或者类型所占的内存字节数,例如sizeof(int)就等于4
2、sizeof()仅仅确定一下答案,并不改变其值,它是一个优秀的员工,完成自己的工作同时并不会影响他人。
例如:
int a=10;
printf("%d,%d",sizeof(++a),a)
结果等于:4,10
由此就可以证明,在sizeof()内进行运算例如自加,不会影响这个变量本身的数值。
3、sizeof在进行数组运算的时候,若括号内为首元素地址,则计算整个数组大小,该定则要保证该首元素的地址没有进行任何运算,若给予括号内其它数组元素元素地址,将会被当作一个普通指针运算,而我们知道指针的大小恒为4,也就能解释上面的倒数第三题

有了上述知识,我们再做些练习

#include <stdio.h>
int main()
{
	char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; 
	printf("%d\n", sizeof(arr));//数组名代表首元素地址,结合第三条,计算整个数组大小
	printf("%d\n", sizeof(arr + 0));//这个不好理解,首元素地址一旦进行运算,将不再代表数组即使是加了0,仅仅是一个地址
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

结果如下:
在这里插入图片描述

#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%c\n", arr[0]);//arr数组仍是给了该字符串的每个字符一个单元,结果为a
	printf("%d\n", sizeof(arr));//这里注意每个字符串的结尾都有一个\0,因此有7个成员
	printf("%d\n", sizeof(arr + 0));//首元素地址进行了运算,将不再代表整个数组
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

结果:
在这里插入图片描述
学到这里,会有一个非常容易搞混淆的题:

#include <stdio.h>
int main()
{
	char *p = "abcdef";
	printf("%d\n", sizeof(p));//这里的p虽然也代表字符串首元素地址,但无法计算整个字符串
	printf("%d\n", sizeof(p + 1));//一个指针
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));
	return 0;
}

在这里插入图片描述
注意第一题,sizeof§,虽然p是首元素地址且没有进行运算,但它不能计算整个字符串的大小。大家千万别搞混淆,前面讲到的定则是数组专用的,其它无关人士并不具备!

接下来就是一个难点,二维数组,先看题目:

int a[3][4]={0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

在做题前,我们先学习相关知识,否则会非常吃力。
1、对于二维数组,它在储存上其实可以看作是一个特殊的一维数组,其图形可以这样画:
在这里插入图片描述
也就是说,二维数组仅仅是在逻辑上二维,而物理上也是连续存储的。因此我们可以把二维数组看作n个一维数组,如上图,每个颜色代表一个一维数组,而组合起来就是这个二维数组
2、sizeof()对于一维数组的定则同样适用于二维数组,即:sizeof在进行数组运算的时候,若括号内为首元素地址,则计算整个数组大小,该定则要保证该首元素的地址没有进行任何运算
3、若一个二维数组,仅仅给了一个中括号,例如a[2]则可视为 第2行这个一维数组,即上图中绿色部分
4、二维数组名可以理解成一个二级指针(仅仅是可以理解,并不是真的二级指针),若计算sizeof(a+1),则要注意,a是二维数组的第一行的一维数组的地址,也就是一个数组指针,它进行了加1运算,首先必然不能代表这个二维数组,但它同样不能代表第二行的那个一维数组,因为该变量仍然是一个数组指针,如果要代表一维数组,必然是该一维数组首元素的地址才行,因此仅仅当成是一个指针运算即可。但假如对它进行解引用*(a+1),*那就是说提取到了该数组指针的内容,此时 (a+1)首先代表了该行这个一维数组,而我们又说一维数组首元素数组名是首元素地址,进行运算就可以是整个数组,运算答案将是 16
整道题答案如下:
在这里插入图片描述
总结:
类似的题目,其实都有本质规律,只要按照笔者给出的办法,一步步判断它是什么,就可以解答。切记:先看数组是几维,再看该地址是否为首元素地址且没有进行运算,若不是则是一个普通指针,否则代表整个数组

指针类的题目

1、

#include <stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int *ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));
	return 0;
}

答案:2,5
注意区分&a与a,这里的ptr指针是一个二级指针,a本身是首元素地址,又取地址符,那就是二级指针,它加1,相当于指向了该数组后面的存储单元,再减一解引用到5

2、

#include <stdio.h>
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int *ptr1 = (int*)(&a + 1);
	int *ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

在这里插入图片描述
细看上面的分析,结果则就是:4,20000000

3、

#include<stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式,总是取后一个的值
	int *p;
	p = a[0];
	printf("%d", p[0]);   //p[0]相当于*(p+0)
	return 0;
}

分析:
这道题的考点很隐蔽,也很容易出错。首先,在数组的定义时,使用了逗号表达式,很多人都会误以为时一个全部赋值的二维数组,即应当如下图:
在这里插入图片描述
逗号表达式:取括号内的后值
结合图,定义的p指针应当指向1处,而再对p进行运算p[0],可以换算为:
*(p+0)
综上,输出结果:1

4、

#include<stdio.h>
int main()
{
	int a[5][5];
	int (*p)[4]=(int(*)[4])a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
}

在这里插入图片描述
5、

#include<stdio.h>
int main()
{
	char *c[] = { "ENTER", "NEW", "POINT", "FIRST" }; 
	char **cp[] = { c + 3, c + 2, c + 1, c };
	char ***cpp = cp;
	printf("%s\n",**++cpp);
	printf("%s\n",*--*++cpp+3);
	printf("%s\n",*cpp[-2]+3);
	printf("%s\n",cpp[-1][-1]+1);
	return 0;
}

笔者这里就不分析了,给一张图,读者可自行结合规则分析
在这里插入图片描述
结果如下:
在这里插入图片描述
本文到此结束,谢谢浏览

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值