有关指针的笔试题

指针和数组笔试题解析
关于一维数组
#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//16,a作为数组名,独立存在与sizeof内部,表示的是整个数组的大小,结果为4*4=16
	printf("%d\n", sizeof(a+0)); //4,此时a并没有单独存在与sizeof内部,所以a此时表示的是首元素的地址
	//a表示首元素的地址,a+0还表示的是首元素的地址,只要是地址,就是4个字节
	printf("%d\n", sizeof(*a)); //4,*a表示的首元素的值,首元素的值为1,类型为int,sizeof(int)=4
	printf("%d\n", sizeof(a+1));//4 a表示首元素的地址,a+1表示的是第二个元素的地址,只要是地址
	//结果就是4
	printf("%d\n", sizeof(a[1]));//4 a[1]表示的是第2个元素的值,值为int类型,所以带大小为4
	printf("%d\n", sizeof(&a));  //4 &a,取出的是整个数组的地址,只要是地址,结果就是4个字节
	printf("%d\n", sizeof(*&a)); // 16 这个需要注意:
	//先对a进行&,去除了整个数组的地址,在对这个地址进行解引用的操作
	//就找到了整个数组,所以大小为16,*&a就相当于a,其实就是sizeof(a),为整个数组的大小
	printf("%d\n", sizeof(&a + 1)); //4 &a+1,跳过了整个数组的大小,然后,还是一个地址,所以结果还是4
	printf("%d\n", sizeof(&a[0])); //4 仍然是一个地址
	printf("%d\n", sizeof(&a[0]+1)); //4 还是地址
}
关于字符数组
#include<stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr)); //arr作为数组名放在sizeof内部,要计算的是整个数组的大小
	//数组中此时并没有放置\0,所以一共有6个元素,每个元素的大小是一个字节
	//所以整个数组的大小为6个字节
	printf("%d\n", sizeof(arr + 0));  //4 此时arr作为首元素的地址,arr+0还是表示首元素的地址
	//只要是地址,大小就是4
	printf("%d\n", sizeof(*arr));     //1  arr作为首元素的地址,进行解引用,然后得到了
	//首元素的值,值为char型的,大小为1个字节
	printf("%d\n", sizeof(arr[1]));   //1 第二个元素的值
	printf("%d\n", sizeof(&arr));     //4  &arr取出的是数组的地址
	printf("%d\n", sizeof(&arr + 1));//4  跳过整个数组,还是一个地址
	printf("%d\n", sizeof(&arr[0] + 1));  //4
}
#include<stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));   //未知数,因为我并没有看到\0,所以不知道\0到底在哪
	//所以值是随机值 X
	printf("%d\n", strlen(arr + 0));  //arr此时作为首元素的地址,首元素地址+0还是首元素的地址
	//所以还是随机值X
	printf("%d\n", strlen(*arr));    //传进去的是字符a,小写a的ASCII码值为97
	//所以为错误代码
	printf("%d\n", strlen(arr[1]));  //传的是B,所以还是错误代码
	printf("%d\n", strlen(&arr));    //X
	printf("%d\n", strlen(&arr + 1));  //Y-X=6
	printf("%d\n", strlen(&arr[0] + 1));  //X+1
}
#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr)); //7 主动放了一个\0,所以为7
	printf("%d\n", sizeof(arr + 0));  //4 首元素地址,所以还是地址
	printf("%d\n", sizeof(*arr));  //1 首元素的值,是char型的,大小为1
	printf("%d\n", sizeof(arr[1])); //1 
	printf("%d\n", sizeof(&arr)); // 4 取出整个数组的地址
	printf("%d\n", sizeof(&arr + 1)); //4 跳过整个数组
	printf("%d\n", sizeof(&arr[0] + 1)); //4 
}
#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));   //6
	printf("%d\n", strlen(arr + 0));  //和上面第一个一样往后数,一共有6个
	printf("%d\n", strlen(*arr));     //error ,错误的传参方式
	printf("%d\n", strlen(arr[1]));   //error ,错误的传参方式
	printf("%d\n", strlen(&arr));     //取出整个数组的地址,往后数,还是6
	printf("%d\n", strlen(&arr + 1)); //随机值,跳过整个数组,已经跳到\0的后面了
	//所以结果是多少是一个未知数
	printf("%d\n", strlen(&arr[0] + 1));  //5
}
#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));  //4 p为指针变量,大小为4
	printf("%d\n", sizeof(p + 1)); //4 p是指针变量,+1的大小还是地址,结果为3
	printf("%d\n", sizeof(*p));//1 ,是一个值,值得类型是char类型,所以大小为1
	printf("%d\n", sizeof(p[0])); //1 同上
	printf("%d\n", sizeof(&p));   // 4 取出整个数组的地址 
	printf("%d\n", sizeof(&p + 1));   //4  跳过整个数组,还是地址
	printf("%d\n", sizeof(&p[0] + 1));  //4,还是地址
}
#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));   //6 p为a的地址,看a后面有几个数字
	printf("%d\n", strlen(p + 1));  //5
	printf("%d\n", strlen(*p));  //error
	printf("%d\n", strlen(p[0]));  //error
	printf("%d\n", strlen(&p));    //未知数 &p和p中存的内容没有什么直接的关系
	printf("%d\n", strlen(&p + 1)); //未知数
	printf("%d\n", strlen(&p[0] + 1)); //5 取出了b的地址,结果为5 
}
二维数组
#include<stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));  //48 整个数组,大小为 48
	printf("%d\n", sizeof(a[0][0]));  //4 第一个元素的大小
	printf("%d\n", sizeof(a[0]));     //16 第一行的数组名,单独放在内部,
	//表示第一行的大小
	printf("%d\n", sizeof(a[0] + 1));  //4 a[0]变成首元素的地址,所以大小还是4
	printf("%d\n", sizeof(*(a[0] + 1)));  //4 是指的大小,值的类型为int
	printf("%d\n", sizeof(a + 1));  //4 a是二维数组的数组名,a为首元素的地址,首元素的地址为第一行的地址
	//第一行的地址加1,是第二行的地址 ,只要是地址,大小就是4 
	printf("%d\n", sizeof(*(a + 1))); //16 对第二行的地址进行解引用的操作,找到了第二行的所有
	//所以大小为16
	printf("%d\n", sizeof(&a[0] + 1)); //4 还是地址 取出的是第二行的地址
	printf("%d\n", sizeof(*(&a[0] + 1)));  //16 找出了第二行的所有元素
	printf("%d\n", sizeof(*a));  //16 第一行解引用
	printf("%d\n", sizeof(a[3]));  //16 //虽然越界了,但是并不影响,结果还是一行的大小
}
sizeof内部的表达式不参与运算,也就是说越界了,也没有任何的影响
总结:数组名的意义:
  • sizeof(数组名)这里的数组名表示的是整个数组,计算的是整个数组的大小
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
  • 除此之外所有的数组名都表示首元素的地址
指针笔试题
#include<stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);  //加1跳过了整个数组,强制转换成了int* 
	printf("%d,%d", *(a + 1), *(ptr - 1));  //-1向前挪动一个,指向5的地址,解引用之后
	//得到的就是5的值
	return 0;
	//结果为2 5
}
struct Test
{
	int Num;
	char *pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//结构体大小为20个字节
int main()
{
	p = (struct Test*)0x100000;
	//这道题主要考察的点在于指针+1的权限到底有多大
	//指针的权限是和指针的类型密切相关的
	printf("%p\n", p + 0x1);
	//p作为一个指针变量,p的大小是20个字节,那么对于此处来说,p+1,它所能够访问的权限就是20个字节
	//所以p+1,加的就是20个字节,0x是16进制,20在16进制下就是14,又因为p为0x100000,所以它+14的结果是
	//0x100014
	printf("%p\n", (unsigned long)p + 0x1);
	//此处p被强制类型转换成了整形,所以p+1在此处就是p+1进行整数运算,所以结果为
	//0x100001
	printf("%p\n", (unsigned int*)p + 0x1);
	//此处p被强制类型转换成了指针类型,整形指针+1所拥有的权限是4个字节,那么,p+1的结果是
	//0x100014
	return 0;
}
#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.

#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;
	//打印的结果是1,考察的点是逗号表达式,利用逗号表达式对数组进行初始化
}
#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;
}

解析:

蓝色的为a[4][2],红色的为p[4][2]
在这里插入图片描述
6.

#include<stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int *ptr1 = (int *)(&aa + 1);
	//&数组名,代表的是整个数组,整个数组加1,位置来到了10的后面
	//然后再-1,位置来到了10,然后再解引用就得到10位置处的值
	//所以结果就为10
	int *ptr2 = (int *)(*(aa + 1));
	//首元素的地址加1,位置来到了第二行,然后进行解引用,相当于拿到了第二行
	//也就是拿到了6的地址,然后-1,到了5的地址处,然后解引用
	//得到了5的值,所以结果为5
	printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
	//结果为 10 和 5 
}
#include<stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char**pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
	//结果为at
	//a为指针数组
	//pa+1,跳过一个char*的大小
}

在这里插入图片描述
8.

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

在这里插入图片描述

#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得到了整个数组的地址,然后+1,跳过了整个数组,位置来到了5的后面,然后(int *)将其强制转换成整形指针的类型,只是将类型转换了,值并没有发生任何的变化,再将其赋给ptr,ptr是一个整形的指针,所以 *(ptr-1)就是将ptr想左边挪动四个字节(int *类型)的大小,来到5的地址,然后再解引用,就得到了5的值,所以输出的第二个值为5.
*(a+1)这里的a没有任何特殊的意义,就表示的是首元素的地址,a+1来到了第二个元素2的地址,然后再进行解引用的操作,就得到了2的值。

#include<stdio.h>
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//结构体的大小为20
//假设p的值为0x100000
int main()
{
	p=struct Test*0x100000
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

考点:指针加一
分析:首先,这个结构体的大小是20个字节,p为结构体的指针类型,所以进行一下强制类型转换,0x1的大小的值就为1。
所以打印的第一个 打印的是p+1的结果,类型又是结构体类型,那么就直接进行加法,又因为结构体的大小为20个字节,20用16进制表示的结果就是14,所以p+1的结果为0x100014
第二个打印,把p的类型强制转换为unsigned long类型,long是长整形,所以也直接进行加法就可以了,p+1的值也就为0x100001
第三个打印,把p的类型强制转换成unsigned int 类型 ,int的类型的大小为4个字节,所以p+1的值就为0x100004

#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和2000000
分析:首先&a取到的是整个数组的地址,整个数组的地址+1,就是跳过了整个数组,来到了4的后面,再把它强制转换成(int )类型,只是改变了类型,但是他的值并没有发生任何的改变,再把它赋给ptr1,ptr1[-1]就相当于*(ptr-1),那么就来到了4的地址出,再去解引用,就得到了4的值,所以第一个输出的值是4
在这里插入图片描述

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

这个题输出的结果为1;
考点:逗号表达式
分析:这个题的陷阱就在于,他对于数组的初始化,并不是用的花括号,而是用的圆括号,这就涉及到了逗号表达式的应用 ,所以初始化结束之后的数组应该为 1,3,5,0,0,0 ; a[0]得到的就是数组第一行的元素,将其赋值给p,打印p[0],那么就打印的是第一个元素1

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

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

#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
分析:&aa取到的是整个数组的地址,它加1跳过了整个数组,来到了10 的后面,与上面的题一个道理,只是改变了类型,值并没有发生实质性的变化,然后将其减1再解引用,就得到了10 的值,所以输出10
第二个aa没有取地址,就得到数组首元素的地址,也就是得到了数组第一行的地址,然后加一,就得到了数组第二行的地址,在进行解引用,就得到了第二行的元素,再将其强制类型转换成int *类型,赋值给ptr2,然后ptr2-1,再解引用,就是6-1,得到了5.
所以输出的结果是10和5。

#include<stdio.h>
int main()
{
	const char* a[] = { "work","at","alibaba" };
	char** pa = &a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

这道题输出的结果是at
分析:a为指针数组,是把字符出首地址放进去了,字符串中由三个,分别是work at alibaba ,把每个字符串的首地址存放起来,放在数组中,起名字称为a,char *中分别存放的是w,a,a的地址,pa中放置的是char *的地址,pa指向的是char * 加以,所以打印的是at

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值