C语言指针详解(3)———指针题目,你确定你学会指针了?进来看看吧!(几十个指针小题+超详解)

你确定你学会指针了? 你确定你明白数组名了?
如果你觉得你学的还不错,就进来看看吧,相信你看完之后一定能收获更多。

数组名的理解一定要弄清楚
数组名是数组首元素的地址 但是有2个例外:

  1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
  2. &数组名,这里的数组名表示整个数组,&数组名取出的是数组的地址

下面是常见 指针和数组笔试题及解析 (解析以注释的形式写在代码当中)
下面题目的类型都是给你一个数组,你来判断printf到底输出什么。

int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };//4个元素,每个元素4个字节(int类型)
	printf("%d\n", sizeof(a));//16   
	//数组名a单独放在sizeof内部,数组名表示整个数组,计算的是整个数组的大小单位是字节,是16字节
	
	printf("%d\n", sizeof(a + 0));//4
	///a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+0还是首元素的地址

	printf("%d\n", sizeof(*a));//4
	//a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,*a是首元素,相当于a[0]

	printf("%d\n", sizeof(a + 1));//4
	//a并非单独放在sizeof内部,也没有&,所以数组名a是数组首元素的地址,a+1就是第二个元素的地址,相当于a[1]

	printf("%d\n", sizeof(a[1]));//4
	//a[1]就是数组的第二个元素,这里计算的就是第二个元素的大小

	printf("%d\n", sizeof(&a));//4
	//是取出数组的地址,但是数组的地址也是地址,是地址就是4/8个Byte,数组的地址和数组首元素的地址的本质区别是类型的区别,并非大小的区别

	//a  -- int*             int * p = a;
	//&a -- int (*)[4]       int (*p)[4] = &a;

	printf("%d\n", sizeof(*&a));//16
	//对数组指针解引用访问一个数组的大小,即*(解引用)和&(取地址)可以相互抵消,sizeof(*&a)==sizeof(a)

	printf("%d\n", sizeof(&a + 1));//4
	//&a数组的地址,&a+1还是地址,此时指向的是数组中a[3](4)后面的地址(数组指针+1跳过一个数组)

	printf("%d\n", sizeof(&a[0]));//4
	//&a[0]是首元素的地址

	printf("%d\n", sizeof(&a[0] + 1));//4
	//&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址

	return 0;
}
int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };//6个字符
	printf("%d\n", sizeof(arr));//6
	//数组名arr单独放在sizeof内部,计算的是整个数组的大小

	printf("%d\n", sizeof(arr + 0));//4
	//arr是首元素的地址==&arr[0],是地址就是4个字节,指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节

	printf("%d\n", sizeof(*arr));//1
	//arr是首元素的地址,*arr就是首元素

	printf("%d\n", sizeof(arr[1]));//1
	//第二个元素

	printf("%d\n", sizeof(&arr));//4
	//&arr是数组的地址,只要是地址就是4/8,sizeof(&arr)就是4/8个字节

	printf("%d\n", sizeof(&arr + 1));//4
	//&arr+1 是跳过数组后的地址,即f后的地址

	printf("%d\n", sizeof(&arr[0] + 1));//4
	//第二个元素的地址,是地址就是4/8
	
	//strlen是求字符串长度的函数,统计的是在字符串中\0之前出现的字符的个数
	printf("%d\n", strlen(arr));//随机值(>=6)
	//arr是首元素的地址,往后走不确定到哪里才能到\0,字符数组不像整型数组一样最后一个数字后面就是\0,字符数组的\0不知道在后面什么地方

	printf("%d\n", strlen(arr + 0));//随机值(>=6)
	//arr是首元素的地址, arr+0还是首元素的地址

	printf("%d\n", strlen(*arr));//非法访问,错误
	//arr是首元素的地址, *arr就是首元素 - 'a'的ASCLL码 - 97,站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址,直接进行访问,就是非法访问

	printf("%d\n", strlen(arr[1]));//非法访问,错误
	//'b' 的ASCLL码- 98,错误与上面相同

	printf("%d\n", strlen(&arr));//随机值(不知道/0在哪里)
	//取出的是整个数组的地址,&arr取出的类型是 char (*)[6](数组指针类型),strlen的参数类型是const char*,编译器会报警告但是不影响,类型发生了变化但是值不变。

	printf("%d\n", strlen(&arr + 1));//随机值
	//&arr+1,是f后的地址(跳过了整个数组),不知道什么时候遇到/0,所以也是随机值

	printf("%d\n", strlen(&arr[0] + 1));//随机值
	//一个字符的地址+1,是b的地址,后面不知道/0在哪里,也是随机值

	return 0;
}
int main()
{
	char arr[] = "abcdef";//a b c d e f /0

	printf("%d\n", sizeof(arr));//7
	//abcdef和/0一共七个元素

	printf("%d\n", sizeof(arr + 0));//4
	//首元素地址+0还是首元素地址

	printf("%d\n", sizeof(*arr));//1
	//首元素地址解引用,还是首元素a,一个字节

	printf("%d\n", sizeof(arr[1]));//1
	//第二个元素,一个字节

	printf("%d\n", sizeof(&arr));//4
	//数组的地址,为4/8

	printf("%d\n", sizeof(&arr + 1));//4
	//跳过整个数组的地址,为/0后的地址,是地址就是4/8

	printf("%d\n", sizeof(&arr[0] + 1));//4
	//第一个元素的地址+1,就是第二个元素的地址,是地址就是4/8

	printf("%d\n", strlen(arr));//6
	//首元素地址传给strlen,往后数因为有/0,所以是6

	printf("%d\n", strlen(arr + 0));//6
	//首元素地址+0,还是首元素地址,往后数,因为有/0,所以是6

	printf("%d\n", strlen(*arr));//非法访问,错误
	//对首元素地址解引用,其实就是字符a,传进去的是a的ASCLL码值97,会报错

	printf("%d\n", strlen(arr[1]));//非法访问,错误
	//传进去的是第二个元素b,穿的是b的ASCLL码,会报错

	printf("%d\n", strlen(&arr));//6
	//传进去的是首元素的地址,往后数,因为有/0,所以是6

	printf("%d\n", strlen(&arr + 1));//随机值
	//首元素的地址+1,跳过整个数组,即到/0之后,/0之后什么时候遇到下一个/0不知道,为随机值

	printf("%d\n", strlen(&arr[0] + 1));//5
	//第二个元素的地址,从b数到/0,有5个

	return 0;
}
int main()
{
	char* p = "abcdef";//a b c d e f \0,但p中存放的是a的地址(p能够找到abcdef/0)

	printf("%d\n", sizeof(p));//4
	//计算的是指针变量的大小

	printf("%d\n", sizeof(p + 1));//4
	//char*指针+1向后偏移一个,但p+1还是一个地址,是地址就是4/8

	printf("%d\n", sizeof(*p));//1
	//  *p=='a'

	printf("%d\n", sizeof(p[0]));//1
	//p[0]等价于*(p+0),等价于*p,即第一个元素a

	printf("%d\n", sizeof(&p));//4
	//&p仍然是一个地址,是地址就是4/8,假设p的地址是0x0012fc60,指向的就是0x00的地址,类型是char**二级指针

	printf("%d\n", sizeof(&p + 1));//4
	//&p是地址,&p+1还是地址,是地址就是4/8,假设p的地址是0x0012fc60,指向的就是0x60,类型是char**二级指针

	printf("%d\n", sizeof(&p[0] + 1));//4
	//指向第二个元素的地址,是地址就是4/8

	printf("%d\n", strlen(p));//6
	//a的地址往后数到/0

	printf("%d\n", strlen(p + 1));//5
	//b的地址往后数到/0

	printf("%d\n", strlen(*p));//非法访问,错误
	//p指向a的地址,*p就是a,传的a的ASCLL码,错误

	printf("%d\n", strlen(p[0]));//非法访问,错误
	//和*p一样的,是a,传的a的ASCLL码,错误

	printf("%d\n", strlen(&p));//随机值
	//&p假设取的是0x0012ff40,这个指针变量占4个字节,即从0x0012ff40往后找/0,后面什么位置有/0不知道

	printf("%d\n", strlen(&p + 1));//随机值
	//&p假设取的是0x0012ff40,&p+1指的是0x40后的地址,即从0x40往后找/0,后面什么位置有/0不知道

	printf("%d\n", strlen(&p[0] + 1))//5
	//第二个元素b的地址,往后数到/0,有五个

	return 0;
}
int main()
{
	//二维数组
	int a[3][4] = { 0 };

	printf("%d\n", sizeof(a));//48
	//数组名a单独放在sizeof内部,表示整个数组,一共3*4=12个元素,每个元素4个字节即12*4=48

	printf("%d\n", sizeof(a[0][0]));//4
	//a[0][0]是数组的第一行第一列的元素

	printf("%d\n", sizeof(a[0]));//16
	//二维数组其实是一维数组的数组,a[0]可以看成第一行的元素,a[1],a[2]可以看成二三行的元素,即整个第一行的大小,即4*4=16

	printf("%d\n", sizeof(a[0] + 1));//4
	//a[0]并非放在sizeof内部,所以a[0]表示数组首元素的地址,也就是第一行第一个元素的地址。a[0]+1表示a[0][1]的地址。是地址就是4/8

	printf("%d\n", sizeof(*(a[0] + 1)));//4
	//a[0]+1是a[0][1]的地址,解引用就是第二个元素

	printf("%d\n", sizeof(a + 1));//4
	//a作为二维数组的数组名,名没有单独放在sizeof内部,a就是首元素的地址,也就是第一行的地址。a+1就是第二行的地址,a的类型是一个数组指针int(*)[4],是地址就是4/8

	printf("%d\n", sizeof(*(a + 1)));//16
	//a+1是第二行的地址,解引用就是第二行  4*4=16

	printf("%d\n", sizeof(&a[0] + 1));//4
	//a[0]是第一行的数组名,&a[0]取出的是数组的地址,取出的是第一行这个一维数组的地址,类型就是int(*)[4],&a[0]+1就是第二行的地址,是地址就是4/8

	printf("%d\n", sizeof(*(&a[0] + 1)));//16
	//对第二行的地址解引用就是第二行,4*4=16

	printf("%d\n", sizeof(*a));//16
	//a没有单独放在sizeof内,a表示数组首元素的地址,也就是第一行的地址,解引用就代表第一行,4*4=16

	printf("%d\n", sizeof(a[3]));//16
	//a[3]表示第四行,按理说应该越界了,但是没有,sizeof是计算,不会去真的访问第四行,其相当于和a[0]一样,4*4=16

	return 0;
}

总结

指针和数组名在C语言中非常重要,它们是C语言中最基本的数据类型和数据结构之一。以下是指针和数组名的重要性:

1.内存管理:指针允许我们直接访问和操作内存中的数据。通过指针,我们可以动态地分配和释放内存,有效地管理内存资源。

2.数组访问:数组名本质上是一个指向数组第一个元素的指针。通过数组名,我们可以方便地访问和操作数组中的元素。

3.函数参数传递:通过指针作为函数参数,可以实现对函数外部变量的修改。这在需要在函数内部修改变量的值时非常有用。

4.字符串处理:C语言中的字符串是以null字符结尾的字符数组。通过指针和数组名,我们可以方便地处理字符串,比如拷贝、连接、比较等操作。

5.动态数据结构:指针可以用来创建和操作动态数据结构,比如链表、树等。通过指针,我们可以动态地创建和销毁节点,连接和遍历节点。

6.性能优化:在一些需要高效处理大量数据的场景中,通过指针和数组名可以减少内存拷贝和数据移动的开销,提高程序的执行效率。

总的来说,指针和数组名在C语言中扮演着非常重要的角色,它们是C语言实现高效、灵活和可靠的关键工具。在学习和使用C语言时,理解和掌握指针和数组名的概念和用法是非常重要的。

你可能感兴趣的:(c语言,开发语言,指针)