第十三讲:指针(5)

这一讲全部是关于指针的一系列练习题

1.sizeof和strlen函数的区别

首先我门要先了解sizeof和strlen函数的区别,总结为以下几点:
在这里插入图片描述

2.数组和指针笔试题解析

总结:
1.sizeof(数组名)中的数组名表示整个数组的地址,功能是求整个数组的大小,单位是字节
2.&数组名中的数组名表示整个数组的地址
3.其它情况数组名表示数组首元素的地址
4.对于指针类型,对其解引用,它的类型决定了能够访问多大的空间
5.对于a+1,a为数组,表示的是跳过a类型大小的指针
6.对于a[1],可以理解成*(a+1)
7.整个数组的地址也是地址,是地址就是4\8个字节
8.对于char* p = “abcdef”,p里面存的是abcdef首字符的地址,也就是a的地址,其实p也就等价于a的地址

2.1一维数组


//2.1一维数组
int main()
{
	int a[] = { 1,2,3,4 };

	printf("%zd\n", sizeof(a));//4 * 4 = 16
	//sizeof数组名,a表示的是整个数组的地址,功能是求整个数组的大小,单位是字节
	printf("%zd\n", sizeof(a + 0));//4\8
	//a+0,a表示的是数组首元素的地址,a+0就表示数组首元素的地址,对于地址,就是4\8个字节
	printf("%zd\n", sizeof(*a));//4
	//*a,a表示数组首元素的地址,*a就表示数组的首元素,也就是1,为int类型,4个字节
	printf("%zd\n", sizeof(a + 1));//4\8
	//a+1,a表示数组首元素的地址,a+1表示跳过一个int类型的地址,即a[1]的地址,地址的单位是4\8个字节
	printf("%zd\n", sizeof(a[1]));//4
	//a[1],可以表示为*(a+1),也就是下表为1的元素的值,也就是2,是int类型,4个字节
	printf("%zd\n", sizeof(&a));//8
	//&a,a表示整个数组的地址,但是整个数组的地址也是地址,是地址就是4\8个字节
	printf("%zd\n", sizeof(*&a));//4 * 4 = 16
	//1. 理解1:*&a,&a取出的是整个数组的地址,进行解引用操作,能够访问int(*) [4]类型的空间,也就是4 * 4 = 16个字节
	//2. 理解2:*和&操作进行抵消,其实就是sizeof(a),也就是第一种情况
	printf("%zd\n", sizeof(&a + 1));//4\8
	//&a,取出整个数组的地址,+1操作跳过了整个数组,指向数组紧邻的位置的地址,但是是地址就是4\8个字节
	printf("%zd\n", sizeof(&a[0]));//4\8
	//&a[0],表示将a[0]位置的地址取出,是地址就是4\8个字节
	printf("%zd\n", sizeof(&a[0] + 1));//4\8
	//&a[0]是将a[0]位置的地址取出,+1操作表示跳过了int类型大小的地址,也就是指向a[1]元素的地址,4\8个字节

	return 0;
}
	

2.2字符数组

2.2.1代码1


int main()
{
	char arr[] = { 'a','b','c','d','e','f' };

	printf("%zd\n", sizeof(arr));//6
	//sizeof(数组名),求的是整个数组的大小,单位是字节,显然,这个数组的大小为6个字节
	printf("%zd\n", sizeof(arr + 0));//4\8
	//arr+0,arr表示首元素地址,+0还表示首元素地址,是地址就是4\8个字节
	printf("%zd\n", sizeof(*arr));//1
	//*arr表示的是数组的首元素,也就是a,为char类型,大小为1个字节
	printf("%zd\n", sizeof(arr[1]));//1
	//arr[1]表示数组第二个元素,1个字节
	printf("%zd\n", sizeof(&arr));//4\8
	//&arr,取出的是整个数组的地址,但是它也是地址,4\8个字节
	printf("%zd\n", sizeof(&arr + 1));//4\8
	//表示紧挨着数组的一个高地址,4\8个字节
	printf("%zd\n", sizeof(&arr[0] + 1));//4\8
	//取出arr[0]的地址,+1表示b的地址,4\8个字节

	return 0;
}


2.2.2代码2


int main()
{
	char arr[] = { 'a','b','c','d','e','f' }; //在这里,数组后边不会自动补上\0

	printf("%d\n", strlen(arr));//随机值
	//strlen(数组名)求的是从这个地址开始,\0之前的字符的个数,而在arr数组中,\0的位置是未知的,所以结果为随机值
	printf("%d\n", strlen(arr + 0));//随机值
	//arr+0表示的示第一个元素的地址,但是\0的位置还是未知的,所以也是随机值,和上一个值的大小相同
	printf("%d\n", strlen(*arr));//系统崩溃
	//*arr得出的是a,a的ASCII码值示97,strlen会将其看成是一个地址,但是97这一块地址是无法访问的,所以这时系统会崩溃
	printf("%d\n", strlen(arr[1]));//系统崩溃
	//arr[1]就是b,ASCII码值为98,同上,系统崩溃
	printf("%d\n", strlen(&arr));//随机值
	//&arr取出的是整个数组的地址,但是在形式上与数组首元素的地址相同,所以也是个随机值,和第一种情况的值相同
	printf("%d\n", strlen(&arr + 1));//随机值
	//&arr+1表示紧邻数组的地址,这时结果也是随机值,但是结果要比第一种情况小6
	printf("%d\n", strlen(&arr[0] + 1));//
	//表示b的地址,是随机值,但是要比第一种情况的随机值小1

	return 0;
}

2.2.3代码3


int main()
{
	char arr[] = "abcdef"; //在这里,数组末尾会自动补上\0

	printf("%zd\n", sizeof(arr));//7
	//sizeof(数组名),计算整个数组的大小,单位是字节,这是要算上\0,所以结果为7个字节
	printf("%zd\n", sizeof(arr + 0));//4\8
	//arr+0表示的是首元素的地址,地址的大小为4\8个字节
	printf("%zd\n", sizeof(*arr));//1
	//*arr表示首元素,也就是a,为char类型,1个字节
	printf("%zd\n", sizeof(arr[1]));//1
	//arr[1]表示数组第二个元素,也就是b,1个字节
	printf("%zd\n", sizeof(&arr));//4\8
	//&arr取出的是整个数组的地址,但还是一个地址,4\8个字节
	printf("%zd\n", sizeof(&arr + 1));//4\8
	//&arr+1表示紧邻数组的那个地址,4\8个字节
	printf("%zd\n", sizeof(&arr[0] + 1));//4\8
	//&arr[0]+1表示b的地址,4\8个字节

	return 0;
}

2.2.4代码4


int main()
{
	char arr[] = "abcdef";

	printf("%d\n", strlen(arr));//6
	//strlen求的是从这个地址开始到\0结束的字符的个数,这里的arr表示首元素的地址,所以结果为6
	printf("%d\n", strlen(arr + 0));//6
	//arr+0还表示首元素的地址,为6
	printf("%d\n", strlen(*arr));//程序崩溃
	//*arr表示a,ASCII码值为97,无法访问,程序崩溃
	printf("%d\n", strlen(arr[1]));//系统崩溃
	//arr[1]表示b,同上,系统崩溃
	printf("%d\n", strlen(&arr));//6
	//&arr取出整个数组的地址,但是形式上还是首元素地址,结果为6
	printf("%d\n", strlen(&arr + 1));//随机值
	//&arr+1表示紧邻数组的地址,这是\0的位置未知,大小是随机值
	printf("%d\n", strlen(&arr[0] + 1));//5
	//表示b的地址,结果为5

	return 0;
}

2.2.5代码5


int main()
{
	char* p = "abcdef";

	printf("%zd\n", sizeof(p));//4\8
	//p为指针类型,大小为4\8个字节
	printf("%zd\n", sizeof(p + 1));//4\8
	//p+1表示b的地址,为4\8个字节
	printf("%zd\n", sizeof(*p));//1
	//*p表示a,大小为1个字节
	printf("%zd\n", sizeof(p[0]));//1
	//p[0]表示a,1个字节
	printf("%zd\n", sizeof(&p));//4\8
	//&p取出的是p指针的地址,还是一个地址4\8个字节
	printf("%zd\n", sizeof(&p + 1));//4\8
	//&p+1指针p的地址,再向后访问一个地址,为4\8个字节
	printf("%zd\n", sizeof(&p[0] + 1));//4\8个字节
	//表示b的地址

	return 0;
}


2.2.6代码6


int main()
{
	char* p = "abcdef";

	printf("%d\n", strlen(p));//6
	//p其实就是表示a的地址,所以结果为6
	printf("%d\n", strlen(p + 1));//5
	//p+1表示b的地址,结果为5
	printf("%d\n", strlen(*p));//系统崩溃
	//*p,对p解引用,得到a,a的ASCII码值为97,系统崩溃
	printf("%d\n", strlen(p[0]));//系统崩溃
	//p[0]表示a,系统崩溃
	printf("%d\n", strlen(&p));//随机值
	//&p取出的是指针类型p的地址,p之后\0的位置不确定,为随机值
	printf("%d\n", strlen(&p + 1));//随机值
	//&p+1表示紧挨着p的地址,仍为随机值,+1会向后移动
	printf("%d\n", strlen(&p[0] + 1));//5
	//表示b的地址,结果为5

	return 0;
}


2.3二维数组

总结:
1.对于二维数组,二维数组中的元素是一维数组
2.对于int a[3][4] = { 0 },第一个元素是a[0],第二个元素是a[1]
3.对于sizeof(a + 1),a表示的是首元素的地址,也就是a[0]的地址,而对于第一行的数组+1,就跳到了a[1],这时表示的时第二行数组的地址,所以再sizeof求大小,结果为4\8个字节
对于*(a+1),a表示数组首元素的地址,也就是第一行数组的地址,+1就表示第二行数组,进行解引用就拿到了第二行的数组,也就是表示a[1],如果这时给出sizeof(*(a+1))这样的代码的话,就是表示sizeof(a[1]),也就是第二行的数组的大小,单位是字节


int main()
{
	int a[3][4] = { 0 };

	printf("%zd\n", sizeof(a));//4 * 12 = 48
	//sizeof(数组名),计算的是整个数组的大小,单位是字节,也就是4 * 12 = 48个字节
	printf("%zd\n", sizeof(a[0][0]));//4
	//a[0][0]表示的是第一行第一个元素,为int类型,4个字节
	printf("%zd\n", sizeof(a[0]));//4 * 4 = 16
	//a[0]表示第一行的数组,结果为4 * 4 = 16个字节
	printf("%zd\n", sizeof(a[0] + 1));//4\8
	//a[0]+1表示第二行的数组,但是并没有单独放在sizeof里边,所以+1表示的是第一行第二个元素的地址,4\8个字节
	printf("%zd\n", sizeof(*(a[0] + 1)));//4
	//a[0]+1为地址,解引用表示的就是第一行第二个元素,大小为4个字节
	printf("%zd\n", sizeof(a + 1));//4\8
	//a表示首元素的地址,+1表示跳过了int (*) [4]个字节,表示第二行数组的地址,结果为4\8个字节
	printf("%zd\n", sizeof(*(a + 1)));//4 * 4 = 16
	//a+1中a表示首元素的地址,+1表示第二行数组的地址,解引用之后再进行sizeof进行计算,结果为4 * 4 = 16个字节
	printf("%zd\n", sizeof(&a[0] + 1));//4\8
	//将a[0}的地址取出,再加1,表示第二行数组的地址,地址为4\8个字节
	printf("%zd\n", sizeof(*(&a[0] + 1)));//4 * 4 = 16
	//进行解引用,表示第二行的数组,字节大小为4 * 4 = 16
	printf("%zd\n", sizeof(*a));//4 * 4 = 16
	//解引用,表示第一行数组,为4 * 4 = 16个字节
	printf("%zd\n", sizeof(a[3]));//4 * 4 = 16
	//仔细观察a[3],根本没有这个数组,那么是不是值就不知道了?不对!
	//sizeof进行操作,不会进行括号内的运算,只需要计算字节大小就可以了,所以结果为4 * 4 = 16个字节

	return 0;
}

3. 指针运算题目解析

3.1题目1


//3.1题目1
int main()
{

	int a[5] = { 1, 2, 3, 4, 5 }; //2   5
	int* ptr = (int*)(&a + 1); //&a取出的是整个数组的地址,&a+1表示紧挨着a数组后边的那一块地址,将其强转成(int*)类型并赋给ptr,其实就表示ptr中放了那一块地址
	//对于ptr - 1,表示向前移动了4个字节的单位,也就是指向了5的地址,进行解引用,表示5
	//对于a + 1,a表示数组首元素的地址,a+1就指向了2的地址,进行解引用就是2
	printf("%d,%d", *(a + 1), *(ptr - 1));

	return 0;
}


3.2题目2


//3.2题目2
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000; 
//由题意可知,p所指向的空间为20个字节,这个空间的首地址是0x100000,因为地址的类型为int*类型,所以需要强制类型转化成结构体指针类型

int main()
{

	printf("%p\n", p + 0x1);//0x100014
	//p的地址为16进制类型,加上0x1,其实就是p+1,就表示跳过一个结构体类型,也就是20个字节,20在16进制中的表示为0x14,所以结果为0x100014
	printf("%p\n", (unsigned long)p + 0x1);//0x100001
	//将p强转成unsigned long类型,就表示成了一个数,数之间的相加减,是直接相加减,所以结果为0x100001
	printf("%p\n", (unsigned int*)p + 0x1);//0x100004
	//将p强转成unsigned int*类型,再+0x1,表示向后跳过了unsigned int类型个字节,也就是4个字节,所以结果为0x100004

	return 0;
}


3.3题目3


//3.3题目3
int main()
{

	int a[3][2] = { (0, 1), (2, 3), (4, 5) }; 
	//注意!!!这里的元素全部都是由()括起来的,所以会将其中的元素当成逗号表达式,也就是a中存了三个逗号表达式得出的结果
	//对于逗号表达式,从左向右依次计算,得出的结果为最后一个表达式的结果
	//所以说,a中存的元素只有三个,为:1, 3, 5
	int* p;
	p = a[0];
	//a[0]不满足两种特殊情况(sizeof和&),所以表示数组首元素的地址,所以p中存的就是第一行首元素的地址
	printf("%d", p[0]);
	//表示*(p+0),也就是将p中存的那个地址解引用,所以得到的结果为1

	return 0;
}


3.4题目4


//3.4题目4
int main()
{

	int a[5][5];	
	//定义一个5行5列的数组
	int(*p)[4];
	p = a;
	//p的类型为int(*)[4],但是a的类型为int(*)[5],所以这个代码就是强制将a的首元素的地址塞到p指针数组里,也就是将a[0]的地址塞到了p中
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	//这里只稍微介绍,具体看下边的图
	//p[4][2]表示*(*(p+4)+2),*(p+4)表示a[0]向后移动int(*)[4]个单位,然后再+2解引用表示再向后移动2个int类型的长度
	//a[4][2]表示第4行第二个元素
	//以上两个取地址进行相减,得到的数为-4,因为是小数-大数
	//对于&p[4][2] - &a[4][2],打印地址和打印整数是不同的
	//1.对于打印整数:
	//①:如上,打印的是有符号整形的数,就是将-4直接打印
	//②:如果要打印无符号整形的数,就要将-4的补码写出,再计算补码的值,这个值显然很大
	//2.对于打印地址:
	//整数在内存中存放的是补码,地址其实就是它的补码,对于-4:
	//原码:10000000000000000000000000000100
	//反码:11111111111111111111111111111011
	//补码:11111111111111111111111111111100
	//而内存中的地址是16进制数,16为F,1100为12,也就是C,所以结果为FFFFFFFFFFFFFFFC

	return 0;
}


在这里插入图片描述

3.5题目5


//3.5题目5
int main()
{

	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//
	int* ptr1 = (int*)(&aa + 1);
	//&aa取出的是整个数组的地址,&aa+1表示的是紧挨着aa数组的那块地址,将这块空间的地址强转成int*类型后存储在ptr1这个指针变量中
	int* ptr2 = (int*)(*(aa + 1));
	//aa表示的是数组首元素的地址,+1表示的是第二行数组的首元素的地址,进行解引用操作就得到了第二行的整个数组,也就相当于aa[1]
	//而数组名没有单独放在&或sizeof中,表示的是数组首元素的地址,也就是aa[1][0]、6的地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	//ptr1-1表示的就是10的地址,解引用就得出10
	//ptr2-1就表示5的地址,解引用就得出5

	return 0;
}


3.6题目6


//3.6题目6
int main()
{

	char* a[] = { "work","at","alibaba" };
	//a是一个指针数组,其中存放的是指针,也就是这三个单词首字符的地址
	char** pa = a;
	//pa是一个二级指针,指向一级指针的地址,而a是一个数组,表示数组首元素的地址,所以pa中存的就是w的地址
	pa++;
	//pa++表示向后移动char类型个字节,也就是a向后移动一个单位,就指向了a的地址
	printf("%s\n", *pa);
	//pa进行解引用就得到了at

	return 0;
}


3.7题目7


//3.7题目7
int main()
{

	//这里还是先简单叙述,详细讲解请看下面的图片
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//将四个数组首元素的地址存到c数组中
	char** cp[] = { c + 3,c + 2,c + 1,c };
	//表示将四个元素的倒叙元素首地址存到cp数组中
	char*** cpp = cp;
	//将cp首元素的地址存到cpp中
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);

	return 0;
}


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 23
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值