指针经典试题

本文围绕指针相关知识展开,对比了sizeof操作符和strlen函数,介绍了数组名的含义及两个例外情况。通过一维数组、字符数组、二维数组的经典笔试题,加深对指针和数组的理解。还给出了指针运算的经典笔试题及解析,帮助读者掌握指针运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习完指针的相关知识点后,为了加深老铁们关于指针的理解,一起来拿这些试题练练手把!毕竟实践是检验真理的唯一标准!理论知识再6,也得学会实际运用才行,这些试题刚开始你可能觉得简单或困难,无论如何都得静下心来,理解其中的奥义,相信你可以收获关于指针的新的理解!最后那一句老梗激励我们继续学习吧!Practice makes perfect——熟能生巧!!

目录

1、sizeof与strlen的对比

1-1 sizeof操作符

​编辑

1-2 strlen函数

​编辑

1-3 sizeof与strlen的对比

2、数组与指针经典笔试题

2-0 储备知识

2-1 一维数组

2-1-1 小结时刻

2-2 字符数组

2-2-1 代码1(sizeof操作符)----结尾无具体 \0字符

2-2-2 代码2(strlen函数)----结尾无具体 \0字符

2-2-3 代码3(sizeof操作符)----结尾有具体 \0字符

2-2-4 代码4(strlen函数)----结尾有具体 \0字符

2-2-5 代码5(sizeof操作符)----字符指针变量的应用

2-2-6 代码6(strlen函数)----字符指针变量的应用

2-2-7 小结时刻

2-3 二维数组

2-3-1 小结时刻

3、指针运算经典笔试题

3-1 试题一----指针+-整数

3-2 试题二----指针+-整数

3-3 试题三----逗号表达式

3-4 试题四----指针-指针

3-5 试题五----指针+-整数

3-6 试题六----指针在二维数组的应用

3-7 试题七----指针在二维数组的应用


1、sizeof与strlen的对比

1-1 sizeof操作符

在学习操作符的时候,我们学习了sizeof操作符,sizeof操作符是计算变量所占内存空间的大小的,单位是字节,如果操作数是类型的话,计算的是使用该类型创建的变量所占内存空间的大小。

sizeof只关注占用内存空间的大小,不关注内存中存放什么数据

实例代码:

#inculde <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
return 0;
}

输出结果:

输出结果为什么是这个呢?看完解析相信你就懂了!!

完整代码解析:

//sizeof操作符
#include <stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));//4--sizeof操作符计算大小不管具体数据只管是什么类型
	printf("%d\n", sizeof a);//4--括号可以省略
	printf("%d\n", sizeof(int));
	return 0;
}

1-2 strlen函数

strlen是C语言库函数,功能是求字符串长度

函数原型:

函数功能:统计的是从strlen函数的参数str中这个地址开始向后,\0之前字符串中字符的个数

strlen函数会一直向后找\0字符,知道找到为止,所以可能会存在越界访问

关于具体的strlen函数的使用和其他的字符函数与字符串函数使用,请看上一篇博客学习

http://t.csdnimg.cn/Kv122

实例代码:

#include <stdio.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
 
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr2));
return 0;
}
输出结果:
输出结果为什么是这个呢?看完解析相信你就懂了!!

完整代码解析:

//strlen函数
#include <stdio.h>
int main()
{
	char arr1[3] = { 'a', 'b', 'c' };//数组内容是	a b c 
	char arr2[] = "abcd";//内容a b c d \0
	printf("%d\n", strlen(arr1));//随机值--不会遇到\0,会一直向后访问知道遇到\0,会产生越界访问的问题
	printf("%d\n", strlen(arr2));//4--遇到\0就停下来计算\0之前字符的个数

	printf("%d\n", sizeof(arr1));//	3
	printf("%d\n", sizeof(arr2));//	5
	return 0;
}

1-3 sizeof与strlen的对比

sizeof

1、sizeof是操作符

2、sizeof计算操作数所占内存的大小,单位是字节。

3、不关注内存中存放什么数据

strlen

1、strlen是库函数,使用需要引用头文件,<string.h>

2、关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界

3、strlen是求字符串长度的,统计的是\0之前字符的个数


2、数组与指针经典笔试题

2-0 储备知识

数组名的理解
 1、数组名是数组首元素(第一个元素)的地址,但有两个例外
 (1)sizeof(数组名)--数组名表示整个数组,计算的是整个数组的大小!单位是字节。数组名必须单独放在sizeof内部才是这种情况,否则不是!
 (2)&数组名--数组名表示整个数组,取出的是整个数组的地址!——无需只存在&与数组名
 除此之外,数组名就是数组首元素(第一个元素)的地址

2-1 一维数组

#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;

}

输出结果:

输出结果为什么是这个呢?看完解析相信你就懂了!!

分析:想知道输出结果我们得先知道数组名的理解——数组名是首元素的地址,但有两个例外,和int * 的理解——解引用访问的是整型,char * 理解——解引用访问的是字符型,至于为什么输出结果是这个请看下面的解析把老铁们!!

完整代码解析:

一维数组
#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//	16--sizeof(数组名)的场景,sizeof(数组名)计算的是整个数组的大小
	
	printf("%d\n", sizeof(a + 0));//	4/8--a是首元素的地址,类型是int * ,a+0还是首元素的地址,是地址的话大小就是4/8个字节		
	
	printf("%d\n", sizeof(*a));//	4--a是首元素的地址,*a就是首元素,大小是4个字节。计算的是整型数组的第一个元素内存大小是4个字节
	//*a==a[0]==*(a+0)
	
	printf("%d\n", sizeof(a + 1));//	4/8--a是首元素的地址,类型是int *,a+1跳过1个整型,a+1就是第二个元素的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(a[1]));//	4--a[1]就是第二个元素,大小是4个字节
	
	printf("%d\n", sizeof(&a));//	4/8--&a是取出整个数组的地址,数组的地址也是地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(*&a));//	16--理解1、&*互相抵消了,sizeof(*&a)==sizeof(a)。连续使用&操作符与*操作符,两操作符的作用相互抵消,相当于没有使用操作符sizeof(数组名)计算的是整个数组的大小-16
	//理解2、&a是取出整个数组的地址,类型是int (*)[4],对数组指针解引用访问的就是数组,转变为sizeof(数组名)的情况,那么计算的就是整个数组的大小-16
	
	printf("%d\n", sizeof(&a + 1));//	4/8--&a+1是跳过整个数组后的那个位置的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&a[0]));//	4/8--首元素的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&a[0] + 1));//	4/8--&a[0]+1是数组第二个元素的地址,是地址的话大小就是4/8个字节
	return 0;
}

2-1-1 小结时刻

  1. &a+1的类型是int(*)[4],a+1的类型是int *
  2. 数组名是首元素的地址,但有两个例外( sizeof(数组名)和&数组名)。具体内容请看前文!
  3. *&a有两种理解:理解1、&*互相抵消了,sizeof(*&a)==sizeof(a)。连续使用&操作符与*操作符,两操作符的作用相互抵消,相当于没有使用操作符sizeof(数组名)计算的是整个数组的大小。理解2、&a是取出整个数组的地址,类型是int (*)[4],对数组指针解引用访问的就是数组,转变为sizeof(数组名)的情况,那么计算的就是整个数组的大小
  4. *a==a[0]==*(a+0)
  5. sizeof操作符是计算类型所占内存空间的大小,不在乎内存中存放什么数据!!
  6. 指针+-整数跳过的是若干个指针指向对象的类型!
  7. 地址是有大小的,在不同的平台上对象不同,32位平台--4个字节;64位平台--8个字节!
     

2-2 字符数组

2-2-1 代码1(sizeof操作符)----结尾无具体 \0字符

#include<stdio.h>

int main()

{

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
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 arr[] = { 'a','b','c','d','e','f' };//数组内容a,b,c,d,e,f
	printf("%d\n", sizeof(arr));//	6--数组名单独放在sizeof内部,计算的是整个数组的大小,单位是字节
	
	printf("%d\n", sizeof(arr + 0));//	4/8--arr是数组名表示首元素的地址,arr+0还是首元素的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(*arr));//		1--arr是首元素的地址,*arr就是首元素,那么计算的是首元素的大小,大小是1个字节!
	// *arr==arr[0]==*(arr+0)
	
	printf("%d\n", sizeof(arr[1]));//	1--arr[1]是第二个元素,大小是1个字节
	
	printf("%d\n", sizeof(&arr));//	4/8--&arr是取出整个数组的地址,数组的地址也是地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&arr + 1));//	4/8--&a+1是跳过整个数组后的那个位置的地址,即指向数组后面的空间,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&arr[0] + 1));//	4/8--&arr[0]+1是数组第二个元素的地址,是地址的话大小就是4/8个字节
	return 0;
}

2-2-2 代码2(strlen函数)----结尾无具体 \0字符

#include<stdio.h>

int main()

{

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

return 0;

}

输出结果:

输出结果为什么是这个呢?看完解析相信你就懂了!!

完整代码解析:

#include<stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };//数组内容同理
	printf("%d\n", strlen(arr));//	随机值--arr是数组首元素的地址,数组中没有\0,就会导致数组 越界访问,所以结果是随机值
	
	printf("%d\n", strlen(arr + 0));//	随机值--arr+0是数组首元素的地址,数组中没有\0,就会导致数组越界访问,所以结果是随机值
	
	//printf("%d\n", strlen(*arr));//	err--arr是首元素的地址,*arr是首元素,就是‘a’,‘a’的ASCII码值是97,
	//相当于将把97作为地址传给strlen函数,strlen函数得到的就是野指针,所以代码是有问题的!
	
	//printf("%d\n", strlen(arr[1]));//	err--arr[1]--‘b’--98,将98传给strlen函数也是错误的!
	
	printf("%d\n", strlen(&arr));//	随机值--&arr是取出数组的地址,起始位置是数组第一个元素的位置,所以是随机值x
	
	printf("%d\n", strlen(&arr + 1));//	随机值--同理,但随机值是x-6
	
	printf("%d\n", strlen(&arr[0] + 1));//	随机值--是从第二个元素开始向后统计,得到的也是随机值 x-1
	return 0;
}

2-2-3 代码3(sizeof操作符)----结尾有具体 \0字符

#include<stdio.h>

int main()

{

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
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 arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//	7--arr是数组名,单独放在sizeof内部,计算的是整个数组的大小--7个字节
	
	printf("%d\n", sizeof(arr + 0));//	4/8--arr表示首元素的地址,arr+0还是表示数组首元素的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(*arr));//	1--arr是数组首元素的地址,*arr是首元素,大小是1个字节
	
	printf("%d\n", sizeof(arr[1]));//	1--arr[1]是数组第二个元素,大小是1个字节
	
	printf("%d\n", sizeof(&arr));//	4/8--&arr是取出整个数组的地址,数组的地址也是地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&arr + 1));//	4/8--&arr+1是跳过整个数组后的那个位置的地址,
	//(&arr是取出整个数组的地址,+1操作跳过整个数组,还是地址)即指向数组后面的空间,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&arr[0] + 1));//	4/8--&arr[0]+1是第二个元素的地址,是地址的话大小就是4/8个字节
	return 0;
}

2-2-4 代码4(strlen函数)----结尾有具体 \0字符

#include<stdio.h>

int main()

{

char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

return 0;

}

输出结果:

输出结果为什么是这个呢?看完解析相信你就懂了!!

完整代码解析:

#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//	6--计算\0之前字符的个数
	
	printf("%d\n", strlen(arr + 0));//	6--arr是首元素的地址,arr+0还是首元素的地址,向后在\0之前有6个字符
	
	//printf("%d\n", strlen(*arr));//	err--‘a’-97
	
	//printf("%d\n", strlen(arr[1]));//	err--‘b’-98
	
	printf("%d\n", strlen(&arr));//	6--&arr是取出整个数组的地址,也是从数组的第一个元素开始向后找\0,
	//计算\0之前的字符个数
	//&arr--char(*)[7]
	//size_t strlen(const char *s)
	
	printf("%d\n", strlen(&arr + 1));//	随机值--取出的是指针变量的地址,所以结果是随机值
	
	printf("%d\n", strlen(&arr[0] + 1));//	5--从数组的第二个元素开始向后找\0,计算\0之前的字符个数
	return 0;
}

2-2-5 代码5(sizeof操作符)----字符指针变量的应用

#include<stdio.h>

int main()

{

char *p = "abcdef";
printf("%d\n", sizeof(p));//p是指针变量,计算的是指针变量的大小,4/8个字节
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;

}

输出结果:

输出结果为什么是这个呢?看完解析相信你就懂了!!

完整代码解析:

#include<stdio.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));//	4/8--p是指针变量,计算的是指针变量的大小,即计算地址的大小,4/8个字节
	
	printf("%d\n", sizeof(p + 1));//	4/8--p+1是字符'b'的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(*p));//	1--p的类型是const char *,*p就是char类型,大小就是1个字节
	
	printf("%d\n", sizeof(p[0]));//	理解1、p[0]->*(p+0)->*p=='a',大小是1个字节
 	//理解2、把常量字符串想象成数组,p可以理解为数组名,那么p[0]就是首元素,大小是1个字节
	
	printf("%d\n", sizeof(&p));//	4/8--取出p的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&p + 1));//	4/8--&p是取出p的地址,+1是跳过p指针变量后的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(&p[0] + 1));//	4/8--&p[0]是取出字符串首字符的地址,+1操作是取出第二个字符的地址,
	//是地址的话大小就是4/8个字节
	return 0;
}

2-2-6 代码6(strlen函数)----字符指针变量的应用

#include<stdio.h>

int main()

{

char *p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

return 0;

}

输出结果为什么是这个呢?看完解析相信你就懂了!!

输出结果:

完整代码解析:

#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));//	6--指针p指向首字符的地址,是计算\0之前字符的个数
	
	printf("%d\n", strlen(p + 1));//	5--p+1是第二个字符的地址,是计算\0之前字符的个数
	
	//printf("%d\n", strlen(*p));//err--‘a’-97
	
	//printf("%d\n", strlen(p[0]));//err--p[0]==*(p+0)==*p。‘a’-97
	
	printf("%d\n", strlen(&p));//	随机值--&p是指针变量p的地址和字符串的地址关系不大
	//从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道。所以结果是随机值
	
	printf("%d\n", strlen(&p + 1));//	随机值--取出的是指针变量的地址,所以结果是随机值
	
	printf("%d\n", strlen(&p[0] + 1));//	5--取出字符串首字符的地址,+1含义是第二个字符的地址
	return 0;
}

2-2-7 小结时刻

  1. 可以把常量字符串想象成数组
  2. p可以理解为数组名,那么p[0]就是首元素
  3. p[0]==*(p+0)==*p
  4. &p是指针变量p的地址和字符串的地址关系不大,从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道!
  5. 将字符对应的ASCII码值传给strlen函数,函数会将其当成地址,但找不到所以会报错!
  6. 对于sizeof(p[0])可以这样理解,理解1、p[0]->*(p+0)->*p=='a',大小是1个字节,理解2、把常量字符串想象成数组,p可以理解为数组名,那么p[0]就是首元素,大小是1个字节
  7. &p是取出p的地址,+1是跳过p指针变量后的地址
  8. &p[0]是取出字符串首字符的地址,+1操作是取出第二个字符的地址,是地址的话大小就是4/8个字节

2-3 二维数组

代码如下:

#include<stdio.h>

int main()

{

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]+) ) );
printf("%d\n",sizeof( *a) );
printf("%d\n",sizeof (a[3])  );

rerurn 0;

}

输出结果:

再次强调一下,数组名的意义

1、数组名是数组首元素(第一个元素)的地址;
 但有两个例外
 (1)sizeof(数组名)--数组名表示整个数组,计算的是整个数组的大小!单位是字节。数组名必须单独放在sizeof内部才是这种情况,否则不是!
 (2)&数组名--数组名表示整个数组,取出的是整个数组的地址!——无需只存在&与数组名
 除此之外,数组名就是数组首元素(第一个元素)的地址!!

小伙伴们肯定会不理解为什么,输出结果是这个呢?那康康下面的解析把下,我相信看完后你一定有所收获!!

完整代码解析:

//二维数组
#include<stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//	48--a是数组名,单独放在sizeof内部,计算的是数组的大小,单位是字节--48=3*4*sizeof(int)
	
	printf("%d\n", sizeof(a[0][0]));//	4--a[0][0]是第一行第一个元素,大小是4个字节
	
	printf("%d\n", sizeof(a[0]));//	16--a[0]是第一行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小--16个字节
	
	printf("%d\n", sizeof(a[0] + 1));//	4/8--a[0]是第一行的数组名,但a[0]没有单独放在sizeof内部,所以这里的数组名a[0]就是
	//数组首元素的地址,就是&a[0][0],+1后就是a[0][1]的地址,大小是4/8个字节
	
	printf("%d\n", sizeof(*(a[0] + 1)));//4--* ( a[0] + 1) 表示第一行第二个元素(访问的是第二行第一个元素),大小是4个字节	
	
	printf("%d\n", sizeof(a + 1));//	4/8--a作为数组名没有单独放在sizeof内部,所以这里的数组名a表示数组首元素的地址,
	//是二维数组首元素的地址,就是第一行的地址,a+1含义跳过一行,指向第二行,a+1是第二行的地址,a+1是数组指针,
	// 是地址的话大小就是4/8个字节!
	
	printf("%d\n", sizeof(*(a + 1)));//	16--理解1、a+1是第二行的地址,*(a+1)是第二行,计算的是第二行的大小
	//理解2、*(a+1)==a[1],a[1]是第二行的数组名,sizeof(*(a+1))==sizeof(a[1]),意思是把第二行的数组名单独放在sizeof内部,
	//计算第二行的大小
	
	printf("%d\n", sizeof(&a[0] + 1));//	4/8--a[0]是第一行的数组名,&a[0]取出的就是这个数组的地址,
	//就是第一行的地址,&a[0]+1就是第二行的地址,是地址的话大小就是4/8个字节
	
	printf("%d\n", sizeof(*(&a[0] + 1)));//	16--*(&a[0] + 1)意思是对第二行的地址进行解引用,访问的是第二行,大小是16个字节
	
	printf("%d\n", sizeof(*a));	//16--a作为数组名没有单独放在sizeof内部,所以这里的数组名a表示数组首元素的地址,
	//是二维数组首元素的地址,就是第一行的地址,*a就是第一行,计算的是第一行所有元素的大小--4*4=16
	//*a==*(a+0)==a[0]
	
	printf("%d\n", sizeof(a[3]));	//16--a[3]无需真实存在,仅仅通过类型的推断就能算出长度,
	//a[3]是第四行的数组名,单独放在sizeof内部,计算的是第四行的大小--16个字节
	return 0;
}

2-3-1 小结时刻

  1. 数组名是首元素的地址,但有两个例外( sizeof(数组名)和&数组名)。具体内容请看前文!
  2. 对于a[0]可以这样理解,a[0]是第一行的数组名,数组名单独放在sizeof内部了,计算的是数组的总大小
  3. 对于sizeof(a[3])的理解要特别注意,a[3]无需真实存在,仅仅通过类型的推断就能算出长度,
  4. a[3]是第四行的数组名,单独放在sizeof内部,计算的是第四行的大小
  5. 对于sizeof(*(a+1))可以这样理解:理解1、a+1是第二行的地址,*(a+1)是第二行,计算的是第二行的大小     理解2、*(a+1)==a[1],a[1]是第二行的数组名,sizeof(*(a+1))==sizeof(a[1]),意思是把第二行的数组名单独放在sizeof内部,计算第二行的大小
  6. 对于sizeof(a+1)可以这样理解:a作为数组名没有单独放在sizeof内部,所以这里的数组名a表示数组首元素的地址,是二维数组首元素的地址,就是第一行的地址,a+1含义跳过一行,指向第二行,a+1是第二行的地址,a+1是数组指针,是地址的话大小就是4/8个字节!
  7. *a==*(a+0)==a[0]


3、指针运算经典笔试题

3-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;
}
//输出结果是什么呢?
输出结果:
输出结果为什么是这个呢?看完分析,相信你就懂了!!
分析与图解:&a---a的类型是int(*)[5],&a+1跳过的是整个数组!

3-2 试题二----指针+-整数

//假设该结构大小是20个字节
// 环境是:X86(32位)
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
//输出结果是什么呢?
输出结果:
输出结果为什么是这个呢?看完分析,相信你就懂了!!
分析与图解:
我们知道整型指针加1,跳过的是一个整型,那么结构体指针+1,跳过的就是一个结构体,
并且不要忘记了整型值加1,就是+1!!
完整代码解析:

3-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]);
return 0;
}
//程序的结果是什么呢?
输出结果:
输出结果为什么是这个呢?看完分析,相信你就懂了!!
分析与图解:
a[0]是第一行的数组名,数组名表示数组首元素的地址,其实就是&a[0][0]的地址。并且逗号表达式的最终结果是最后一个表达式的结果。
完整代码解析:
#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//初始化,逗号表达式
	int* p;//1 3	5 0		0 0
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

3-4 试题四----指针-指针

#include <stdio.h>
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;
}
//假设环境是x86环境,输出结果是什么?
输出结果:
输出结果为什么是这个呢?看完分析,相信你就懂了!!
分析与图解:
题目的关键之处是a[4][2]与p[4][2]的位置!对于a[4][2]的位置可以把二维数组想象成一维数组的排列,那么每一行就是一个一维数组例如a[1],a[2],
再来看一下a的类型:int(*)[5],但p的类型是int(*)[4]两者类型不同,p指向的数组存放了4个整型数据。但根据数组在内存中是连续存放的,以及p的类型可以推算出p[4][2]的位置,再根据指针-指针与数组的元素下标是由低到高的,地址也是由低地址到高地址的知识可以得到最终结果!
完整代码解析:
#include <stdio.h>
int main()
{
	int a[5][5];
	int(*p)[4];//p是数组指针,p指向的数组是4个整型元素
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}
// 指针-指针的绝对值==指针与指针之间的元素个数!

3-5 试题五----指针+-整数

#include <stdio.h>
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;
}
//输出的是什么呢?

输出结果:

输出结果为什么是这个呢?看完分析,相信你就懂了!!

分析与图解:

这里的*(aa+1)==aa[1] !

3-6 试题六----指针在二维数组的应用

#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
//输出结果是什么?
输出结果:
输出结果为什么是这个呢?看完分析,相信你就懂了!!
分析:可以将其理解为a数组存放了字符’w'的地址,字符’a‘的地址和字符’a'的地址,那么二级指针a指向的是字符’w',+1操作指的就是字符’a',可以得到相关的地址!%s是打印字符串,给一个地址,从这个地址向后打印字符串,直到 \0,
完整代码解析:
#include <stdio.h>
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			5
}
//1, 2, 3, 4, 5,	6, 7, 8, 9, 10

3-7 试题七----指针在二维数组的应用

#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;
}//你知道输出结果吗?

输出结果:

输出结果为什么是这个呢?看完分析,相信你就懂了!!

分析与图解:

* * ++cpp,首先根据操作符的优先级,将cpp指向的位置+1,然后再访问相关的空间。即流程是cpp+1找到空间并访问,空间里存放c+2,然后再根据c+2找到存放point的空间

*-- * ++cpp +3,首先根据操作符的优先级,先将cpp在上一次的位置+1,然后再访问空间,空间里存放数值c+1,再将c+1变为c,再根据c访问相关的空间,可知空间里存放了字符'E'的地址,再+3指向的就是字符’e‘,即打印er

*cpp[-2] + 3,首先根据操作符的优先级,先将cpp在上一次的位置-2,然后再访问空间,空间里存放数值c+3,再根据c+3,访问相关的空间,可知空间里存放了字符‘F’的地址,再将其+3,那么就指向了字符‘s'!!

cpp[-1][-1]+1==*(*(cpp-1)-1)+1,首先根据操作符的优先级,先将cpp在上一次的位置-1,然后再访问空间,空间里存放数值c+2,再将c+2改变为c+1,再根据c+1,访问相关的空间,可知空间里存放了字符'N'的地址,再+1指向的就是字符’e‘,即打印ew

老铁们制作不易,一键三连吧!!!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值