目录
1.指针和数组的相关题目
//一维数组
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));
好的,让我们先来看这些题目。在做这些题目之前,我们最好先了解一些规则。
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。3. 除此之外所有的数组名都表示首元素的地址。
在了解了这些规则之后,做这些题目不就如鱼得水了吗。
我们从第一题开始看,首先,sizeof里面放了个数组名,用第一条规则我们很容易就能得出答案为16。然后是第二题,我们都知道,数组名表示的是首元素的地址,其实在我们给他+0的时候,他就不再代表数组名了,而是代表的数组第一个元素的地址,所以这个地方实际上是求的指针的大小,也就是4/8(32位平台是4,64位平台是8)。然后是第三题,对a进行解引用,也就是对数组的首元素地址进行解引用,所以的得到的是数组首元素,大小就是4。下一个呢,跟第二个同理,4/8。第五题呢,就是求第二个元素的大小,4。第六个是对数组名进行&得到的是整个数组的地址,所以是个指针,因此答案是4/8。第七个呢是对数组的名字取地址之后再解引用,也就是啥都没干,所以答案是16。第八个取出数组的地址之后+1,表示的还是一个指针,所以答案还是4/8,然后后面紧跟着的两个也是这个意思,就不在多谈了。
//字符数组
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));
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));
第一个,求这个数组的大小,这个数组之后6个字符,所以大小自然是6。第二个跟前文一样,表示的是求一个指向首元素地址的指针的大小,所以答案是4/8。第三,四,五,六,七都跟前面同理,这里我们不再解释。看第八个,在这之前我们要知道
strlen在读取到\0之后停止
很明显,这个数组在初始化的时候并没有\0,所以说第八个是随机值。第九个呢也是同理的,第十个,是一个非法的访问,因为strlen接受的是一个指针,我们传递给他一char类型的变量自然不行。第十一个也同理。第十二个更不用说,没有\0固然是随机值,第十三,十四也都是这样的。
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));
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));
先来看第一个,很明显,arr里面有七个字符,a,b,c,d,e,f,\0所以大小是7。后面几个与前面同理,就不在讲了。然后我们看到第八个,因为前面的初始化方式会默认放一个\0在后面,所以结果是6。下面几个原理与前面的类似,所以这里不再过于解释了。
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));
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));
来看这一个,很明显,第一个,第二个求的是指针变量的大小,所以说是4/8.第三个的话,字符型指针在指向字符串的时候,存储的是字符串首字符的地址,所以这里解引用得到的是一个字符,因而大小是1。第四个也同理,在前面的文章中我们讲过,这其实就等同意*(p+0),所以自然大小也是1。然后五,六,七在前面讲过,不必在多谈。然后看到第八个,其实我们不难发现,这个其实是跟数组的存储原理是一抹一样的,所以strlen在接受到p之后会逐个向后访问,直到访问到\0为止,因而答案是6。第九个同理。第十个,十一个跟前文一样,非法访问了。然后第十二个的话,可以理解为取出了整个字符串的地址,因为找不到\0所以,会输出随机值。第十三个同理。最后一个的话,其实就是&(*(p+0))+1,也就是指向了下一个元素的地址,故会输出5。
//二维数组
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]));
其实呢,二维数组就是每个元素都是一维数组的数组。第一个不必多说,求的是二维数组的总大小,也就是48。第二个呢求得是二维数组第一行第一个元素的大小,也就是4。第三个,a[0]其实代表的是第一行的数组,也就是第一样数组首元素的地址,因而这个地方求得是第一行数组的大小,所以答案是16。 第四个因为+1了吗,所以就变成第一行第二个元素的地址了,所以这个地方求的是指针的大小,所以答案是4/8。第五个其实就是求第一行第二个元素的大小,所以是4。第六个,二维数组名+1,二维数组名指向的是第一行的地址,+1就成了第二行的地址,也就是一个数组指针,所以答案是4/8.第七个可以理解为把第二行的数组名解引用,得到的是第二行数组名,所以答案是16。第八个,其实表示的就是第二行数组的地址,是一个数组指针,所以答案是4/8。下一个不用解释16。第九个,二维数组名表示的是二维数组第一行的地址,也就是一个数组指针,解引用表示的是一个数组,所以说答案是16,最后一个也不需要讲。
2.指针笔试题
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
这一题,在拿到数组a的地址后+1跳过了整个数组,然后将数组强制类型转化为int类型然后赋给ptr,所以ptr每一次加减都是跳过四个字节,而a是跳过一个数组,因此答案是2,5。
//这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
第一个打印实际上就是打印p指针+1之后跳到的位置,因为结构体大小是20个字节,转化为16进制是14,所以打印0*1000014,第二个打印现将p转化为了无符号长整形,然后+1所以答案是0*1000001 第三个打印是将p转化为无符号长整形的指针,然后+1跳过的是4个字节,所以最后输出的是0*1000004。
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;
}
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
//假设以小端存储方式
//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
int* ptr1 = (int*)(&a + 1);
//&a+1相当于跳过了整个数组,到达了4之后的位置,然后强制转化成int*
//ptr1也指向那里
int* ptr2 = (int*)((int)a + 1);
//这里的a被强制转化成int型,整型+1就是+1,差了一个1就1个字节
//然后又被强转为int*,所以ptr2指向第一个元素的第二个字节位置处,与第一个元素差一个字节
//ptr2是一个整型指针
printf("%x,%x", ptr1[-1], *ptr2);//4,2000
//ptar[-1]可以理解为*(ptr1+(-1)),又ptr1是整型指针4个字节,-1跳到0x 00 00 00 04
//对ptr2解引用后向后访问4个字节,又以%x打印,所以是0x 02 00 00 00
return 0;
}
#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) };
//首先要注意的是这里用的是逗号和括号,所以说这里其实是逗号表达式,也就是说只初始化了前三个元素,分别问1,3,5
int *p;
p = a[0];
printf( "%d", p[0]);
//p[0]就是*(p+0)也就是a[0],所以是1
return 0;
}
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 main()
{
int a[5][5];//这里是一个五行五列的二维数组
int(*p)[4];//这个可以理解为一个不知道多少行,但是有4列的数组指针,所以说这里+1跳过的是4个元素
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//因此这边的答案是FFFFFFFC -4
return 0;
}
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;
}
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));
//实际上是将第二行的首元素转化为int*然后赋给*ptr2,所以这里+-是跳过四个字节的
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
//因此答案是10 5
return 0;
}
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
所以说这里打印的结果是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);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}
*++cpp:第一次,++cpp指向c+2,c+2指向point地址,第二*得到point
*–*++cpp+3:++cpp指向c+1的地址,解引用找到c+1,–就把c+1变成了c,c指向ENTER的地址,在解引用,得到ENTER,+3就是ENTER第三个位置开始,得到ER。
*cpp[-2]+3:相当于* *(cpp-2)+3.首先我们要知道经过前面两个前置++之后,cpp指向了第3个元素的地址,现在-2相当于打回原形,指向cp第一个元素的地址,第一次解引用指向c+3,在一次解引用得到FIRST,+3指向ST,所以结果为ST
cpp[-1][-1]+1:相当于*(*(cpp-1)-1)+1:上面的那次并没有自增自减,所以还是第三个元素的地址,cpp-1指向第二个元素的地址,解引用得到c+2,-1得到c+1,c+1就是NEW的地址,在解引用得到NEW,+1得到EW。
3.答案
一维数组 16 4/8(这是4或者8的意思) 4 4/8 4 4/8 16 4/8 4/8 4/8
字符数组 6 4/8 1 1 4/8 4/8 4/8 随机 随机 访问冲突 访问冲突 随机 随机 随机 7 4/8 1 1 4/8 4/8 4/8 6 6 访问冲突 访问冲突 6 随机 5 4/8 4/8 1 1 4/8 4/8 4/8 6 5 访问冲突 访问冲突 随机 随机 5
二维数组 48 4 16 4/8 4 4/8 16 4/8 16 16 16
指针笔试题 2,5 0x100014 0x100001 0x100004 4,2000000 1 FFFFFFFC,-4 10,5 at POINT ER ST EW