第一章 初识C语言
第二章 变量
第三章 常量
第四章 字符串与转义字符
第五章 数组
第六章 操作符
第七章 指针
第八章 结构体
第九章 控制语句之条件语句
第十章 控制语句之循环语句
第十一章 控制语句之转向语句
第十二章 函数基础
第十三章 函数进阶(一)(嵌套使用与链式访问)
第十四章 函数进阶(二)(函数递归)
第十五章 数组进阶
第十六章 操作符(详解及注意事项)
第十七章 指针进阶(1)
第十八章 指针进阶(2)
第十九章 指针进阶(3)
第二十章 指针进阶(4)
第二十一章 数据的存储(秒懂原、反、补码)
第二十二章 指针和数组笔试题详解(1)
随着前面的学习,我们对指针和数组都有了较深的理解,那么本章节将通过一些题目的讲解来巩固一下,同时加深我们对于知识点的记忆。
sizeof
中的时候,数组名代表的是整个数组。&
的时候,此时的数组名代表的是整个数组的地址。&
并且不是单独存在的时候,数组名代表的是首元素的地址。sizeof
的计算法则是看括号内的内容所占的数据内存,但是sizeof内的表达式是不进行计算的。strlen
函数的计算法则是寻找字符串中的\0
,从而返回\0和首元素之间的元素个数。请思考如下代码的打印结果:
//一维数组
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));
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//打印的是整个数组的大小——4x4=16
printf("%d\n",sizeof(a+0));//打印的是首元素的地址大小——4或8
printf("%d\n",sizeof(*a));//打印的是首元素的大小——int——4
printf("%d\n",sizeof(a+1));//打印的是第二个元素的地址大小——4或8
printf("%d\n",sizeof(a[1]));//打印的是第二个元素的大小——int——4
printf("%d\n",sizeof(&a));//打印的是整个数组的地址的大小——4或8
printf("%d\n",sizeof(*&a));//打印的是数组的大小——4x4=16,*&a==a,所以这个式子等价于sizeof(a);
printf("%d\n",sizeof(&a+1));//打印的是该数组的地址向后偏移一个数组的大小后的地址大小——4或8
printf("%d\n",sizeof(&a[0]));//打印的是首元素的地址——4或8
printf("%d\n",sizeof(&a[0]+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));
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//arr在这里代表的是整个数组的内存大小,所以是6x1=6
printf("%d\n", sizeof(arr+0));
//arr+0是指首元素的地址向后偏移0个字节后的地址的大小,所以这里指的是首元素的地址,输出的就是4或者8
printf("%d\n", sizeof(*arr));
//arr是首元素的地址,对其解引用,结果就是首元素,所以打印的结果就是1
printf("%d\n", sizeof(arr[1]));
//arr[1]是指数组中的第二个元素所占的内存的大小,所以打印的结果是1
printf("%d\n", sizeof(&arr));
//&arr取出的是整个数组的地址,所以依旧是求地址的内存大小,所以输出的结果就是4或者8
printf("%d\n", sizeof(&arr+1));
//&arr取出的是一个数组指针,+1是指从首元素向后偏移一个数组的长度后对应的地址的内存大小,所以还是4或者8
printf("%d\n", sizeof(&arr[0]+1));
//这里取出的是第一个元素的地址并将其向后偏移一个字节,即第二个元素的地址的内存,4或者8
分析下面代码最终的打印结果:
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));
char arr[]={'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));//strlen的遇到\0的时候才会停止计算,但是数组中没有\0,于是就会不断地向后寻找\0,所以此处是随机值
printf("%d\n", strlen(arr+0));//arr不是单独存在的,所以这里的arr代表的是首元素地址,同上,是随机值
printf("%d\n", strlen(*arr));//*arr,是对首元素的地址进行解引用操作,因此括号内的不是地址,而是首元素,所以系统报错
printf("%d\n", strlen(arr[1]));//同上,系统报错
printf("%d\n", strlen(&arr));//虽然&arr代表的是整个数组,但是其数值上依旧和数组的首元素地址一致,所以strlen函数也会从这个地址开始向后遍历,寻找\0,所以其结果依旧是随机值。
printf("%d\n", strlen(&arr+1));//从数组末尾元素的下一个地址,开始向后遍历,寻找\0,所以其结果依旧是随机值。
printf("%d\n", strlen(&arr[0]+1));//取出了第一个元素的地址,然后向后偏移一个元素的位置,所以括号内的就是第二个元素的地址,因此从这个位置开始寻找\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));
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
//arr单独存在于sizeof的内部,代表的是整个数组,同时由于利用双引号创建了一个字符串,所以内容中多了一个\0,因此其所占的内存空间是:(6+1)X 1 = 7
printf("%d\n", sizeof(arr+0));
//arr不是单独存在,所以其代表的是首元素的地址,所以是4/8
printf("%d\n", sizeof(*arr));
//arr不是单独存在,所以代表的是首元素地址,对其解引用,得到的是首元素,首元素是字符型,所以打印的结果是1
printf("%d\n", sizeof(arr[1]));
//arr[1]==*(arr+1);所以这代表的是第二个元素,所以打印的结果是1
printf("%d\n", sizeof(&arr));
//&arr是整个数组的地址,所以本质上是一个地址,因此结果是4或8
printf("%d\n", sizeof(&arr+1));
//&arr取出的是整个数组的地址,其指针类型是数组指针,所以对其加一操作后,跳过的是一个数组后的下一个紧邻的空间的地址,因此其本质上依旧是一个地址,所以是4/8
printf("%d\n", sizeof(&arr[0]+1));
//这个取出的是第二个元素的地址,4/8
printf("%d\n", strlen(arr));
//由于我们是用双引号的方式创建了一个字符串,所以在字符串的末尾包含\0,所以计算的结果是:6
printf("%d\n", strlen(arr+0));
//arr+0,也是指首元素的地址,所以结果同上
printf("%d\n", strlen(*arr));
//传入的是,首元素而不是地址,所以代码错误,会报错
printf("%d\n", strlen(arr[1]));
//同上,传入的是第二个元素,依旧会报错
printf("%d\n", strlen(&arr));
//传入的是整个数组的地址,而这个地址和数组首元素的地址在数值上是相同的,因此其结果也是6
printf("%d\n", strlen(&arr+1));
//&arr是整个数组的地址,其类型是数组指针,所以+1后会跳过整个数组,从最后一个元素的末尾后面的紧邻内存空间的地址开始向后访问,寻找\0,所以结果依旧是随机值
printf("%d\n", strlen(&arr[0]+1));
//传入的是第二元素的地址,所以结果是6-1=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));
char *p = "abcdef";
//首先上面的代码写的并不严谨,因为将一个字符型指针指向了一个字面常量,而常量是不允许修改的,所以这里应该用const进行修饰所以写成下面的这种形式,并且上述代码并不是将整个数组存储在p中,而是将数组的首元素的地址存储在p里面。
const char*p="abcdef";
printf("%d\n", sizeof(p));
//p中记录的是首元素的地址,所以传入的是地址,因此结果就是4/8
printf("%d\n", sizeof(p+1));
//p+1,传入的就是第二个元素的地址,所以结果是4/8
printf("%d\n", sizeof(*p));
//p记录的是首元素地址,所以解引用传入的就是首元素,首元素是字符,因此结果是1
printf("%d\n", sizeof(p[0]));
//p[0]==*(p+0)==*p,所以结果同上
printf("%d\n", sizeof(&p));
//&p,取出的是整个数组的地址,所以传入的是一个地址,因此结果就是4/8
printf("%d\n", sizeof(&p+1));
//传入的依旧是一个地址,这不过是跨过一整个数组后,所指的内存空间中的地址。
printf("%d\n", sizeof(&p[0]+1));
//传入的是第二个元素的地址,所以结果是1
printf("%d\n", strlen(p));
//传入的是p,p中记录的是字符串首元素的地址,所以结果是6
printf("%d\n", strlen(p+1));
//p是字符型指针,所以p+1后,指向的就是第二元素的地址,所以打印的结果是5
printf("%d\n", strlen(*p));
//p指向的是首元素的地址,所以*p传入的是首元素,而不是地址,所以系统报错
printf("%d\n", strlen(p[0]));
//同上
printf("%d\n", strlen(&p));
//&p是整个数组的地址,但是在数值上依然和首元素的地址相同,因此结果还是6
printf("%d\n", strlen(&p+1));
//&p是整个数组的地址,其指针类型是数组指针,所以会跨过一个数数组,传入的是数组后面紧邻的空间的地址,此空间后面的空间中的内容完全是随机的,所以\0的位置也是随机的,因此这里的结果就是随机的。
printf("%d\n", strlen(&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]));
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//数组名单独存在,所以数组名此时代表的是整个数组,因此结果就是3X4X4=12X4=48
printf("%d\n",sizeof(a[0][0]));
//a[0][0]访问的是第一个元素,所以其结果是4
printf("%d\n",sizeof(a[0]));
//a[0]==*(a+0)==*a,a是二维数组的数组名,其指针的数据类型是数组指针,数组指针=&数组名,因此对其解引用,得到的就是一个数组名,而这个数组名就是二维数组中第一行的数组名,而这个数组名单独存在,代表的就是整个数组,因此其结果为:4x4=16;
printf("%d\n",sizeof(a[0]+1));
//a[0]是数组名,但是这个数组名并不是单独存在的,所以此时这个数组名代表的是一维数组中首元素的地址,因此对其+1,即第二个元素的地址。既然是地址,那么其结果就是4/8
printf("%d\n",sizeof(*(a[0]+1)));
//a[0]是第一行的数组名,因为不是单独存在,所以这个数组名代表的是首元素的地址,因此对其加一,代表的是第二个元素的地址,对这个地址进行解引用操作,得到的就是第一行的第二个元素,所以结果就是4
printf("%d\n",sizeof(a+1));
//a是二维数组的数组名,但是a不是单独存在的,所以其代表的就是首元素的地址,但是对于一个二维数组而言,其首元素就是第一行数组的地址,所以对其加一得到的就是第二行数组的地址,既然是地址,那么结果就是4/8
printf("%d\n",sizeof(*(a+1)));
//(a+1)得到的是第二行数组的地址,对其解引用,得到的就是第二行的数组名,而第二行的数组名单独存在,所以其代表的是第二行的整个数组,结果就是16
printf("%d\n",sizeof(&a[0]+1));
//&a[0]==&(*a)==a,二维数组名不是单独存在,所以代表的是二维数组中,第一行数组的地址,对其加一,得到的就是第二行数组的地址,结果就是4/8
printf("%d\n",sizeof(*(&a[0]+1)));
//得到第二行数组的地址,解引用,得到的就是第二行数组的数组名,这个数组名单独存在,代表的是整个数组,所以结果是16
printf("%d\n",sizeof(*a));
//a是二维数组的数组名,但是数组名不是单独存在,代表的就是首元素的地址,对其解引用,得到的就是首元素,所以得到的就是第一行数组,结果就是16
printf("%d\n",sizeof(a[3]));
//a[3]==*(a+3)-->代表的是第四行数组,但是这个二维数组一共就有三行,按理来说,这里发生了越界访问,但其实并没有报错,因为sizeof仅仅是通过这里的描述确定了其数据类型,并没有真的去访问。所以结果就是16