江南可采莲,莲叶何田田。鱼戏莲叶间。鱼戏莲叶东,鱼戏莲叶西,鱼戏莲叶南,鱼戏莲叶北。 — 两汉·汉乐府《江南》
这篇博客我们将会讲解一些习题,习题是有关于数组和指针的,数组方面的习题也能帮助我们更好的理解sizeof和strlen,指针的习题也全方位锻炼我们对指针的理解。
//一维数组
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));
解析
16
在32位下是4,在64位下是8。
4。
求地址的大小在32位下是4,在64位下是8。
4
32位下是4,在64位下是8。
16
32位下是4,在64位下是8。
32位下是4,在64位下是8。
32位下是4,在64位下是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));
}
解析
6。
32位下就是4,64位下就是8。
1。
1。
32位下就是4,64位下就是8
。32位下就是4,64位下就是8
。32位下就是4,64位下就是8
。来看看strlen
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));
}
数组里面放的是字符并不是字符串
,那怎么办呢?其实\0的ASCII码值就是0(字符'0'的是48),
也就是说strlen会从数组首元素地址开始从内存中找0,但我们是不知道什么时候能找到的,所以是一个随机值
。随机值。
error
error。
随机值
是相当的。-1。
吗?,注意这里是&arr,我们知道&arr的类型数组指针,也就是要跳过一个数组,所以这里一个是随机值-6
-1
。接下来就是字符串了。
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));
}
7。
32位下是4,64位下是8。
1。
1。
32位下是4,64位下是8。
32位下是4,64位下是8。
32位下是4,64位下是8。
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));
然后是剩下的strlen的。
6。
6。
error
error。
6
个字节。随机值。
5。
然后来看看指针与字符串结合。
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));
32位下是4,64位下是8。
32位下是4,64位下是8。
1。
1。
32位下是4,64位下是8。
32位下是4,64位下是8。
32位下是4,64位下是8。
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));
6。
5。
error
error。
随机值。
肯定会出现随机值,但是不一样就会与上面这个题的随机值一样。
5。
这就是数组的最后一道题。需要仔细的讲讲
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(a):这里是sizeof中出现数组名的情况,求的是整个数组的大小,数组3*4的个数是12个元素,类型都为int,12 *4 = 48 。
sizeof(a[0][0]):这是就是二维数组中第一个元素的写法,第一个元素类型为int,大小就是4。
sizeof(a[0]):这个就不一样了,我们说a[0]
其实也可以写成这样*(a+0)
,a
为数组名,数组名代表首元素地址,但是在二维数组中数组名代表第一行的地址,
也就是说这个地址+1的话会跳过一行,而不是一个元素。但是这里只是+0就还是不变,我们知道a代表第一行的地址(把第一行看成是一维数组
),和我们的一维数组的&arr是一样的
。都是拿到整个一维数组的地址
。那么其实a+0可以看成就是&arr,为什么呢?因为都是拿到整个数组的地址,并且都是一维数组。那么*(a+0),就可以看成是 * (&arr),
而*又可以与&抵消就得到arr
,这个arr我们就直接看成是第一行的数组名****就行。那么这道题的意思就是求第一行的总大小,是[4]*4=16。
其实你也可以这样记忆,sizeof(a[0])代表求第一行的所有元素大小。
那么a[0]就可以看成是第一行的数组名,并且是第一行的地址。
sizeof(a[0] + 1):a[0]作为第一行的数组名,并没有单独放在sizeof内部,也没有被取地址,按上面的分析方法a[0]代表第一行的数组名也是第一行的地址,所以a[0]就是数组首元素的地址,就是第一行第一个元素的地址,a[0]+1就是第一行第二个元素的地址,是地址那么在32位下是4,64位下是8。
sizeof(*(a[0] + 1)):a[0]+1就是第一行第二个元素的地址,解引用得到第二行第一个元素,大小为4。
sizeof(a + 1):a代表第一行地址,+1跳过第一行,但是还是地址,是地址那么在32位下是4,64位下是8。
sizeof(*(a + 1)):对第二行的地址解引用得到第二行,也可以写成这样:a[1],根据我所理解的,a[1]看成是第二行的数组名,并且是第二行的地址。解引用就得到第二行。大小为16。
sizeof(&a[0] + 1):a[0]我们知道是第一行的数组名对数组名取地址相当于拿到整个数组,然后+1跳过一行,但也还是地址,是地址那么在32位下是4,64位下是8。
sizeof(*(&a[0] + 1)):与多加了一个 *,代表对第二行的地址解引用,拿到第二行,对第二行求大小得16。
sizeof(*a):a代表二维数组数组名,也是第一行的地址,对第一行的地址解引用得到第一行。第一行大小为16。
sizeof(a[3]):最后是这个,我们看到我们行数最大也为2,为什么这里写3呢?
这个代码是错误的吗?
int a[3][4] = { 0 };
其实不是,我们要知道一个知识点
, sizeof在进行计算类型的大小时,它不会去看你括号里写的东西存不存在而是直接根据类型推得大小,a[3]就是第三行的数组名,即使没有第三行也会计算,数组名放在sizeof里得到第三行的大小,也是16。
小总结
二维数组中 :
a +1的+1是要跳过一行。(a是第一行的地址+1跳过一行,并且a是相对于二维数组而言)
a[0] +1的+1是要跳过一个元素。(a[0]是第一行的数组名,一维数组的数组名+1就跳过一个元素)
&a[0]+1的+1是要跳过一行。(&是拿到整个一维数组的地址再+1,肯定就跳过一行了)。
一 :
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
程序的结果是什么?
分析
//结构体的大小是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的值是0x100000,p+0x1
呢? (unsigned long)p + 0x1); (unsigned int*)p + 0x1)
又是多少?
struct Test *p;
。0x100014。
(unsigned long)p + 0x1);
这里就改变的p的类型,之前是结构体指针现在强转为u long了,而u long咱们就把p不再看作一个指针变量了,就是一个普通的变量,相当于 int i = 0; i + 1。 0x100001。
(unsigned int*)p + 0x1)
,与上面一样,都是改变了指针的类型,u int *的大小也是 4个字节(32位),那么+1也是跳过4个字节。也相当于0x100000+0x4。0x100004。
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;
}
输出的结果是什么?
分析:
我们a是4个元素的整型数组, ptr1指向的是&a+1的地址处
,就是跳过一个数组,这里强转是因为&a的类型相当于int (*)[4],
最后将数组指针转为int *。
ptr2就不一样的了,a是数组名也是首元素地址,也可看作是一个int *类型
,但是这里强转了,转为int 就相当于变成普通的变量加减
,而int *+1跳过4个字节
,int 类型+1,只跳过一个字节,
最后再把类型转为int*就行了。
图
四
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
输出的结果是什么?
分析:
这个就比上面的简单一些了,考察二维数组的知识点,
首先这个题目有个坑,
在二维数组初始化的时候,用(),就相当于逗号表达式,
实际上应该是这样的, int a[3][2] = { 1, 3, 5};
我们知道a[0]就是代表第一行的数组名,数组名也是首元素地址,p[0]就相当于*(p +0),
对p 解引用得到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,有定义了一个数组指针要十分的注意这个数组指针指向的数组看作是4个元素的。
p这个指针变量存放的a,a又代表着二维数组数组第一行的地址。
4。
六
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;
}
输出的结果是什么?
分析:aa是个二维数组的两行一共十个元素。
&aa + 1:aa是二维数组数组名对二维数组的数组名取地址就是拿到整个数组的地址,整个数组的地址+1,就跳过整个二维数组。
*(aa + 1):可以写成这样aa[1],相当于第一行的地址+1再解引用得到第二行的地址,aa[1]也就相当于数组名。
七
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
输出的结果是什么?
分析:有一个字符指针数组,里面有三个元素,又有一个二级指针存储a的首元素的地址,因为a的首元素是char *类型存储的是字符串的首字符地址。pa++就意味着pa中存储的地址+1,就到了数组的下一个元素,下一个元素是’‘at’'的首字符地址,故输出的是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;
}
输出的结果是什么?
分析:有一个字符指针数组。数组的每个元素是相应字符串的首字符地址。
而cp这个二级指针数组存放的是char *类型的地址,刚好c数组的每个元素是char *,最后cpp这个三级指针指向的是二级指针数组的首元素地址。
printf("%s\n", **++cpp);
,首先cpp先永久+1,也就是移动char ***个步长也就是1,得到指向c+2地址处,再解引用得到c+2,而c+2是c这个数组中的地址,再解引用得到"POINT"。
*-- * ++cpp + 3
:首先我们还是要明确理解的顺序,先cpp永久+1,再解引用,得到c+1,而c+1是c数组中的地址,然后再–,所以改变了c+1的值变成了c,然后再解引用得到"E"的地址,然后再加3,这时候就代表着"ENTER"这个字符串加3,ENTER我们可以理解成就是E的地址再+3,结果就得到"ER"。
int main()
{
printf("%s", "abcde" + 3);
}
*cpp[-2] + 3
:首先我们拆解一下,* (*(cpp-2))+3,首先cpp-2,但是不是永久的,然后再解引用,上面我们cpp指向的是下标为2的位置,现在应该指向0,解引用得到c+3,再解引用得到F的地址,F的地址+3得到"ST";
cpp[-1][-1] + 1
:首先还是拆解一下,* (*(cpp-1)-1)+1,cpp-1指向c+2,解引用得到c+2,c+2然后要-1得到c+1,c+1解引用得到"NEW",再+1输出"EW"。
这篇博客篇幅较长是因为先要深入的理解数组与地址与指针的关系是不太容易的,所以我们需要题目来练习,在数组方面我们让大家理解了数组名±或者&的一些差异,也包括一些类型的不同,指针方面也比较全面是各种类型的杂糅为的就是锻炼自己的理解能力。 完(๑′ᴗ‵๑)