C语言指针的深入讲解(二)(习题讲解)

文章详细分析了C语言中指针与数组的关系,包括sizeof运算符在不同情况下的结果,strlen函数的使用以及涉及字符串和二维数组的指针操作。同时,提供了几道笔试题,解释了每道题目的解题思路和答案,强调了指针在内存地址和数据访问中的关键作用。
摘要由CSDN通过智能技术生成

目录

 

 

1.指针和数组的相关题目

2.指针笔试题

3.答案


 

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;
}

 3942e2ed16644632a906bebefc320120.pngbadef45120ab4858a2c6d79ad4493b83.png

所以说这里打印的结果是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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值