【C进阶】—— 指针相关试题

楔子

在进入试题前,我们先了解相关重点

  1. 对于一个数组例如int arr[5] = {1,2,3,4,5},只有在 &arrsizeof(arr) (即sizeof内部单独使用时)二者中的数组名才代表整个数组。
  2. 指针加减整数实际上是加减其所指向类型的大小。
  3. 在32位平台下指针大小为4个字节,64位为8个字节(VS 2019环境下测试)
  4. strlen()函数它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)。
  5. sizeof() 返回一个对象或者类型所占的内存字节数。
    注:默认为32机器

一维数组

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//sizeof内部单独使用,即代表整个数组,每个元素类型为int,一共有4个即结果为4*4=16
printf("%d\n",sizeof(a+0));
//未单独使用,代表首元素地址,类型为int*,即4字节
printf("%d\n",sizeof(*a));
//对数组名解引用,代表的是第一个元素,类型为int,即4字节
printf("%d\n",sizeof(a+1));
//a是首元素地址,类型为int*,加1实际上是加上其类型大小4,然后指向第二个元素,地址大小恒为4字节
printf("%d\n",sizeof(a[1]));
//a[1]即第二个元素2,类型为int,大小为4
printf("%d\n",sizeof(&a));
//&a代表整个数组的地址,类型为int(*)[]的数组指针,但它还是指针,大小为4
printf("%d\n",sizeof(*&a));
//*和&相互抵消,相当于sizeof(a),同上16
printf("%d\n",sizeof(&a+1));
//&a同上,加1跳过整个数组,但还是指针,大小为4
printf("%d\n",sizeof(&a[0]));
//取a[0]的地址,类型为int*,大小为4
printf("%d\n",sizeof(&a[0]+1));
//地址加1,跳过一个元素,指向a[1],类型为int*,为4字节

字符数组

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//arr代表整个数组,类型为char,一个有6个元素,大小为6字节
printf("%d\n", sizeof(arr+0));
//未单独使用数组名,即代表首元素地址,类型为char*,大小为4
printf("%d\n", sizeof(*arr));
//对首元素地址解引用得到的是第一个元素,类型为char,大小为1
printf("%d\n", sizeof(arr[1]));
//访问的是数组中的第二个元素,类型为char,大小为1
printf("%d\n", sizeof(&arr));
//&arr的类型为char(*)[6]的数组指针,指针大小为4
printf("%d\n", sizeof(&arr+1));
//同上,跳过整个字符数组,但还是指针大小为4
printf("%d\n", sizeof(&arr[0]+1));
//&arr[0]是首元素地址,类型为char*,加1后指向第二个元素,大小仍为4

printf("%d\n", strlen(arr));
//由于arr中没有字符串结束标志'\0',所以答案为随机值
printf("%d\n", strlen(arr+0));
//arr为单独使用为首元素地址,但原因同上,不知道'\0'在哪里,所以为随机值
printf("%d\n", strlen(*arr));
//对首元素地址解引用即*arr == 'a',但strlen中接受的是一个地址,所以在这里错误的将a的ASCII码当为地址,直接去访问0xx0000061处的地址,结果发生非法访问内存的错误,所以这个表达式是error的
printf("%d\n", strlen(arr[1]));
//arr[1]是字符b,原因同上访问内存编号为98的地址,error
printf("%d\n", strlen(&arr));
//虽然&arr是整个数组的地址,但是它存储的还是首元素地址,结果与strlen(arr)相同为随机值
printf("%d\n", strlen(&arr+1));
//&arr+1跳过整个数组,但是我们不知道’\0'在何处,所以结果为随机值
printf("%d\n", strlen(&arr[0]+1));
//&arr[0] + 1指向第二个元素,原因同上,随机值
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
//'\0'为字符串的一部分,所以计算大小仍要计算为7
printf("%d\n", sizeof(arr+0));
//arr未单独使用那么他就是首元素地址,类型为char*,大小为4
printf("%d\n", sizeof(*arr));
//*arr == *(arr + 0) == a[0],所以为1
printf("%d\n", sizeof(arr[1]));
//第二个元素,类型为char,大小为1
printf("%d\n", sizeof(&arr));
//取出整个数组的地址,类型为char(*)[],所以为4
printf("%d\n", sizeof(&arr+1));
//跳过整个数组,大小为4
printf("%d\n", sizeof(&arr[0]+1));
//&arr[0]代表第一个元素的地址,类型为char*,加1,指向第二个元素,大小为4

printf("%d\n", strlen(arr));
//一共有abcdef6个字符,即长度为6
printf("%d\n", strlen(arr+0));
//同上为6
printf("%d\n", strlen(*arr));
//*arr == ’a‘,访问97处的内存,非法访问,error
printf("%d\n", strlen(arr[1]));
//同上,error,只不过访问的是98处的地址
printf("%d\n", strlen(&arr));
//&arr是首元素地址,同strlen(arr),长度为6
printf("%d\n", strlen(&arr+1));
//不知道跳过整个数组后向后访问时’\0‘,在那,所以为随机值
printf("%d\n", strlen(&arr[0]+1));
//指向b,然后向后读取直到'\0'停止,长度为5
char *p = "abcdef";
printf("%d\n", sizeof(p));
//p中存放的时字符串首元素的地址,所以大小为4
printf("%d\n", sizeof(p+1));
//加1指向b,但还是指针大小仍为4
printf("%d\n", sizeof(*p));
//由于p的类型为char*,解引用后只能访问一个字节,所以大小为1
printf("%d\n", sizeof(p[0]));
//p[0] == *(p + 0) == *p,同上为1字节
printf("%d\n", sizeof(&p));
//p为一个指针变量,&相当于是一个二级指针,即char**,大小为4
printf("%d\n", sizeof(&p+1));
//&p类型为char**,加1指向p指针变量后一个地址,大小为4
printf("%d\n", sizeof(&p[0]+1));
//&p[0]为a的地址,加1后指向b,大小为4

printf("%d\n", strlen(p));
//p中存放的是首元素地址,所以为6
printf("%d\n", strlen(p+1));
//(p + 1) == *(p + 1) ==p[1],所以其为5
printf("%d\n", strlen(*p));
//*p为是字符’a‘,即访问97处的地址,error
printf("%d\n", strlen(p[0]));
//p[0] == *(p + 0) == *p,同上,error
printf("%d\n", strlen(&p));
//&p中的是p的地址,由于不知道’\0‘在何处,所以为随机值
printf("%d\n", strlen(&p+1));
//同上,只不过这次跳过p地址,到它后面开始访问
printf("%d\n", strlen(&p[0]+1))
//从第二个字符向后访问,长度为5

二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));
//单独使用,代表整个数组,大小为12*4 = 48
printf("%d\n",sizeof(a[0][0]));
//第一行第一列元素,类型为int,大小为4
printf("%d\n",sizeof(a[0]));
//将二维数组看为3个一维数组,每个一维数组有4个元素,所以这里的a[0]指的就是第一行的地址,类型为int(*)[4],所以大小为4*4 = 16
printf("%d\n",sizeof(a[0]+1));
//a[0]是第一行的地址,但是此时它并没有单独使用,所以它的类型不是int(*)[4],而是int*,加1指向第一行第二个元素的地址,大小为4
printf("%d\n",sizeof(*(a[0]+1)));
//接上一题,对该地址解引用就是第一行第二个元素,类型为int,大小为4
printf("%d\n",sizeof(a+1));
//a没有单独使用,所以它代表是首元素地址,那么二维数组首元素是一个一维数组,即指向第一行,类型为int(*)[4],加一指向第二行,大小为4
printf("%d\n",sizeof(*(a+1)));
//接上一题,解引用是整个第二行,所以大小为4*4 = 16
printf("%d\n",sizeof(&a[0]+1));
//&a[0]等价于第一行地址,加一后指向第二行,指针大小为4
printf("%d\n",sizeof(*(&a[0]+1)));
//第二行解引用,是16字节
printf("%d\n",sizeof(*a));
//第一行解引用,类型为int(*)[4],所以大小为16字节
printf("%d\n",sizeof(a[3]));
//访问第4行,大小为16

笔试题

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

在这里插入图片描述

struct Test
{
	int Num;
	char *pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	p = (struct Test*)0x100000;
	printf("%p\n", p + 0x1);
	//由于p的值为0x100000加上一个16进制的数字1,加上其类型的大小即加20,结果为0x100014
	printf("%p\n", (unsigned long)p + 0x1);
	//强制转化为unsigned long,相当于数学中的加法,直接为0x100001
	printf("%p\n", (unsigned int*)p + 0x1);
	//p类型为int*,加1跳过4个整形,即为0x100004;
	return 0;
}

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

在这里插入图片描述

#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//由于{},内部为(),所以它为逗号表达式,所以数组内元素为{1,3,5,0,0,0}
	int *p;
	p = a[0];
	printf( "%d", p[0]);//所以这里的p[0] == *(p) ,所以即第一行的第一个元素1
	return 0;
}

int main()
{
	int a[5][5];
	int(*p)[4];//因为p类型为int(*)[5]所以它加减跳过16个字节
	p = a;
	printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
	return 0;
}

在这里插入图片描述

int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int *ptr1 = (int *)(&aa + 1);//指向10后面
	int *ptr2 = (int *)(*(aa + 1));//指向6
	printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10和5
	return 0;
}

#include <stdio.h>
int main()
{
	char *a[] = {"work","at","alibaba"};
	//首先这是一个数组指针,每个元素类型为char*,
	char**pa = a;
	//数组名的地址的指针,即为二级指针来接受
	pa++;
	//++后指向第二个元素
	printf("%s\n", *pa);//结果为at
	return 0;
}

int main()
{
	char *c[] = {"ENTER","NEW","POINT","FIRST"};
	//c数组为字符指针数组,内部存放的是4个字符的首元素地址
	char**cp[] = {c+3,c+2,c+1,c};
	//cp也为字符指针数组,存放的是字符地址的地址
	char***cpp = cp;//为cp的地址,指向cp数组中的c+3
	printf("%s\n", **++cpp);
	//cpp先++,其指向发生改变,现在指向c+2,然后解引用得到其内容为,POINT
	printf("%s\n", *--*++cpp+3);
	//cpp先++,指向c+1,然后解引用指向c中的第二个元素,然后在--,指向c中第一个,然后解引用找到ENTER,然后+3,因为类型为char,所以现在就指向E,访问得到ER
	printf("%s\n", *cpp[-2]+3);
	//现在的cpp指向的是c+1,*cpp[-2] +3 == **(cpp - 2)+3,现在cpp指向c+3,其内容为FIRST,+3指向S,访问得到ST
	printf("%s\n", cpp[-1][-1]+1);
	//cpp[-1][-1] + 1 == *(*(cpp-1)-1)+1 ,cpp现在指向c+1,-1指向c+2,解引用指向c中第三个元素,然后在-1,指向第二个元素,解引用指向NEW,+1指向E,内容为EW
	return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zzt.opkk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值