突破•指针六

数组和指针笔试题解析🫧

sizeof关注类型,strlen关注内容。

一维数组

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

答案与解析(看注释):

#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//规定sizeof(数组名)计算的是整个数组的大小,而 &数组名 取出的是整个数组的地址,其余情况的数组名都是首元素的地址,一个int型4个字节,答案是4*4=16。
	printf("%d\n", sizeof(a + 0));//这里的a是数组首地址,首地址加0还是首地址,地址都是4或8字节【x86下是4个字节,x64下是8个字节】,(实际sizeof并不会计算括号内的表达式,它只是推断出表达式结果的类型,根据类型和个数得出结果),这里的答案是4或8。
	printf("%d\n", sizeof(*a));//这里的a是首地址,解引用后就是首元素,答案是4。
	printf("%d\n", sizeof(a + 1));//首元素的地址加1就是第二个元素的地址,地址都是4或8【地址都是同样长度的一串字母和数字】,答案是4或8。
	printf("%d\n", sizeof(a[1]));//第二个元素,一个int型的元素,答案是4。
	printf("%d\n", sizeof(&a));//整个数组的地址,是地址,答案是4或8。
	printf("%d\n", sizeof(*&a));//两种解释,第一种是*和&抵消了,最终是sizeof(a)。第二种是&a取出的是整个数组的地址,用*把地址解引用后就是整个数组了。答案是16。
	printf("%d\n", sizeof(&a + 1));//整个数组的地址加1,跳过整个数组的地址,最终还是地址,答案是4或8。
	printf("%d\n", sizeof(&a[0]));//第一个元素的地址,答案是4或8。
	printf("%d\n", sizeof(&a[0] + 1));//第一个元素的地址加1,跳过一个元素,第二个元素的地址,答案是4或8。
	return 0;
}

字符数组

1🍔🍔🍔🍔🍔🍔🍔

🍔题:问运行结果是什么

#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' };
	printf("%d\n", sizeof(arr));//计算整个数组的大小,6个字符,答案是6
	printf("%d\n", sizeof(arr + 0));//首元素地址加0,还是首元素地址,答案是4或8
	printf("%d\n", sizeof(*arr));//首元素地址解引用后是首元素,1个char型的数据,答案是1
	printf("%d\n", sizeof(arr[1]));//第二个元素,答案是1
	printf("%d\n", sizeof(&arr));//整个数组的地址,答案是4或8
	printf("%d\n", sizeof(&arr + 1));//跳过整个数组后的地址,答案是4或8
	printf("%d\n", sizeof(&arr[0] + 1));//跳过第一个元素后的地址,即第二个元素的地址,答案是4或8
	return 0;
}

2🌭🌭🌭🌭🌭🌭🌭

🌭题:问运行结果是什么

#include <stdio.h>
#include <string.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>
#include <string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//arr是首元素的地址,从首元素开始向后数,没有\0,停不下来,答案是随机值,不同平台的结果不同
	printf("%d\n", strlen(arr + 0));//首元素加0还是首元素的地址,从首元素开始向后数,没有\0,停不下来,答案是随机值,不同平台的结果不同
	printf("%d\n", strlen(*arr));//解引用首元素地址后即首元素,括号中应该写地址,所以这个代码是错的
	printf("%d\n", strlen(arr[1]));//第二个元素,括号中应该写地址,所以这个代码是错的
	printf("%d\n", strlen(&arr));//整个数组的地址【&arr的类型是char (*)[6],会被强制转换成char*类型,因为strlen是size_t strlen(const char* s)】的值与首元素地址相同,从首元素开始向后数,没有\0,停不下来,答案是随机值,不同平台的结果不同
	printf("%d\n", strlen(&arr + 1));//跳过整个数组后的地址,从此处往后数,没有\0,停不下来,答案是随机值,不同平台的结果不同
	printf("%d\n", strlen(&arr[0] + 1));//跳过首元素后的地址,即第二个元素的地址,从此处往后数,没有\0,停不下来,答案是随机值,不同平台的结果不同
	return 0;
}

3🍜🍜🍜🍜🍜🍜🍜

🍜题:问运行结果是什么

#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));//计算整个数组的大小,字符串末尾有\0,答案是6+1=7
	printf("%d\n", sizeof(arr + 0));//首元素地址加1还是首元素地址,答案是4或8
	printf("%d\n", sizeof(*arr));//首元素解引用后是首元素,答案是1
	printf("%d\n", sizeof(arr[1]));//第二个元素,答案是1
	printf("%d\n", sizeof(&arr));//整个数组的地址,答案是4或8
	printf("%d\n", sizeof(&arr + 1));//跳过整个数组后的地址,答案是4或8
	printf("%d\n", sizeof(&arr[0] + 1));//跳过首元素后的地址,即第二个元素的地址,答案是4或8
	return 0;
}

4🌮🌮🌮🌮🌮🌮🌮

🌮 题:问运行结果是什么

#include <stdio.h>
#include <string.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>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//从首元素开始数,字符串末尾有\0,数到\0前面的停止,答案是6
	printf("%d\n", strlen(arr + 0));//还是首元素地址,答案是6
	printf("%d\n", strlen(*arr));//传的是首元素,会报错,应该传地址
	printf("%d\n", strlen(arr[1]));//传的是第二个元素,会报错
	printf("%d\n", strlen(&arr));//整个数组的地址,与数组首元素的地址相同【&arr的类型是char(*)[7],会被强制类型转换成char*类型】,即从首元素开始数,字符串末尾有\0,数到\0前面的停止,答案是6
	printf("%d\n", strlen(&arr + 1));//跳过整个数组后的地址,注意\0属于数组内的,即后面没有\0了,答案是随机值
	printf("%d\n", strlen(&arr[0] + 1));//从第二个元素开始数,字符串末尾有\0,数到\0前面的停止,答案是5
	return 0;
}

5🍢🍢🍢🍢🍢🍢🍢

🍢题:问运行结果是什么

#include <stdio.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));
	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));//p是指针,是字符串首字符a的地址,答案4或8
	printf("%d\n", sizeof(p + 1));//p+1是b的地址,答案是4或8
	printf("%d\n", sizeof(*p));//p的类型是const char*,*P的类型就是char*,答案是1个字节
	printf("%d\n", sizeof(p[0]));//可以把常量字符串看成数组,即p[0]是首字符a,也可以理解成p[0] == *(p+0) == *p == 'a',答案是1
	printf("%d\n", sizeof(&p));//p本来就是地址,再把p的地址取出来,相当于二级指针,即&p的类型是char**,地址都是4或8,答案是4或8
	printf("%d\n", sizeof(&p + 1));// 跳过p指针变量一个char*大小之后的地址,答案是4或8
	printf("%d\n", sizeof(&p[0] + 1));//首字符地址再跳过一个char*大小的字节,即b的地址,答案是4或8
	return 0;
}

6🥟🥟🥟🥟🥟🥟🥟

🥟题:问运行结果是什么

#include <stdio.h>
#include <string.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>
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));//a的地址向后数,答案是6
	printf("%d\n", strlen(p + 1));//b的地址向后数,答案是5
	printf("%d\n", strlen(*p));//字符a,会报错
	printf("%d\n", strlen(p[0]));//一样,字符a,会报错
	printf("%d\n", strlen(&p));//从p这个指针变量的起始位置开始向后数,p的地址是一块空间,字符串"abcdef"的地址是另一块空间,p的地址未知,也不知道有没有\0,答案是随机值
	printf("%d\n", strlen(&p + 1));//跳过p的地址后延续的地址,往后数不知道有没有\0,答案是随机值
	printf("%d\n", strlen(&p[0] + 1));//&p[0]是a的地址,+1后是b的地址,向后数,答案是5
	return 0;
}

在这里插入图片描述

二维数组

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

1🧋🧋🧋🧋🧋🧋🧋

🧋题:问运行结果是什么

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

答案与解析(看注释):
可以看作数组名的,单独放在sizeof内就是计算整个的大小。

#include <stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//数组名a单独放在sizeof里,计算整个数组的大小,答案是3*4*4=48
	printf("%d\n", sizeof(a[0][0]));//第一行第一个元素,答案是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]的首元素地址,即&a[0][0],+1就是&a[0][1],答案是4或8
	printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)是第一行的第二个元素,答案是4
	printf("%d\n", sizeof(a + 1));//这里的a是二维数组a的首元素地址,即第一行地址,a+1跳过一行,是第二个行的地址,二维数组的每个元素是一行,a+1是数组指针,答案是4或8
	printf("%d\n", sizeof(*(a + 1)));//第二行元素,答案是4*4=16
	printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,+1后是第二行的地址,答案是4或8
	printf("%d\n", sizeof(*(&a[0] + 1)));//第二行的元素,答案是4*4=16
	printf("%d\n", sizeof(*a));//a是二维数组首元素地址,a是第一行的地址,*a是第一行元素,答案是4*4=16
	printf("%d\n", sizeof(a[3]));//可以通过类型推断出长度,a[3]无需真实存在,a[3]是第四行的数组名,单独放在sizeof里,计算第四行的大小,答案是4*4=16
	return 0;
}

指针运算笔试题解析🫧

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

答案与解析(看注释):

#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));//a是首元素的地址,+1后就是第二个元素的地址,即2。&a取出的是整个数组的地址,+1就是跳过整个数组后的地址【在5后面】,再-1回到5。答案是2,5
return 0;
}

2🍉🍉🍉🍉🍉🍉🍉

🍉题:问运行结果是什么

#include <stdio.h>
//在X86环境下
//假设结构体的大小是20个字节
//程序输出的结果是啥?
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;
}

答案与解析(看注释):

#include <stdio.h>
//在X86环境下
//假设结构体的大小是20个字节
//程序输出的结果是啥?
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;//0x100000默认为int型,强制转换为struct Test*型
int main()
{
	printf("%p\n", p + 0x1);//0x1其实就是1,即p+1,+1就是跳过一个p大小的空间,答案是0x100000+20=00100014【1*16¹+4*16⁰=20】
	printf("%p\n", (unsigned long)p + 0x1);//整型相加直接加,答案是0x100000+1=0x100001,打印出来是00100001
	printf("%p\n", (unsigned int*)p + 0x1);//一个unsigned int*是4个字节,答案是0x100000+4=0x100004,打印出来是00100004【在32位环境下用%p打印前面要补够两个0才变成8个16进制位】
	return 0;
}

运行截图:
在这里插入图片描述

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

答案与解析(看注释):

#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//初始化应用{},所以这里是逗号表达式,结果是最后一个表达式的结果,即a[3][2] = { 1, 3, 5 },即 a[3][2] = { (1, 3), (5, 0), (0, 0) }
	int* p;
	p = a[0];//a[0]是第一行的数组名,这里不是前面讲的那两种特殊情况,则这里的a[0]是第一行的首元素,即&a[0][0]
	printf("%d", p[0]);//p[0] == *(p+0) == *p,即 1
	return 0;
}

在这里插入图片描述
运行截图:
在这里插入图片描述

4🥥🥥🥥🥥🥥🥥🥥

🥥题:问运行结果是什么

//假设环境是x86环境,程序输出的结果是啥?
#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环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{+
	int a[5][5];
	int(*p)[4];
	p = a;//把a的地址赋值给p
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//p只是指向和a一样的初始地址,但p有自己的类型int (*)[4],所以p[4][2]跳过4个整型和2个字节。a[4][2]跳过5个整型和5个字节。%p是十六进制打印地址,直接把补码转成地址形式。%d打印有符号整型,要把补码转成原码再打印。指针减指针后的绝对值是指针之间的元素个数。
	return 0;
}

在这里插入图片描述
运行截图:
在这里插入图片描述

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

答案与解析(看注释):

#include <stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);//&aa取出整个数组的地址,+1跳过整个数组,由int (*)[2][5]类型强制转为int (*)
	int* ptr2 = (int*)(*(aa + 1));//这里的aa是aa的首元素地址,即第一行aa[0],+1后跳过一行,就是第二行首元素的地址,即aa[1][0]==6,本来就是int (*),所以这里的强制转换是迷惑人的
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//ptr1和ptr2的类型都是int (*)类型,指针跳过几个字节由指针的类型决定
	return 0;
}

在这里插入图片描述
运行截图:
在这里插入图片描述

6🥑🥑🥑🥑🥑🥑🥑

🥑题:问运行结果是什么

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

答案与解析(看注释):

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };//a是一个指针数组,里面存放的是指针,字符串存的是首字符的地址,分别是w,a,a
	char** pa = a;//a是首元素的地址,指向"work"
	pa++;
	printf("%s\n", *pa);//%s打印字符串,从这个地址向后打印直到\0
	return 0;
}

在这里插入图片描述
运行截图:
在这里插入图片描述

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

答案与解析(看注释):

#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);//先++,再*,再*。cpp指向c+3的地址,++后来到c+2,*(c+2)后得到P的地址,再*后就是%S向后打印出POINT
	printf("%s\n", *-- * ++cpp + 3);//加法的优先级最低。上一个printf时,cpp以及变成了c+2,现在再++就变成c+1了,*后得到c+1【二级指针里存放的是一级指针的地址,解引用得到存放的内容】,c+1被--后得到c【是cp[2]的内容变成了c,不是变成了cp[3]】,解引用得到c的内容,即字符串ENTER的首字符地址,即E的地址,再+3后就得到第二个E的地址,向后打印ER
	printf("%s\n", *cpp[-2] + 3);//*cpp[-2]+3 == **(cpp-2)+3,cpp本来指向了cp[2],-2后指向cp[0],**(cpp-2)+3 == *cp[0]+3 == F地址+3 == S地址,向后打印ST
	printf("%s\n", cpp[-1][-1] + 1);//cpp还是cp[2],因为只有++和--会改变cpp,上面的cpp[-2]不会。cpp[-1][-1]+1 == *(*(cpp-1)+1) == *(c+2 - 1)+1 == *(c+1)+1 == NEW中N的地址+1 == NEW中E的地址,向后打印EW
	return 0;	
}

在这里插入图片描述

💗💗💗

在这里插入图片描述

💗💗💗

在这里插入图片描述
运行截图:
在这里插入图片描述

能量站😚

当你弱小的时候,连愤怒都像在撒娇。
请添加图片描述

❤️❤️❤️ 恭喜! 恭喜! 闯关成功! ❤️❤️❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值