把C语言指针练习一网打尽

一 、指针与数组经典练习题及解析

10个printf,请大家先自己做一遍之后再翻看答案

第一组

	int a[] = {1,2,3,4};
	printf("%d\n",sizeof(a));//1
	printf("%d\n",sizeof(a+0));//2
	printf("%d\n",sizeof(*a));//3
	printf("%d\n",sizeof(a+1));//4
	printf("%d\n",sizeof(a[1]));//5
	printf("%d\n",sizeof(&a));//6
	printf("%d\n",sizeof(*&a));//7
	printf("%d\n",sizeof(&a+1));//8
	printf("%d\n",sizeof(&a[0]));//9
	printf("%d\n",sizeof(&a[0]+1));//10

答案解析:
     首先第一个printf ,sizeof (数组名) ,数组名单独放在sizeof 操作符中计算的是整个数组的大小,又因为是int类型的数组,有4个元素 ,很容易得出是16个字节
     第二个printf,sizeof(a+0),数组名表示的 是数组首元素的地址,首元素的地址加 0 ,得到的还是首元素的地址, 故就是sizeof 计算首元素的地址,地址的字节大小不论类型,只在乎它的编译环境,64位8个字节32位4个字节
    第三个printf,sizeof(*a), *很数组名a表示的是数组首元素地址,解引用操作,得到首元素1,sizeof (1), 1 是int类型,故得4个字节
     第四个printf,sizeof(a + 1) ,根据第二个printf, 可知 a + 1 是第二个元素的地址,地址的大小不管大小,由编译器决定,故得4 或 8 个字节
     第五个printf , sizeof( a[1]),这就很容易啦, 计算的是第2个元素的大小,第二个元素是int 类型,故答案是4个字节
     第六个printf, sizeof(&a),&a 取的是整个数组的地址,但整个数组的地址仍是地址,得4个字节或8个字节
    第七个printf ,sizeof(*&a),先对a进行取地址,取得的是整个数组的地址, 如果将这个地址存放到指针变量p当中,这个p指针的类型应当是int (*)[],是一个数组指针, 故对这个地址解引用操作,得到的是整个数组,故得答案16
     第八个printf ,sizeof(&a+1),&a 得到的是整个数组的地址,对其加1 ,则越过啦整个数组,但归其本质仍是一个地址,故答案是4 或 8 个字节
     第九个printf , sizeof (&a[0]),[]的优先级高于*,故数组名a 先与[] 结合,a[0] 得到的是第1个元素 ,int类型的1 ,再对其&取地址操作, 得到的是第一个元素的地址,地址的答案就是 4或8个字节
    第十个printf ,sizeof(&a[0] + 1),在第九个printf的基础上,可知&a[0] 是第一个元素的地址, &a[0]+1就是第二元素的地址,但地址还是地址,答案自然就是4或8个字节
在这里插入图片描述

第二组

//字符数组
	char arr[] = {'a','b','c','d','e','f'};
	printf("%d\n", sizeof(arr));//1
	printf("%d\n", sizeof(arr+0));//2
	printf("%d\n", sizeof(*arr));//3
	printf("%d\n", sizeof(arr[1]));//4
	printf("%d\n", sizeof(&arr));//5
	printf("%d\n", sizeof(&arr+1));//6
	printf("%d\n", sizeof(&arr[0]+1));//7

答案解析
    第一个printf , sizeof (arr),由上题可知,sizeof (数组名),计算的是整个数组的大小,又数组的每个元素是char类型, 故很容易就得到6个字节
    第二个printf, sizeof(arr+0) ,arr表示的是数组首元素的地址,arr + 0 还是数组首元素的地址,但地址还是地址,地址大小由编译器决定, 故得到答案4或8个字节
    第三个printf , sizeof(*arr),arr表示的是字符首元素的地址,对其解引用得到的是字符’a’, 对其求大小,固然是1个字节
    第四个printf, sizeof(arr[1]), arr[1] 得到 ‘a ’ ,故答案是1个字节
     第五个printf , sizeof(&arr),&arr,对数组名取地址得到的是整个数组的地址,但地址还是地址,不由其类型决定,故答案是4或8个字节
    第六个printf, sizeof(&arr+1) , 在第五个printf的基础上,&arr再加1, 得到的是越过整个字符数组的下一个字节的地址,但地址还是地址,答案4或8
     第七个printf , sizeof(&arr[0]+1),&arr[0]取到的是第一个元素的地址,但还是那句话,地址还是地址,不由其类型决定,故答案是4或8个字节
在这里插入图片描述

第三组

	char arr[] = {'a','b','c','d','e','f'};
	printf("%d\n", strlen(arr));//1
	printf("%d\n", strlen(arr+0));//2
	printf("%d\n", strlen(*arr));//3
	printf("%d\n", strlen(arr[1]));//4
	printf("%d\n", strlen(&arr));//5
	printf("%d\n", strlen(&arr+1));//6
	printf("%d\n", strlen(&arr[0]+1));//7

注意,这里输出的是strlen函数,请区别于sizeof操作符,以下源码是strlen库函数的实现。请大家关注3点,第一点传进来的参数是一个char * 类型的指针变量,第二点判断条件是指针遇到’\0’跳出循环,第三点返回值是int 类型, 值为eos - str - 1 ,表示并不把‘\0’计算string字符串长度在内。

size_t __cdecl strlen (const char * str)
{
        const char *eos = str;

        while( *eos++ ) ;

        return( eos - str - 1 );
}

答案解析 :
    第一个printf ,arr表示的数组首元素地址 ,当arr传进strlen函数里执行循环体,因为字符串数组本身没有’\0’,故知道指针eos遍历到数组末尾越界还没有停止循环,只有当指针遇到内存中恰好有一块内存是’\0‘,才停止循环,此时因为我们并不知道内存的下一个’\0‘,即我们并不知道指针eos会指向那块内存空间。故返回一个随机值
    第二个printf,arr表示的数组首元素的地址,arr + 0 也还是首元素的地址,传进strlen函数,结果跟第一个一样的结果 , 随机值
    第三个printf, arr是数组首元素地址,对它解引用之后,*arr得到一个’a’ ,因为’a’ 的ASCII码值是97,用二进制表示的是0x00000601,将该值传进strlen函数中,此时指针str将该值当成地址传给eos,eos根据str给定的值访问,将会报访问异常,非法访问,程序出现错误
    第四个printf , 跟第三个printf同样的道理,将arr[1],即第二元素b的传给指针str ,程序将会报错,访问异常
     第五个printf, strlen (&arr) , &arr得到的是整个数组的地址,但是他的值跟数组首元素的地址是一模一样的,故仍然是个随机值
    第六个printf, strlen(&arr+1),&arr得到的是整个数组的地址,&arr+1 之后得到的是下一个数组大小相同的地址,尽管这个数组并未定义,但指针就指向那个位置,返回的仍然是个随机值,但这个随机值与第四、第五个随机值有数值关系,相差一个数组大小的字节数,即随机值-6。
    第七个printf,取得是第一个元素的地址,&arr[0] + 1 之后是第二个元素的地址,将其传给strlen函数时,得到的值仍然是个随机值,与第四第五的随机值关系是随机值-1。
在这里插入图片描述

第四组

注意 : 用双引号”abcdef“,与上一组不同的是在字符串的尾部有‘\0‘。

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//1
	printf("%d\n", sizeof(arr+0));//2
	printf("%d\n", sizeof(*arr));//3
	printf("%d\n", sizeof(arr[1]));//4
	printf("%d\n", sizeof(&arr));//5
	printf("%d\n", sizeof(&arr+1));//6
	printf("%d\n", sizeof(&arr[0]+1));//7

答案解析 :
    第一个printf ,将arr单独放到sizeof当中 ,计算的是整个字符数组的大小,故得7个字节,别忘啦’\0‘也算一个字节。
    第二个printf,arr表示的是arr数组的首元素地址,arr + 0 仍是第一元素的地址,只要是地址,大小由编译器决定得4或8个字节
     第三个printf,arr表示的是数组首元素的地址,若将其存放在一个指针变量当中,该指针变量的类型应是char *类型,对其解引用就是元素a,故答案是1个字节
    第四个printf,arr[1]得到的是第二个char类型元素b,很容易求到他的字节大小1
    第五个printf,&arr得到的是整个数组的地址,还是那句话,不管这个地址是什么类型的,他的大小只有4个或8个字节
    第六个printf,&arr + 1 同理得到的是arr后一个同样大小的字符数组的地址,但还是地址,故答案4或8个字节
    第七个printf,&arr[0] + 1 表示的是第二个元素b 的地址, 同样是4 或8 个字节
在这里插入图片描述

第五组

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//1
	printf("%d\n", strlen(arr+0));//2
	printf("%d\n", strlen(*arr));//3
	printf("%d\n", strlen(arr[1]));//4
	printf("%d\n", strlen(&arr));//5
	printf("%d\n", strlen(&arr+1));//6
	printf("%d\n", strlen(&arr[0]+1));//7

答案解析
    第一个printf,根据前面源码代码实现,很容易得到答案6
    第二个printf.arr表示的是数组首元素的地址,arr+0仍然是第一个元素的地址,故答案同第一个printf一样,传给strlen函数一样值,答案6
    第三个printf,arr表示的是数组首元素的地址,对其解引用*arr得到的是字符a ,将其传给strlen 参数str ,程序报错,非法访问
    第四个printf,跟第三个printf一样,得到是第二个字符b,传给strlen,将其二进制的值作为地址传给指针strlen, 程序报错,非法访问
    第五个printf,&arr虽然它取得是整个字符数组的值,但它的值同第一个元素的值是一样的,故答案同第一个printf一样是6个字节
    第六个printf,&arr+1 ,指针越过整个字符数组,取得的是同arr字符数组相同大小的下一数组的地址,故指针不知道在什么地方会停止循环,结果返回随机值
    第七个printf,&arr[0]取得的是第一个元素a的地址,&arr+1就是第二个元素b 的地址。故比第一个printf少1 ,得 5个字节
在这里插入图片描述

第六组

	char *p = "abcdef";
	printf("%d\n", sizeof(p));//1
	printf("%d\n", sizeof(p+1));//2
	printf("%d\n", sizeof(*p));//3
	printf("%d\n", sizeof(p[0]));//4
	printf("%d\n", sizeof(&p));//5
	printf("%d\n", sizeof(&p+1));//6
	printf("%d\n", sizeof(&p[0]+1));//7
	

答案解析:
    第一个printf ,指针就是地址,故指针p的字节大小为4或8个字节
    第二个printf,p指向的是第一个元素a的地址,p+1之后得到的是第二个元素b的地址,但地址还是地址,故得4或8个字节
    第三个printf,p指向的是第一个元素的地址,对其解引用之后,得到的是第一个元素a,而元素a是char类型,故得1个字节
    第四个printf,同第三个printf一样,得到的是元素a,得1个字节
    第五个printf,对指针变量p取地址,取得是二级指针的地址,但二级指针仍然是地址,故得4/8个字节
    第六个printf,在第五的基础上&p + 1,得到的地址可如图所示,得到的是存放指针p地址的相邻的下一个空间。但不管什么地址,字节大小为4/8个字节
    第七个printf,p[0]得到的是第一个元素a,又对其取地址,得到的是a的地址,也就是第一个元素的地址,&p[0] + 1 加1之后得到的就是第二个元素的地址,还是那句话,地址始终是地址,得4 / 8 个字节
在这里插入图片描述

第七组

	char *p = "abcdef";
	printf("%d\n", strlen(p));//1
	printf("%d\n", strlen(p+1));//2
	printf("%d\n", strlen(*p));//3
	printf("%d\n", strlen(p[0]));//4
	printf("%d\n", strlen(&p));//5
	printf("%d\n", strlen(&p+1));//6
	printf("%d\n", strlen(&p[0]+1));//7

    第一个printf , p的值是第一个元素a的地址,传给strlen函数后,得到的是字符串的长度6
    第二个printf,p传给strlen函数的是第二个元素b的地址,得到的字符串的长度5
    ,第三个printf,p对p解引用,得到的是元素a,将a在内存中的二进制传给strlen 中的char * str 参数时,程序报错,非法访问
    第四个prinf,同第三个printf一样,将a在内存中的二进制的值传给strlen函数,程序报错,非法访问
     第五个printf,&p,得到的一个二级地址,若用指针来存储这地址,应当是char
* , 如图7-1,由图易得一个随机值
    如图7-2,由图易得一个随机值
    第七个printf,&p[0]得到的是第一个元素a的地址,&p[0]+1之后得到的是第二个元素b的地址,将b的地址传进strlen函数中,得到长度5

第八组

	//二维数组
	int a[3][4] = {0};
	printf("%d\n",sizeof(a));//1
	printf("%d\n",sizeof(a[0][0]));//2
	printf("%d\n",sizeof(a[0]));//3
	printf("%d\n",sizeof(a[0]+1));//4
	printf("%d\n",sizeof(*(a[0]+1)));//5
	printf("%d\n",sizeof(a+1));//6
	printf("%d\n",sizeof(*(a+1)));//7
	printf("%d\n",sizeof(&a[0]+1));//8
	printf("%d\n",sizeof(*(&a[0]+1)));//9
	printf("%d\n",sizeof(*a));//10
	printf("%d\n",sizeof(a[3]));//11

答案解析 :
    第一个printf : a 单独放入sizeof中, 故计算的是整个二维数组的大小,得到答案48个字节
    第二个printf :a[0][0] 是第一行第一列的元素,元素的类型是int型,故sizeof (int) 得 4个字节
    第三个printf: 可以将a[3][4] 看成一个 3个连续的元素个数为4的一维数组,那么a[0] 就是第一个一维数组的数组名,将一维数组的数组名单独放在sizeof中,那么计算就是整个一维数组的字节大小就是 4 * 4 = 16 个字节
     第四个printf: a[0] 表示的是第一行一维数组首元素的地址,故a[0]+1 是第一行第二个元素的地址, 但其仍然是个地址, 得 4/8个字节
     第五个printf :由第四个printf可知,a[0]+1 是第一行第二个元素的地址,对其解引用,就得到啦第一行第二个元素, 故得 其大小4 字节
     第六个printf : a表示的是二维数组首元素的地址,即第一行数组的地址, a + 1 表示的是第二行数组的地址,即a[1],故该字节大小为4/8个字节
    第七个printf : 由第六个printf知,a + 1 表示的是第二行数组的地址,即a[1]的地址, 若将其用指针将其存起来,指针的类型为int (*) [] ,对它解引用,得到的是整个一维数组元素,故字节大小为 16个字节
    第八个printf: &a[0] 得到的是第一行整个元素的地址,对其加1 ,也就得到啦第二行整个数组的地址,但地址还是地址, 字节大小为== 4/8 ==
    第九个printf:在第八的基础上 ,解引用得到的是第二行整个一维数组的元素,字节大小为16
    第十个printf: a 表示二维数组首元素的地址,即第一行一维数组的地址,对其解引用得到的是整个一维数组的元素,故其字节大小 为16
     第十一个printf : a[3] 很明显越界啦, 但程序并不会报错,因为sizeof只需要知道它要计算的元素类型,并不会去访问该内存空间是什么值,故 答案 16个字节
在这里插入图片描述

总结: 数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。(另外需要提醒一点, 二维数组的数组名
表示的是第一行整个一维数组的地址)。
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。 如下表表达式的值分别为多少?
int main()
{
   printf("%p\n", p + 0x1);
   printf("%p\n", (unsigned long)p + 0x1);
   printf("%p\n", (unsigned int*)p + 0x1);
   return 0; 
   }

答案解析:
    第一行printf : p为结构体的指针,指针类型为struct Test *,它加1指针地址需要向后移动20个字节,故用%p打印,为0x100014。
     第二行printf: p被强转为整形, 故它 加1 就是普通的+ 1 , 用%p 打印就是0x100001。
    第三行printf: p被强转成整形指针,故整形指针加1 ,地址向后移动4个字节,用%p打印就是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; 
}

在这里插入图片描述
在这里插入图片描述

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

答案解析:
       这题主要是考的是逗号表达式,注意{}里面是()小括号,(0,1),(2,3),(4,5),逗号表达式的结果是1 , 2 , 3,将其存入二维数组,如图所示。
在这里插入图片描述

在这里插入图片描述

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

图片解析:

在这里插入图片描述
在这里插入图片描述

#include <stdio.h>
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;
 }

图片解析:
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值