指针题目练习

hello,大家好哈,窝是脆皮炸鸡,今天的内容是指针方面的练习题,加油!!!

仍然是点赞加关注,追番不迷路。

在这里插入图片描述

一.sizeof和strlen对比

1.1sizeof

sizeof是一种操作符,用来计算变量所占的内存大小(与这个变量是多大没有关系),单位字节。
如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

int main()
{
	int a = 78;
	printf("%zd\n", sizeof(a));
	printf("%zd\n", sizeof(int));

	int arr[10] = { 0 };
	printf("%zd\n", sizeof(arr));
	return 0;
}

在这里插入图片描述

1.2.strlen

strlen是库函数,用来求字符串长度(只能针对字符串or字符数组)。
(是在统计字符串中\0之前的字符个数)
使用strlen需要包含头文件:#include<stdio.h>
在打印时用%zd

char arr[] = { 'a','b','c' };//这个的结果是随机的,因为你也不知道,到底在什么时候会遇见\0
char arr2[] = "abc"; //这个的结果是确定的,它里面就是'a','b','c','\0',在\0之前只有3个字符

printf("%zd\n", strlen(arr)); 
printf("%zd\n", strlen(arr2));

2.数组和指针运算题解析

2.1 一维数组

//计算所占内存大小
int main()
{
	//回忆一下两个例外
	//sizeof(数组名),&数组名
	//之所以4或者8,是因为环境不同,x86或x64
	int a[] = { 1,2,3,4 };
	
	printf("%zd\n", sizeof(a));	//sizeof(a)即sizeof(数组名),这里的数组名代表整个数组,所以大小是16
	printf("%zd\n", sizeof(a + 0));//这里sizeof()里面并不是单纯的数组名,所以代表的不是整个数组,而是首元素地址a+0还是首元素地址,所以大小是4或8
	printf("%zd\n", sizeof(*a));//并不是单纯的数组名,不是那两个例外,所以是首元素地址,*(首元素地址)=首元素,sizeof(1)=4
	printf("%zd\n", sizeof(a + 1));//这是第二个元素的地址,这里是计算二个元素地址的大小,是4或8
	printf("%zd\n", sizeof(&a + 1));//&a是整个数组的地址,&a+1就是整个数组+1,已经跳到数组最后面了,数组后那个位置的地址它也只是一个单纯的地址,大小是4或者8
	printf("%zd\n", sizeof(a[1]));//第二个元素(类型int)的大小,是4
	printf("%zd\n", sizeof(&a));//(&a)是取出整个数组的地址(它也就只是个地址),sizeof(整个数组的地址)也是4或8
	printf("%zd\n", sizeof(*&a));//*和&相互抵消,sizeof(*&a)=sizeof(a),即整个数组的大小,4*4=16
    printf("%zd\n", sizeof(&a[0]));//数组首元素地址,地址的大小都是4或者8
	printf("%zd\n", sizeof(&a[0] + 1));//数组第二个元素地址,地址的大小都是4或者8
}

2.2 字符数组

//计算所占内存大小
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	
	printf("%zd\n", sizeof(arr));//整个数组的大小,6
	printf("%zd\n", sizeof(arr + 0));//首元素地址的大小,4或8
	printf("%zd\n", sizeof(*arr));//两种情况之外,首元素地址解引用,即首元素,1
	printf("%zd\n", sizeof(arr[1]));//首元素大小,1
	printf("%zd\n", sizeof(&arr));//&arr整个数组的地址(不管什么地址,大小都是4或者8)
	printf("%zd\n", sizeof(&arr + 1));//整个数组的地址+1,整个数组后面那个位置的地址的大小,是4或者8
	printf("%zd\n", sizeof(&arr[0] + 1));//第二个元素的地址大小,4或者8
}
//计算内存所占大小
char arr[] = "abcdef";//它代表的是 a b c d e f \0,一共包含7个字符

printf("%d\n", sizeof(arr));//俩例外之一,arr是整个数组,整个数组所占内存大小,7*1=7
printf("%d\n", sizeof(arr + 0));//arr+0,并不是单纯的arr放在sizeof里面,所以arr代表的是首元素地址,+0也是首..址,一个地址的大小是4或者8个字节
printf("%d\n", sizeof(*arr));//不是arr一个在sizeof里面,所以arr代表的是首元素地址,*arr是首元素,sizeof(a)即sizeof(char)=1
printf("%d\n", sizeof(arr[1]));//sizeof(a)==sizeof(char)==1
printf("%d\n", sizeof(&arr));//&arr是首元素地址,一个地址的大小是4或者8个字节
printf("%d\n", sizeof(&arr + 1));//整个数组+1,跳过整个数组之后的位置的地址的大小,是4或者8个字节
printf("%d\n", sizeof(&arr[0] + 1));//首元素地址+1,就会到下一个元素的地址,一个地址的大小,4或者8个字节

//计算字符串长度,单位字节
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };//创建了a,b,c,d,e,f,g,并没有\0
	
	printf("%zd\n", strlen(arr));//arr不在sizeof里,没有&,所以代表的是首元素地址,从首元素开始数,到找到\0为止,个数随机
	printf("%zd\n", strlen(arr + 0));//arr代表首元素地址依然从头开始,到\0为止,个数随机
	printf("%zd\n", strlen(*arr));//首元素地址解引用,*arr是'a',strlen(a)==strlen(97),strlen会认为97是地址,去访问,但是无法访问,程序崩溃
	printf("%zd\n", strlen(arr[1]));//即strlen(b)==strlen(98),仍然崩溃
	printf("%zd\n", strlen(&arr));//strlen(整个数组),依然从头开始访问找\0,个数随机
	printf("%zd\n", strlen(&arr + 1));//跳过整个字符数组,从结尾的地址开始,找\0,个数随机
	printf("%zd\n", strlen(&arr[0] + 1));//从第二个元素地址开始计数,找\0,个数随机
}
	char arr[] = "abcdef";//储存的仍然是a,b,c,d,e,f,\0

	printf("%d\n", strlen(arr));//这里的arr并不是在sizeof里,所以arr是数组首元素地址,strlen统计第一个元素到\0之前的字符个数,6
	printf("%d\n", strlen(arr + 0));//arr不在sizeof里,这里的arr代表的是首元素地址,+0仍然是首元素地址,strlen统计第一个元素到\0之前的字符个数,6
	printf("%d\n", strlen(*arr));//strlen(a)==strlen(97),97被作为地址传给strlen,但97不能访问,程序崩溃
	printf("%d\n", strlen(arr[1]));//strlen(b)与上一个一样
	printf("%d\n", strlen(&arr));//strlen(数组地址),然后从第一个开始访问,直到\0,6个
	printf("%d\n", strlen(&arr + 1));//strlen(跳过整个数组后的地址),后面是未知的,不知道何时遇见\0,结果是随机值
	printf("%d\n", strlen(&arr[0] + 1));//首元素地址+1,strlen(第二个元素的地址),所以,从第二个元素开始,直到\0,统计字符个数,5

2.3 指针变量

char* p = "abcdef";//将数组的地址(首元素)放在指针变量p里面

printf("%d\n", sizeof(p));//指针变量的大小是4或者8个字节(因为指针里面放的是地址,一个地址的大小是4或8个字节)
printf("%d\n", sizeof(p + 1));//p里面放的是首元素地址,p+1就是第二个元素的地址了,大小4或8字节
printf("%d\n", sizeof(*p));//p是char*类型,*p一次只能访问一个字节,访问的是a,sizeof(a是char类型)==1
printf("%d\n", sizeof(p[0]));//p[0]就是*(p+0),*(p),和上一个一样
printf("%d\n", sizeof(&p));//sizeof(指针变量的地址)==4或8个字节   (其实,不管是谁的地址,大小都是4或8)(补充小知识点:&p类型:char**,是二级指针
printf("%d\n", sizeof(&p + 1));//p是变量的地址,&p是指针变量p的地址(char**),&p+1是跳过指针变量p的地址,指向它的后边的地址,只要是地址,大小就是4或8
printf("%d\n", sizeof(&p[0] + 1));//p[0]就是*(p+0)即*p,&p[0]是&*p,即p,&p[0] + 1即p+1,即b的地址sizeof(b的地址)==4或者8
//难点就是+1到底跳过多少
char* p = "abcdef";  //指针变量p里面放的是数组首元素地址
printf("%d\n", strlen(p));//从a的那个地址往后数字符个数,6个
printf("%d\n", strlen(p + 1));//p里是a的地址,a的地址+1就是b的地址
printf("%d\n", strlen(*p));//p是a的地址,*p就是a,即97,程序崩溃
printf("%d\n", strlen(p[0]));//p[0]就是*(p+0),即*p,即a,即97,程序崩溃
printf("%d\n", strlen(&p));//&p即指针变量p的地址,往后找\0,个数是随机值
printf("%d\n", strlen(&p + 1));//跳过指针变量p的地址,往后找\0,个数是随机值
printf("%d\n", strlen(&p[0] + 1));//&(*(p+0)+1)即&*(p+1),即p+1,即b的地址,往后找\0,5

2.4 二维数组

大家可以先复习一下之前讲解的二维数组(在之前的章节里),理解二维数组的含义

  • 二维数组的首元素地址是第一行元素的地址
  • 第一行数组名是a[0],第二行数组名a[1],第三行数组名a[2]

在这里插入图片描述

int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//a单独放在sizeof里,所以a是整个数组,sizeof(整个数组)是计算整个数组的大小==(3*4)*4=48字节
printf("%d\n", sizeof(a[0][0]));//sizeof(第一行第一列)==sizeof(类型int)=4个字节
printf("%d\n", sizeof(a[0]));//第一行数组名单独放在sizeof里,计算的是第一行数组大小,4*4=16 
printf("%d\n", sizeof(a[0] + 1));//a[0]是第一行数组名,但没有单独放在sizeof里面,所以a[0]表示的是第一行数组的首元素地址,即a[0][0]的地址,a[0][0]+1即a[0][1],sizeof(第一行第二个元素的地址)==4个字节
printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1即a[0][1]的地址,*(a[0][1])即a[0][1],sizeof(第一行的二个元素,类型int)==4字节
printf("%d\n", sizeof(a + 1));//a是二维数组数组名,没有单独放在sizeof,代表的是数组首元素地址,首元素是第一行,所以a是第一行的地址,a+1即跳过第一行,是第二行的地址,sizeof(第二行地址)==4或8个字节
printf("%d\n", sizeof(*(a + 1)));
//(1)*(a+1)即a[1],即第二行数组名,单独放在sizeof,即第二行整个数组,计算的是第二行的大小
//a+1是第二行地址,类型int*[4],数组指针,解引用这个数组,16
printf("%d\n", sizeof(&a[0] + 1));//&第一行数组名即第一行地址,第一行地址+1,跳过第一行,是第二行数组的地址,sizeof(第二行地址)=4或8
printf("%d\n", sizeof(*(&a[0] + 1)));//*(第二行地址)访问的是第二行,16
printf("%d\n", sizeof(*a));//a是二维数组的数组名,没有单独放在sizeof里,所以代表的是数组首元素地址,a--第一行的地址,*第一行地址=第一行元素,16
printf("%d\n", sizeof(a[3]));//越界,没有第四行 

3.指针运算笔试题解析

3.1

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

解析:
(1) &a,此处的a代表的是整个数组,&a+1直接跳过整个数组,到整个数组后面的那个地址。
在这里插入图片描述

(2) a是一个数组,&a是取数组的地址,在之前说过,数组的地址存放在哪里?可以回顾一下”数组指针变量与函数指针变量“宝典里的数组指针变量,数组指针是用来存放数组的地址。

从(int * )(&a+1)可以看出,这是在强制类型转换。数组a的类型是int [5],数组指针的类型是int ( * ) [5],将(&a+1)的类型从int ( * ) [5]强制转换为int * ,并将它放在ptr中。

(3) * (a+1),a并没有单独放在sizeof,也没有&,所以a是数组首元素地址,a+1到了第二个元素的地址,* (a+1)就是第一个元素:2。

(4) * (ptr-1):ptr是数组最后面的地址,int * 决定了它的步长和访问权限,【指针+1,和类型有关系。整型指针+1,走4个字节。字符指针+1,走1个字节。结构体指针+1,走一个结构体】ptr-1就是往前走4个字节,刚好指向元素5的地址,* (ptr-1)就是元素5
在这里插入图片描述

3.2

//在X86环境下 ,假设结构体的⼤⼩是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;  
//p前面的那一堆堆都是指针变量p的结构类型
int main()
{
	printf("%p\n", p + 0x1);  //0x1就是1
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);  
	return 0;
}
//打印结果是00100014,00100001,00100004

解析:
(1) 0x的含义是16进制。0x100000是一个整数,类型是int,将它强制转换为(struct Test*)类型,赋给p。

(2) p + 0x1:即结构体+1,题目刚开始就说结构体的大小是20个字节,所以结构体+1就是跳过20个字节,16进制的20是0x14,p+0x1就是0x100014。

(3) (unsigned long)p + 0x1,先将结构体指针变量p强制转换为无符号长整型,所以p此时就是一个整数,整数0x100000+1就是0x10001。

(4) (unsigned int*)p + 0x1:p强制转换为无符号整型的指针,+1,跳过4个字节,即0x100004。

3.3

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}
//打印结果:1

(1) 大家应该注意到了,应该用大括号,却使用了圆括号。(0, 1)这是逗号表达式,从左到右以此计算,最终的表达式结果是最后一个表达式结果,所以(0, 1)的结果是1,(2, 3)的结果是3,(4, 5)的结果是5。所以数组a里面的元素是1,3,5,0,0,0

(2) a[0]不是那两种例外,所以代表的是数组首元素地址,将它放在指针变量p里。p[0]即*(p+0)就是将首元素地址解引用,即首元素:1

3.4

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}
//打印结果是FFFFFFFC,-4

解析:
(1) a不是那两种例外,所以代表的是二维数组首元素地址,即第一行地址,所以a的类型是int (*) [5]

(2) p的类型是int (*) [4]。但现在让p=a,这样的话,是p说了算。

(3) 两个地址相减,得到的是两个地址之间相隔的元素个数。
在这里插入图片描述
(4)从上图可以看出,两地址相隔4个元素,且题目中是小地址-大地址,所以是-4。注意:一个是%d,一个是%p(打印地址,其实内存里存的值就是地址,地址是没有什么原反补之分的)

// -4在内存中存的是补码
//-4的原码10000000000000000000000000000100
//-4的反码11111111111111111111111111111011
//-4的补码1111 1111 1111 1111 1111 1111 1111 1100
           F    F    F    F    F    F    F    12即C
4个二进制的1(就是15)就可以换成一个F

3.5

int main()
 {
 int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 int *ptr1 = (int *)(&aa + 1);
 int *ptr2 = (int *)(*(aa + 1));
 printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
 return 0;
 }
 //打印结果:10,6

(1) &aa中aa代表的是整个数组,&aa+1直接指向二维数组最后面的地址,所以ptr1也在那儿,ptr1转换为int(*)类型,-1则往回走4个字节。将其解引用,则是10
在这里插入图片描述

(2) *(aa+1)即a[1],即第二行数组名,它既没有单独放在sizeof,也没有&,所以它是第二行首元素的地址,即6的地址。所以ptr2就是6的地址,ptr2-1就是5的地址(注意,往前4个字节是因为ptr2是int * 类型),解引用就是5.

3.6

int main()
 {
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
 }

这一道题留给大家,不明白的可以在评论区讨论。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值