记录一些关于指针和数组的笔试题,加强对于指针和数组的理解!!!
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关键字用于计算变量或类型、常量在内存中所占空间大小;
在做题之前我们先明白一个问题;
数组名在什么情况先代表整个数组、又在什么情况下代表数组首元素地址;
代表整个数组:
1、数组名单独放在sizeof内部,表示算的是整个数组的内存大小;
2、&数组名,表示的是一个数组的指针,该指针指向数组(&要和数组名结合,中间不能有任何“杂质”)
代表数组首元素地址:
不满足以上俩个结论就是代表数组首元素地址;
会过头来我们继续分析该题:
1、sizeof(a)数组名单独放在sizeof内部,此时数组名表示整个数组,因此sizeof求的是整个数组的大小,结果也就是:16;
2、再看a+0;数组名没有单独放在sizeof内部,也没有&,因此此时数组名代表首元素地址,既然是地址,那么地址也就是固定的,结果:4/8;
3、a,数组名没有单独放在sizeof内部,也没有&,因此此时数组名代表首元素地址,对首元素地址解引用,自然也就是int型,结果:4
4、看a+1;数组名没有单独放在sizeof内部,也没有&,因此此时数组名代表首元素地址,既然是地址,那么地址也就是固定的,结果:4/8;
5、a[1];数组名没有单独放在sizeof内部,也没有&,因此此时数组名代表首元素地址,a[1],代表下标为1的元素,该元素为int型,结果:4;
6、&a;数组名没有单独放在sizeof内部,但是有&,此时数组名代表的是整个数组,只不过是整个数组的指针,也就是指向该数组,既然是指针,大小也就自然而然的固定了,结果:4/8;
7、 *&a;数组名没有单独放在sizeof内部,但是有&,因此&a代表的是取出整个数组的指针, *代表对指针解引用,由于该指针指向整个数组,那么解引用出来,也就代表着整个数组,自然也就是计算的整个数组的大小,结果:16;(这里我们还可以这么理解,数值指针是怎么来的?是&+数组名吧,那么现在对数组指针解引用不就相当于得到了数组名嘛!)
8、&a+1;数组名没有单独放在sizeof内部,但是有&,&a是个数组指针指向整个数组,&a+1,跳过一个数组的大小,既然是指针,大小自然固定,结果:4/8;
9、&a[0];数组名没有单独放在sizeof内部,&也没有和数组名结合;a[0]代表下标为0的元素,&表示取出这个位置的地址,最终sizeof计算的是地址,结果:4/8;
10、&a[0]+1;数组名没有单独放在sizeof内部,也没有&,&a[0]+1,表示下表为1的元素地址,sizeof计算的是地址,结果:4/8;
运行结果:
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;
}
1、arr;数组名单独放在sizeof内部,此时数组名表示整个数组,sizeof计算的也是整个数组的大小,结果:6;
2、arr+0;数组名没有单独放在sizeof内部,此时数组名表示首元素地址,地址+0还是地址,自然结果就就是:4/8;
3、*arr;数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,对地址解引用,访问其指向对象,它指向对象为char型,sizeof计算char型,大小为:1;
4、arr[1];数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址;arr[1]表示访问下标为1的元素,类型为char,结果:1
5、&arr;数组名没有单独放在sizeof内部,&和数组名结合,此时数组名代表数组整个数组;&arr代表整个数组的指针,结果:4/8;
6、&arr+1;与上述一样,&arr既然是指针,&arr+1,自然也是指针,结果:4/8;
7、&arr[0]+1;arr先和[ ]结合,于是首先排除数组名代表整个数组的可能,因此此时数组名代表数组首元素地址,&arr[0]+1,就代表取出下标为0的元素的地址然后+1,结果还是地址,结果:4/8;
运行结果:
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;
}
strlen函数用于统计字符串长度,遇到‘\0’结束(不计算‘\0’);
1、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,由于我们不知道什么时候能遇到’\0’,strlen也就不知道什么时候结束,一切全靠编译器决定,因此结果:随机值;
2、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,结果:随机值;
3、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,*arr表示访问下标0的元素,即为:‘a’,阿斯克码值为97,也就是我们要求strlen从地址为97的位置开始计算,此时编译器会报错,因为我们非法访问内存了,97地址的空间不属于我们,属于操作系统,我们无权对其进行读写!结果:err
4、arr[1];与上述一样的问题,结果:err;
5、&arr;代表整个数组,也就是指向该数组的指针由于指向该数组的指针与数组首元素地址在值的方面上是一样的,因此,该运行结果与1等效,结果:随机值;
6、&arr+1;&arr代表数组指针,+1代表跳过一个数组,表示从该位置统计字符,由于‘\0’依旧不明确,结果:随机值;
7、&arr[0]+1;arr先于[ ]结合srr也就代表数组首元素地址,&arr[0]+1,表示是下标为1的位置处的地址,也就是从该位置开始统计字符个数,同样是无法预料’\0’位置,结果也即是:随机值;
由于3、4会造成编译器崩溃,我们先测试3、4:
接下来我们测试能正常输出的:
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));
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;
}
1、数组名单独放在sizeof内部,数组名代表整个数组,故sizeof计算的是整个数组的大小,结果:7;
2、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,结果:4/8;
3、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,结果:1;
4、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,结果:1;
5、数组名没有单独放在sizeof内部,&和数组名结合,此时数组名代表数组,但还是指针,结果:4/8;
6、数组名没有单独放在sizeof内部,&和数组名结合,此时数组名代表数组,&arr+1跳过一个数组,但还是指针,结果:4/8;
7、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,&arr[0]+1,但还是指针,结果:4/8;
8、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,字符串又是以‘\0’结尾的,我们明确直到‘\0’位置,结果:6;
9、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,字符串又是以‘\0’结尾的,我们明确直到‘\0’位置,结果:6;
10、数组名没有单独放在sizeof内部,&也没和数组名结合,此时数组名代表数组首元素地址,*arr表示访问下标0的元素,即为:‘a’,阿斯克码值为97,也就是我们要求strlen从地址为97的位置开始计算,此时编译器会报错,因为我们非法访问内存了,97地址的空间不属于我们,属于操作系统,我们无权对其进行读写!结果:err;
11、同10;
12、&arr;代表整个数组,也就是指向该数组的指针由于指向该数组的指针与数组首元素地址在值的方面上是一样的,因此,该运行结果与1等效,结果:6;
13、&arr+1;&arr代表数组指针,+1代表跳过一个数组,表示从该位置统计字符,由于‘\0’不明确,结果:随机值;
14、&arr[0]+1;arr先于[ ]结合srr也就代表数组首元素地址,&arr[0]+1,表示是下标为1的位置处的地址,也就是从该位置开始统计字符个数,预料的到’\0’位置,结果即是:5;
由于10、11会造成编译器崩溃,我们先测试10、11:
能输出结果的运行:
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));
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;
}
p里面存的是字符的首元素地址(不是数组名!!!):
1、p是一个char*指针,结果:4/8;
2、p+1,还是一个指针,结果:4/8;
3、*p访问字符串首元素,数组首元素为char类型,结果:1;
4、p[0],访问字符串首元素,数组首元素为char类型,结果:1;
5、&p是个二级指针,指向p这块空间,二级指针也是指针,结果:4/8;
6、&p是个二级指针,&p+1也是个二级指针,结果:4/8;
7、&p[0]+1,p先和[ ]结合,先访问,在取出该空间的地址,地址+1还是地址,结果:4/8;
8、p数组首元素,‘\0’位置明确,结果:6;
9、p+1,指向第二个元素,结果:5;
10、*p,非法访问,报错,结果:err;
11、同上;
12、&p,取出p空间的地址,从此位置统计字符,由于不知’\0’位置,结果:随机值;
13、同上;
14、&p[0]+1,从第二个元素开始统计字符,结果:5;
10、11运行结果:
可以运行处结果的:
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//12*4
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//16
printf("%d\n", sizeof(a[0] + 1));//4/8
printf("%d\n", sizeof(*(a[0] + 1)));//4
printf("%d\n", sizeof(a + 1));//4/8
printf("%d\n", sizeof(*(a + 1)));//16
printf("%d\n", sizeof(&a[0] + 1));//4/8
printf("%d\n", sizeof(*(&a[0] + 1)));//16
printf("%d\n", sizeof(*a));//16
printf("%d\n", sizeof(a[3]));//16
return 0;
}
我们直到二维数组是由多个相同类型的一维数组组成起来的:
1、数组名单独在sizeof内部使用,表示整个数组,计算的也即是整个数组的大小,结果:48;
2、数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,a[0] [0]表示0行0列的元素,元素为int型,结果:4;
3、数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,二维数组的首元素是一个一维数组,因此此时数组名表示一个一维数组指针,同时a[0]等价于
*(a+0);我们同时再想一想,我们是怎样得到一个数组指针的,是不是通过&数组名,现在我们对数组指针解引用,是不是就相当于访问数组名嘛,现在数组名单独放在sizeof内部,表示整个一维数组,这个一维数组有4个元素,因此大小为:16;
4、上面说了a[0]是第一个一维数组的数组名,现在数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,首元素地址加1还是地址,也就是a [0][1]的地址,结果:4/8;
5、a[0]是第一个一维数组的数组名,现在数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,首元素地址加1还是地址,首元素地址加1还是地址,也就是a [0][1]的地址,对其解引用自然访问的是a[0][1],结果:4;
6、数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,也就是一个数组指针,数组指针+1,跳过一个数组,但还是指针,结果:4/8;
7、数组名没有单独放在sizeof内部,也没有&,此时数组名表示首元素地址,也就是一个数组指针,数组指针+1,跳过一个数组,但还是指针,也就是第二个一维数组的数组指针,我们对其解引用,自然拿到的就是第二个一维数组的数组名,数组名单独放在sizeof内部,自然计算的是整个数组大小:16;
8、&a[0] + 1;数组名表示首元素地址,也就是第一个一维数组的数组指针,[0],表示拿到第一个一维数组的数组名,现在我们再对数组名&又拿到了第一个一维数组的数组指针,在+1,跳过一个数组,拿到第二个一维数组的数组指针,既然是指针,结果自然就是:4/8;
9、* (&a[0] + 1)),接着上面,我们拿到了第二个一维数组数组指针,对其解引用自然拿到的就是第二个一维数组的数组名,数组名单独放在sizeof内部表示整个数组,结果自然就是:16;
10、*a,数组名表示首元素地址,也就是第一个一维数组的数组指针,对其解引用,拿到的自然是第一个一维数组的数组名,数组名单独放在sizeof内部,表示整个数组,结果:16;
11、虽然a[3]已经越界了,但是我们并没有去访问它,所以不算是越界访问,此时编译器把它当作与合法范围内的一维数组相同类型的数组,a[3]拿到的依旧是第4个一维数组的数组名,数组名单独放在sizeof内部,表示整个数组,结果:16;
运行结果:
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));//2,5
return 0;
}
数组名与&结合表示整个数组指针+1跳过整个数组,是个数组指针,现在被强转为int*;
运行结果:
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);//作对照
printf("%p\n", p + 0x1);//100014
printf("%p\n", (unsigned long)p + 0x1);//100001
printf("%p\n", (unsigned int*)p + 0x1);//100004
return 0;
}
p是一个结构体指针,故p+1,跳过其所指向类型大小,故p+0x1,实际上是+20;
p原本是个指针,现在被强转为无符号长整型,也就是是个普通整数,普通整数+1,实际上就是+1;
p原本是个结构体指针,现被强转为int*指针,指针+1跳过其所指类型大小,实际上p+1,加的其实是20;
运行结果:
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((unsigne int)a + 1);
printf("%x,%x", ptr1[-1], *ptr2);
return 0;
}
ptr1:&a表示数组指针,数组指针+1,跳过一个数组,现在被强转为int*
ptr2:数组名表示首元素地址,被转换为无符号整型在+1,也就是普通加一,也就是跳过一个字节;
运行结果:
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];
printf("%d", p[0]);//1
return 0;
}
我们在数组中发现了逗号表达式,我们先对逗号表达式处理一下,
int a[3][2]={1,3,5};
a[0],表示第一个一维数组的数组名,此时数组名没有单独放在sizeof内部,也没有&,因此数组名表示第一个一维数组首元素地址,p[0]访问第一个一维数组的第一个元素,也就是1;
运行结果:
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;
}
a单独使用表示数组首元素地址,也就是int ()[5]类型;
p也是一个数组指针,int(*)[4]类型;
p=a,会有警告,但是语法没错;
a+1,实际跳过20个字节;
p+1,实际跳过16个字节;
a[4]跳过80个字节,解引用,表示数组名;
p[4]跳过64个字节,解引用,表示数组名;
a[4][2],在a[4]的基础上再跳24个字节;
p[4][2],在p[4]的基础上再跳2*4个字节;
故实际上a[4][2]的地址:88
故实际上p[4][2]的地址:72;
故&p[4][2] - &a[4][2]=-4;(指针相减表示指针之间元素个数)
%p打印无符号整数,%d打印有符号整数;
运行结果:
小试牛刀:
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));//10,5
return 0;
}
#include
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解决这个体的最好办法就是画图:
运行结果:
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;
}
有了上一题的基础,这一题就会简单很多:
还是画图:
运行结果: