指针笔试题

数组名的考察(选填)

一维数组

数组名是数组首元素的地址
这里有2个例外:

  1. sizeof(数组名),这里的数组名是表示整个数组的,计算的是整个数组的大小,单位是字节。
  2. &数组名,这里的数组名也表示整个数组,取出的是数组的地址。

除上面2中特殊情况外,所有的数组名都是数组首元素的地址

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

sizeof(a) ,数组名a单独放在sizeof内部,计算的整个数组的大小,单位是字节,4*4 = 16。

sizeof(a + 0), a表示的首元素的地址,a+0还是数组首元素的地址,是地址大小4/8。

sizeof(* a) a表示的首元素的地址,※a就是对首元素的地址的解引用,就是首元素,大小是4个字节

sizeof(a + 1)),a表示的首元素的地址,a+1是第二个元素的地址,是地址,大小就4/8个字节。

sizeof(a[1])) ,a[1]是数组的第二个元素,大小是4个字节。

sizeof(&a)),&a 表示是数组的地址,数组的地址也是地址,地址大小就是4/8字节。

sizeof(*&a)),可以理解为※和&抵消效果,※&a相当于a,sizeof(a)是16。

sizeof(&a + 1)) , &a是数组的地址,&a+1 跳过整个数组后的地址,是地址就是4/8

sizeof(&a[0])) , &a[0]取出数组第一个元素的地址,是地址就是4/8

sizeof(&a[0] + 1)), &a[0]是int*的类型,+1就跳过四个字节,是第二个元素的地址,是地址大小就是4/8个字节

字符串数组

sizeof只关注占用空间的大小,单位是字节。
sizeof不关注类型。
sizeof是操作符。

strlen关注的字符串中\0的为止,计算的是\0之前出现了多少个字符。
strlen指针对字符串。
strlen是库函数。

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

sizeof(arr), arr作为数组名单独放在sizeof内部,计算的整个数组的大小,结果为6字节。

sizeof(arr + 0),arr就是首元素的地址,arr+0还是首元素的地址,地址大小就是4/8。

sizeof(*arr), arr就是首元素的地址,*arr就是首元素,是一个字符,大小是一个字节,1。

sizeof(arr[1]),arr[1]就是数组的第二个元素,是一个字符,大小是1个字节。

sizeof(&arr),&arr取出的是数组的地址,数组的地址也是地址,地址就是4/8个字节。

sizeof(&arr + 1),&arr取出的是数组的地址,&arr+1,跳过了整个数组,&arr+1还是地址,地址就是4/8个字节。

sizeof(&arr[0] + 1),&arr[0]是第一个元素的地址,&arr[0]+1就是第二个元素的地址,地址就是4/8个字节。

char arr[] = “abcdef”;后面其实放了一个隐藏的’\0’。

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

sizeof(arr) ,后面其实放了一个隐藏的’\0’,所以结果为7。

sizeof(arr + 0), arr表示首元素的地址,+0还是首元素地址,因为是地址,所以结果为4 or 8 。

sizeof(*arr) , arr是首元素的地址,解引用得到元素’a’,计算元素的大小为1。

sizeof(arr[1]) , arr[1]访问第二个元素’b’,计算元素的大小为1。

sizeof(&arr), &arr表示的是首元素的地址,因为是地址,所以结果为4 or 8 。

sizeof(&arr + 1) &arr表示的是首元素的地址,+1跳过整个数组,因为是地址,所以结果为4 or 8 。

sizeof(&arr[0] + 1), arr[0]访问第一个元素,&表示取地址,&arr[0]表示第一个元素的地址,+1表示跳过一个元素后的地址,因为是地址,所以结果为4 or 8 。

strlen
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));//随机+报警,&arr是arr数组的地址,虽然和strlen的参数类型有些差异,
	//但是传参之后还是从第一个字符位置向后数字符,结果还是随机值
	printf("%d\n", strlen(&arr+1));//随机值
	printf("%d\n", strlen(&arr[0]+1));//随机值
	printf("%d\n", strlen(&arr[1]));//随机值
	return 0;
}

strlen(arr),arr是首元素的地址,但是arr数组中没有\0,计算的时候就不知道什么时候停止,结果是:随机值(x)。

strlen(arr+0),arr是首元素的地址,arr+0还是首元素的地址,结果是:随机值(x)。

strlen(*arr),访问第一个元素’a’, ASCII码为97,那么就从地址为97的位置向后访问,但是这个访问是非法的,所以会报错!

strlen(arr[1]), 和上一个一样,内存访问冲突。

strlen(&arr), &arr是arr数组的地址(可以理解为指向数组的指针),虽然和strlen的参数类型有些差异,但是传参之后还是从第一个字符位置向后数字符,结果还是随机值(x)。

strlen(&arr+1) ,&arr是指向数组的指针,+1就会跳过整个数组,数组的长度为6,那么向后访问’\0’的时候,得到的结果任然是随机值,只不过这个随机值为(x-6)。

strlen(&arr[0]+1) ,arr[[0]表示第1个元素,&arr[0]表示取第一个元素的地址,由于arr是char类型,所以+1向后移动1bit,相当于从第二个元素的地址往后访问’\0’,结果为随机值(x-1)。

strlen(&arr[1]),参考上一条。 相当于从第二个元素的地址往后访问’\0’,结果为随机值(x-1)。

char arr[] = “abcdef”;后面其实放了一个隐藏的’\0’。

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

strlen(arr),后面其实放了一个隐藏的’\0’,从前往后数到’\0’,数了6位,所以结果为6。

strlen(arr + 0), arr表示首元素的地址,+0还是首元素地址,从前往后数到’\0’,数了6位,结果为6。

strlen(*arr), 访问第一个元素’a’, ASCII码为97,那么就从地址为97的位置向后访问,但是这个访问是非法的,所以会报错!

strlen(arr[1]),和上一个一样,内存访问冲突。

strlen(&arr), &arr是arr数组的地址(可以理解为指向数组的指针),虽然和strlen的参数类型有些差异,但是传参之后还是从前往后数到’\0’,数了6位,结果为6。

strlen(&arr + 1),&arr是指向数组的指针,+1就会跳过整个数组,从前往后数到’\0’,结果为 随机值。

strlen(&arr[0] + 1),arr[[0]表示第1个元素,&arr[0]表示取第一个元素的地址,由于arr是char类型,所以+1向后移动1bit,从前往后数到’\0’,数了5位,结果为5。

字符指针

sizeof
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));//计算的指针变量的大小,4or8
	printf("%d\n", sizeof(p + 1));//任然是一个地址,4 or 8
	printf("%d\n", sizeof(*p));//指针解引用只访问一个字节,所以字节的大小是1
	printf("%d\n", sizeof(p[0]));//*(p+0),一个字节
	printf("%d\n", sizeof(&p));//&p也是地址,是二级指针,是指针就是4 or 8
	printf("%d\n", sizeof(&p + 1));//4 or 8,是p的地址+1,在内存中
	printf("%d\n", sizeof(&p[0] + 1));//p[0]就是a,&p[0]+1就是b的地址,4or8字节
	return 0;
}
strlen
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));//p中存放的是a的地址,strlen(p)就是从a的位置向后求字符串的长度,长度是6
	printf("%d\n", strlen(p + 1));//p+1是b的地址,求长度的话就是5
	//printf("%d\n", strlen(*p));erro,非法访问
	//printf("%d\n", strlen(p[0]));erro,非法访问
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));//和p+1是一个效果,5

	return 0;
}

二维数组

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

sizeof(a),数组名单独放在sizeof的内部,计算的的是整个数组的大小。3x4x4=48。

sizeof(a[0][0]) , 第一行第一列元素的大小,结果为4。

sizeof(a[0]) ,表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是第一行的大小4*4=16。

sizeof(a[0] + 1) , a[0]作为第一行的数组名,没有&,没有单独放在sizeof内部,所以a[0]表示的就是首元素的地址,即a[0][0]的地址。a[0] + 1就是a[0][1]的地址,地址大小为4或8字节。

sizeof(*(a[0] + 1)),第一行第二个元素,4。

sizeof(a + 1)),a是二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素地址,即第一行的地址,a+1就是第二行地址。是int(*)[4]的数组指针。是地址就是4或8字节。

sizeof(* (a + 1) ), * (a+1)就是第二行,相当于第二行的数组名, * (a+1)->a[1],sizeof( * (a + 1))计算的是第二行的大小,16字节。

sizeof(&a[0] + 1), a[0]是第一行的地址,&a[0]是第一行的地址,&a[0]+1就是第二行的地址,是地址就是4/8字节。

sizeof(*(&a[0] + 1)) ,相当于第二行,也就是sizeof(a[1]),大小是16字节。

sizeof(*a), a二维数组的数组名,没有&,没有单独放在sizeof内部,a表示首元素地址 , * a是二维数组的首元素,也就是第一行,大小是16字节。

sizeof(a[3]) , 感觉越界了,但是没关系,看到了a[3]和a[1] a[0]是一样的,大小是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;
}
//程序的结果:2 5

解:&a表示取出整个数组的地址,+1跳过整个数组,然后对这个强制类型转换成int * 的类型。
ptr-1,就指向了5,(a+1)指向了2。
在这里插入图片描述

第二题:

假设p 的值为0x10 0000。 如下表表达式的值分别为多少?
已知,结构体Test类型的变量大小是20个字节。

struct Test
{
    int Num;//4
    char* pcName;//4 
    short sDate;//2
    char cha[2];//1*2
    short sBa[4];//2*4
    //其实结构体的大小也是可以算的。
}*p;

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

解:p是结构体指针,已知这个结构体为20个字节,所以+1跳过20个字节,0x0010 0014。

将p强制类型转化为unsigned long类型,这个是一个整数,+1的话就是普普通通的整数+1,千万不要和指针+1搞混了!结果为0x0010 0001。

将p强制类型转化为unsigned int* 类型,这个是一个整型指针,+1的话跳过四个字节,
结果为0x0010 0004。

第三题

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);//4,2000000
    return 0;
}

%X表示以十六进制数的形式打印。

解:a是数组名,&a+1跳过了整个数组,然后强制类型转化为int* 的类型,命名为ptr1,
ptr[-1]表示* (ptr-1),跳过了4个bit,指向了04的位置。
由于是小端存储,所以十六进制打印出来的是4。

a先转化为整形,+1,然后转化为int* 类型,命名为ptr2,ptr2 访问到的是 00 00 00 02
由于是小端存储,所以十六进制打印出来的是02 00 00 00。
在这里插入图片描述

第四题

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

解:这里有逗号表达式,实际上存放在a[3][2] 里面的值为:
1 3
5 0
0 0
p指向a[0]的位置,p[0]表示*(p+0) ,解引用得到的值为1,
那么也不难得到p[1]的值为3。

第五题

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

解:int(*p)[4]表示数组指针,p能够指向的数组是4个元素,p+1往后跳4个整型。
p[4][2]可以视为 * ( * (p+4)+2)
&p[4][2] 和 &a[4][2] 的位置如图所示:
内存
小的地址减去大的地址,得到-4,一个是以整型的形式打印就是-4,
一个是以%p的形式打印,那么就是打印-4的补码:FFFFFFFC。

第六题

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(这个比较简单)
在这里插入图片描述

第七题

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

解:a先和[ ]结合,说明他是一个数组。数组中的元素是char* 类型,指针指向字符串的第一个字符。
所以a是“字符串数组指针”。
char** pa,是一个二级指针,+1,就会往后4个bit ,其实就是打印char* 指向的元素。
结果为 at
在这里插入图片描述

第八题

本题最难。

int main()
{
    char* c[] = { "ENTER","NEW","POINT","FIRST" };
    char** cp[] = { c + 3,c + 2,c + 1,c };
    char*** cpp = cp;
    printf("%s\n", **++cpp);//POINT
    printf("%s\n", *-- * ++cpp + 3);//ER
    printf("%s\n", *cpp[-2] + 3);//
    printf("%s\n", cpp[-1][-1] + 1);
    return 0;
}

解:
结构
c是“字符串数组指针”
**++cpp,cpp先和++结合,然后两次解引用,得到了POINT。

*-- * ++cpp + 3。++ 的优先级高于 “解引用操作符” 高于“加法操作符”。注意由于执行了第一步,cpp已经指向了C+2的位置!!
++cpp指针指向c+1的位置,解引用就访问到了c+1,这个时候计算- -操作符得到c,这时解引用访问到了c[0]的位置。c是“字符串数组指针”,c[0]是’ENTER\0’的地址。+3之后,指向了’E’,所以打印出ER。

目前cpp指针指向c+1的位置,cpp[-2]可以看成* (cpp-2),到了c+3的位置,再次解引用,得到c[3]他是’FIRST\0’的地址.+3之后,指向了’S’,最后打印出来的是ST。

目前cpp指针还是指向c+1的位置,cpp[-1][-1]可以看成*(* (cpp-1)-1),cpp-1解引用得到c+2,-1得到c+1,然后解引用得到c[1],他是’NEW’的地址,+1得到了E,最后打印的是EW。

结语

有大佬说过,编程有三个阶段,初级阶段:看代码是代码。中级阶段:看代码是内存。高级阶段:看代码是代码。
这刚好也符合马原中讲的螺旋上述的规律。
很幸运目前我步入了第二阶段,我将不断前进。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值