今天,我们一起来解析笔试题,一起走上人生巅峰。我们先来看看关于数组的笔试题。
首先我们要搞清楚数组名的意义( 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要注意起始地址。
做题的时候要仔细读题,注意数组中的逗号表达式,还要注意大端或小端的存储方式。我们要明白二维数组首元素是什么,这些题还需要多多揣摩,才能更好的理解。