今天,我们一起来解析笔试题,一起走上人生巅峰。我们先来看看关于数组的笔试题。
首先我们要搞清楚数组名的意义( sizeof计算结果的单位是字节):
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
数组笔试题
一维数组
sizeof只关心类型,括号中给出什么类型,sizeof就计算它分配内存的大小。
//一维数组
int a[] = { 1, 2, 3, 4 };
//sizeof(数组名),计算的是整个数组的大小
printf("%d\n", sizeof(a));//16字节
//a是首元素的地址,a+0也是地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(a + 0)); //4字节
//a是首元素地址,*a为首元素,int类型,4字节
printf("%d\n", sizeof(*a)); //4字节
//a是首元素地址,a+1为a[1]的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(a + 1)); //4字节
//a[1]=2,为int类型
printf("%d\n", sizeof(a[1])); //4字节
//&a为整个数组的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&a)); //4字节
//&a为整个数组的地址,*&a就得到整个数组
printf("%d\n", sizeof(*&a)); //16字节
//&a为整个数组的地址,&a+1跳过整个数组的那个地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&a + 1)); //4字节
//&a[0],取a[0]的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&a[0])); //4字节
//&a[0] + 1,得到的是a[1]的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&a[0] + 1));//4字节
字符数组
//字符数组
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
//sizeof(数组名),数组名表示整个数组,计算整个数组的大小,char类型为1个字节
printf("%d\n", sizeof(arr)); //6字节
//arr首元素地址,arr+0也为地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(arr + 0)); //4字节
//arr首元素地址,*arr指向的是首元素,char类型
printf("%d\n", sizeof(*arr)); //1字节
//arr[1]为char类型
printf("%d\n", sizeof(arr[1]));//1字节
//&arr取整个数组的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&arr)); //4字节
//&arr为整个数组的地址,&a+1跳过整个数组的那个地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&arr + 1)); //4字节
//&arr[0],取arr[0]的地址,&arr[0]+1,跳过一个字节,到arr[1]的地址,指针大小为4字节
printf("%d\n", sizeof(&arr[0] + 1));//4字节
strlen()函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0’)。strlen结束的标志是,找到’\0’为止。
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
//arr是首元素的地址,从首元素开始找,直到'\0'停止,但是arr数组中没有'\0'
//strlen函数就会继续往后面找,直到'\0'停止,我们也不能具体的知道'\0'的位置
//就会是一个不确定的值
printf("%d\n", strlen(arr)); //随机值
//arr+0也是首元素地址,与上面同理
printf("%d\n", strlen(arr + 0)); //随机值
//arr是首元素的地址,*arr指向首元素为'a'
//strlen就会以'a'的ASCII值为地址,向后寻找'\0'
//这个地址不在我们程序范围内,就会出现野指针的问题,程序就会报错
printf("%d\n", strlen(*arr)); //error
//arr[1]为'b',与上面同理
printf("%d\n", strlen(arr[1])); //error
//&arr是整个数组的地址,它与arr的值相同,以这个地址继续向后面找'\0'
printf("%d\n", strlen(&arr)); //随机值
//&arr+1是跳过整个数组,'f'后面的地址,继续找'\0'
printf("%d\n", strlen(&arr + 1)); //随机值
//&arr[0]是首元素'a'的地址,&arr[0]+1是'b'的地址
printf("%d\n", strlen(&arr[0] + 1));//随机值
char arr[] = "abcdef";
//sizeof(arr),arr表示整个数组,计算整个数组的大小
//数组为 [a][b][c][d][e][f][\0] char -> 1字节
printf("%d\n", sizeof(arr));//7字节
//arr是首元素地址,arr+0还是首元素地址,在32位平台,在32位平台,指针大小为4字节
printf("%d\n", sizeof(arr + 0));//4字节
//arr是首元素地址,*arr是指向首元素的地址,*arr为'a'
printf("%d\n", sizeof(*arr)); //1字节
//arr[1]='b'
printf("%d\n", sizeof(arr[1]));//1字节
//&arr是整个数组的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&arr)); //4字节
//&arr+1跳过整个数组的那个地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&arr + 1));//4字节
//&arr[0]+1是arr[1]的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(&arr[0] + 1));//4字节
//arr是首元素的地址, 从这个地址往后找到'\0'为止
printf("%d\n", strlen(arr));//6
//arr+0也是首元素的地址
printf("%d\n", strlen(arr + 0));//6
//arr是首元素的地址,*arr指向首元素为'a'
//strlen就会以a的ASCII值为地址,向后寻找'\0'
//这个地址不在我们程序范围内,就会出现野指针的问题,程序就会报错
printf("%d\n", strlen(*arr));//error
//arr[1]='b',与上面同理
printf("%d\n", strlen(arr[1])); //error
//&arr是整个数组的地址,它与arr的值相同,以这个地址继续向后面找'\0'
printf("%d\n", strlen(&arr));//6
//&arr + 1是跳过整个数组的那个地址,此时以这个地址向后找'\0'
//直到内存中出现'\0'停止,但我们不知道'\0'的具体位置,会是个不确定的值
printf("%d\n", strlen(&arr + 1));//随机值
//&arr[0] + 1,是arr[1]的地址,从这个位置找'\0'
printf("%d\n", strlen(&arr[0] + 1));//5
char *p = "abcdef";
//此时的p不是一个数组
//"abcdef"是一个常量字符串,p中存储的是'a'的地址
//在32位平台,指针大小为4字节
printf("%d\n", sizeof(p));//4字节
//p+1是'b'的地址,在32位平台,指针大小为4字节
printf("%d\n", sizeof(p + 1)); //4字节
//p中存储的是'a'的地址,*p指向'a','a'是char类型
printf("%d\n", sizeof(*p)); //1字节
//p[0] <==> *(p+0) <==> *p 与上面同理
printf("%d\n", sizeof(p[0])); //1字节
//p中存储的是'a'的地址,&p是p的地址,&p需要一个二级指针来存储
//在32位平台,指针大小为4字节
printf("%d\n", sizeof(&p)); //4字节
//&p是p的地址,&p+1跳过整个p (char *),&p+1还是地址
printf("%d\n", sizeof(&p + 1)); //4字节
//p[0] <==> *(p+0) <==> *p *p指向'a'
//&p[0]是'a'的地址,&p[0] + 1是'b'的地址
printf("%d\n", sizeof(&p[0] + 1));//4字节
//p中存储的是'a'的地址,从'a'开始往后面找'\0'
//"abcdef" -> a b c d e f \0
printf("%d\n", strlen(p)); //6
//p+1是'b'的地址,从'b'开始往后面找
printf("%d\n", strlen(p + 1));//5
//p中存储的是a的地址,*p指向'a'
//strlen就会以a的ASCII值为地址,向后寻找'\0'
//这个地址不在我们程序范围内,就会出现野指针的问题,程序就会报错
printf("%d\n", strlen(*p)); //error
//p[0] <==> *(p+0) <==> *p,*p指向'a',与上面同理
printf("%d\n", strlen(p[0]));//error
//p中存储的是'a'的地址,&p是p的地址
//我们不知道'\0'的位置,所以是个随机值
printf("%d\n", strlen(&p));//随机值
//&p + 1跳过整个p的地址,我们也不知道'\0'的位置
printf("%d\n", strlen(&p + 1)); //随价值
//p[0] <==> *(p+0) <==> *p,*p指向'a'
//&p[0]是'a'的地址,&p[0] + 1是'b'的地址
printf("%d\n", strlen(&p[0] + 1));//5
二维数组
//二维数组
int a[3][4] = {0};
//sizeof(a),a表示整个数组,计算整个数组的大小
//总共有12个元素,每个元素都是int型
printf("%d\n",sizeof(a)); //48
//a[0][0] -> 是一个元素,元素类型为int
printf("%d\n",sizeof(a[0][0]));//4
//a[0]是第一行元素数组名
//sizeof(),括号中单独放数组名,计算整个数组的大小
printf("%d\n",sizeof(a[0])); //16
//a[0]+1不是单独放,所以不是计算整个数组的大小
//a[0]是数组名,a[0]是第一行一维数组首元素地址
//a[0]+1就是a[0][1]的地址,在32位平台,指针大小为4字节
printf("%d\n",sizeof(a[0]+1)); //4
//a[0]+1是a[0][1]的地址,*(a[0]+1)就是a[0][1],int类型
printf("%d\n",sizeof(*(a[0]+1))); //4
//a是二维数组名,数组名是首元素地址
//二维数组的首元素就是第一行一维数组
//a就是第一行一维数组地址,a+1跳过第一行一维数组,是第二行的数组地址
printf("%d\n",sizeof(a+1));//4
//a+1是第二行的数组地址,*(a+1)是第二行的数组
printf("%d\n",sizeof(*(a+1))); //16
//&a[0]是第一行一维数组地址&a[0]+1
//&a[0]+1跳过第一行一维数组,是第二行的数组地址
printf("%d\n",sizeof(&a[0]+1)); //4
//&a[0]+1是第二行的数组地址
//*(&a[0]+1)是第二行的数组
printf("%d\n",sizeof(*(&a[0]+1))); //16
//a是第一行一维数组地址
//*a是第一行数组
printf("%d\n",sizeof(*a)); //16
//二维数组a[3][4],有a[0],a[1],a[2]
//a[3]已经越界了,但是sizeof只关注类型
//如果数组a[3][4],有a[3],那么a[3]和a[2]的类型相同,都是数组名
//sizeof(),括号中单独放数组名,计算整个数组的大小
printf("%d\n", sizeof(a[3])); //16
指针笔试题
笔试题一
int a[5] = { 1, 2, 3, 4, 5 };
//&a是数组地址,&a+1跳过整个数组,指针指向元素5后面的地址
//(int *)(&a + 1)将数组地址强制转化为整形地址
int *ptr = (int *)(&a + 1);
//a是数组名,数组名是首元素地址,a[0]的地址
//a+1是a[1]的地址,*(a + 1)是a[1]=2
//ptr指向5后面的地址,ptr-1指向元素5的地址
//*(ptr - 1)是5
printf("%d,%d", *(a + 1), *(ptr - 1)); //2 5
笔试题二
//这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//0x1是16进制,化为十进制是1 p+1
//结构体的大小是20个字节,p是结构体指针
//p+1 -> p+20字节 -> 20的16进制是0x00000014
//0x100014
printf("%p\n", p + 0x1); //0x100014
//(unsigned long)p 将p强制转换为无符号整数,现在0x100000就是一个整数
// 0x100000+0x1 -> 0x100001
printf("%p\n", (unsigned long)p + 0x1); //0x100001
//(unsigned int*)p 将p转化为int* ,p+1 ,p+4字节 4的16进制是4
//0x100004
printf("%p\n", p+ 0x1);//0x100004
笔试题三
int a[4] = { 1, 2, 3, 4 };
//&a是数组地址,&a+1,跳过整个数组,指针指向元素4后面的地址
//(int *)(&a + 1)将指针指向元素4后面的地址强制转化为int*
//ptr1[-1] -> *(ptr1-1)
//ptr1-1指向元素4的地址,*(ptr1-1)就是元素4
//要16进制输出,0x4
int *ptr1 = (int *)(&a + 1);
//a是首元素地址,(int)a 将首元素地址强制转化为整数
//(int *)((int)a + 1),相当于在首元素地址加一个字节
//在vs编译器中,数组在内存中的存储是小端存储 01 00 00 00 02 00 00 00 03 00 00 00...
//首元素的地址是01的地址,首元素地址+一个字节的地址就是01后面紧跟着00的地址
//此时的ptr2是整形指针,*ptr2访问4个字节,00 00 00 02,小端存储就要小端拿回来 02 00 00 00
//16进制输出,0x2000000
int *ptr2 = (int *)((int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);//4 0x2000000
笔试题四
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//a中是逗号表达式,实际上数组中的元素a[3][2]={1,3,5}
int *p;
p = a[0];
//a[0]是第一行一维数组名,是首元素地址,a[0][0]的地址
//p[0] -> *(p+0) ->*p -> a[0][0] ->1
printf("%d", p[0]); //1
笔试题五
int a[5][5];
int(*p)[4];
p = a;
//a是二维数组名,是首元素地址,二维数组的首元素是第一行数组
//a是第一行数组地址,第一行数组有5个元素
//p为数组指针,只能接受4个元素的一维数组地址
//指针-指针就是中间所隔元素的个数
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//0xFFFFFFFC -4
详解见下图
笔试题六
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//&aa是二维数组地址,&aa+1跳过整个二维数组,元素10后面的地址
//ptr1-1是元素10的地址,*(ptr1-1)是元素10
int *ptr1 = (int *)(&aa + 1);
//aa是二维数组名,是第一行一维数组地址
//aa+1跳过第一行数组,到第二行数组地址
//*(aa + 1)是第二行的首元素地址
//ptr2 - 1是元素5的地址,*(ptr2 - 1)是元素5
int *ptr2 = (int *)(*(aa + 1));
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 5
笔试题七
char *a[] = { "work", "at", "alibaba" };
char**pa = a;
pa++;
printf("%s\n", *pa);//at
详解如下图
笔试题八
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); //ST
printf("%s\n", cpp[-1][-1] + 1);//EW
总结
在我们遇到sizeof(),一定要注意括号里的类型意义,strlen要注意起始地址。
做题的时候要仔细读题,注意数组中的逗号表达式,还要注意大端或小端的存储方式。我们要明白二维数组首元素是什么,这些题还需要多多揣摩,才能更好的理解。