【C语言】——指针七:
- 前言
- 一、 s i z e o f sizeof sizeof 与 s t r l e n strlen strlen 的对比
- 二、数组和指针笔试题解析
- 2.1、题组一
- 2.2、题组二
- 2.3、题组三
- 2.4、题组四
- 2.5、题组五
- 2.6、题组六
- 2.7、题组七
- 2.8、题组八
前言
在前面的学习中,我们已经对C语言指针的知识有一个较为全面的了解,那么接下来我们做一些练习吧,即是检验我们的学习成果,也是对之前的知识的巩固。
一、 s i z e o f sizeof sizeof 与 s t r l e n strlen strlen 的对比
1.1、 s i z e o f sizeof sizeof
因为后面的习题大量涉及 s i z e o f sizeof sizeof 与 s t r l e n strlen strlen ,这里我们将简单回顾一下。
- s i z e o f sizeof sizeof 用于计算类型的大小,单位是字节。
- 括号中可以放置类型也可以放置表达式。他是一个操作符,而非函数,当后面放入的是表达式时,括号可以省略不写(这也侧面验证了 s i z e o f sizeof sizeof 不是函数,你见过函数行参考可以不带括号的吗)
- s i z e o f sizeof sizeof 只关心他里面的类型,表达式所占用空间的大小,并不进行具体的计算。
举例:
#include<stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof a);printf("%d\n", sizeof(int));return 0;
}
1.2、 s t r l e n strlen strlen
s t r l e n strlen strlen 是C语言中的库函数,它用来计算字符串的长度。
他的原理是从传递的指针变量开始,从前往后计算字符的个数,知道遇到 ‘\0’ 停止( ‘\0’ 本身不计算)
需要注意的是 s t r l e n strlen strlen 是直到 ‘\0’ 才停止,一直没有 ‘\0’ 则一直计算,因此 s t r l e n strlen strlen 有可能会越界访问
。
下面是 s t r l e n strlen strlen 的 模拟实现:
#include<stdio.h>
#include<assert.h>int my_strlen(const char* str)
{assert(str);const char* p = str;while (*(str)++){;}return str - p - 1;
}
s t r l e n strlen strlen 的应用举例
#include<stdio.h>
int main()
{char arr1[] = { 'a','b','c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr2));return 0;
}
1.3、 s i z e o f sizeof sizeof 和 s t r l e n strlen strlen 对比
s i z e o f sizeof sizeof:
- s i z e o f sizeof sizeof 是
操作符
- s i z e o f sizeof sizeof 计算操作数所
占内存空间的大小
,单位是字节
- s i z e o f sizeof sizeof
不关心
内存中存放什么数据
s t r l e n strlen strlen
- s t r l e n strlen strlen 是库函数,使用前需要包含头文件 < s t r i n g . h > <string.h> <string.h>
- s t r l e n strlen strlen 是
求字符串长度
的,统计的是 ‘\0’ 之前字符的个数- s t r l e n strlen strlen 关注的是内存中是否有 ‘\0’ ,如果不是 ‘\0’ 就会持续往后找,可能会
越界
。
二、数组和指针笔试题解析
2.1、题组一
int a[] = { 1,2,3,4,5 };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));
答案:
20 4/8 4 4/8 4 4/8 20 4/8 4/8 4/8
printf("%d\n", sizeof(a));
数组名单独放在 s i z e o f sizeof sizeof 中,这里的数组名代表整个数组,计算的是整个数组的大小。答案:20printf("%d\n", sizeof(a + 0));
这里,虽然 s i z e o f sizeof sizeof(a)和 s i z e o f sizeof sizeof(a + 0)的计算结果是一样的,但他们代表的含义并不一样。 s i z e o f sizeof sizeof(a + 0)中,数组名 a a a 并没有单独放,此时的数组名表示数组首元素的地址,“+0” 地址不变,这里表示的是个指针变量,这个指针指向的是数组首元素。答案:4/8printf("%d\n", sizeof(*a));
这里数组名表示的是数组首元素的地址,解引用为数组的元素,数组元素类型为 i n t int int。答案:4printf("%d\n", sizeof(a + 1));
与上面的 s i z e o f ( a + 0 ) sizeof(a + 0) sizeof(a+0)一样,都是指针变量,只是这里指向的是数组的第二个元素。答案:4/8printf("%d\n", sizeof(a[1]));
这个很简单,即数组第二个元素,类型为 i n t int int,等价于:*(a + 1)printf("%d\n", sizeof(&a));
指针变量,指向的是整个数组,可访问 20 个字节,虽然是数组指针,但只要是指针,大小就是4/8。答案:4/8printf("%d\n", sizeof(*&a));
这里,& 和 ∗ * ∗ 互相抵消,即 ∗ * ∗ &a = a,即 s i z e o f ( a ) sizeof(a) sizeof(a)。答案:20printf("%d\n", sizeof(&a + 1));
这里是指针变量,&a为指向整个数组,+1跳过了整个数组。该指针类型为 i n t int int(*)[ 5 ]。但这里不是野指针,因为 s i z e o f sizeof sizeof 是不会关心访问里面的值的,你不能因为我站在银行门口就说我抢银行吧。答案:4/8printf("%d\n", sizeof(&a[0]));
指针变量,指向数组首元素的指针,为 i n t ∗ int* int∗ 类型。答案:4/8printf("%d\n", sizeof(&a[0] + 1));
指针变量,直向数组第二个元素的指针,只要是指针变量,为 i n t ∗ int* int∗ 类型。答案:4/8
2.2、题组二
char arr[] = { 'a','b','c','d','e','f' };printf("%zd\n", sizeof(arr));printf("%zd\n", sizeof(arr + 0)); printf("%zd\n", sizeof(*arr));printf("%zd\n", sizeof(arr[1]));printf("%zd\n", sizeof(&arr));printf("%zd\n", sizeof(&arr + 1));printf("%zd\n", sizeof( & arr[0] + 1));
答案:
6 4/8 1 1 4/8 4/8 4/8
printf("%zd\n", sizeof(arr));
数组名单独放,表示整个数组。答案:6printf("%zd\n", sizeof(arr + 0));
数组名没有单独放,后面有“+0”,表示的是指向首元素的指针,指针变量的大小为 4/8。这里不要因为 c h a r char char 类型大小为 1,所以就认为 c h a r ∗ char* char∗ 大小也为 1,只要是指针类型它的大小就是 4/8。答案:4/8printf("%zd\n", sizeof(*arr));
表示的是数组首元素,类型 c h a r char char。答案:1printf("%zd\n", sizeof(arr[1]));
表示的是数组第二个元素,类型 char,等价于*(arr + 1)。答案:1printf("%zd\n", sizeof(&arr));
表示的是指向整个数组的指针,指针变量的大小为 4/8,这里,不要以为他是数组类型的指针就以为他是 6,只要是指针类型,它的大小就是 4/8。答案:4/8printf("%zd\n", sizeof(&arr + 1));
这题与上面一题类似,都是指针变量,+1跳过了整个数组(sizeof不会运算里面的表达式,所以这里不算野指针)。答案:4/8printf("%zd\n", sizeof( & arr[0] + 1));
字符指针,指向的是数组第二个元素。答案:4/8
2.3、题组三
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))
答案:
随机值 随机值 程序无法运行 程序无法运行 随机值 随机值 - 6 随机值 - 1
printf("%d\n", strlen(arr));
这里 a r r arr arr 表示的是首元素的地址, s t r l e n strlen strlen 函数从首元素开始,往后计数,什么时候遇到 ‘\0’ 什么时候停下,计数停止。答案:随机值printf("%d\n", strlen(arr + 0));
这一题与上面的是一样的。答案:随机值printf("%d\n", strlen(*arr));
这里,传给 s t r l e n strlen strlen 函数的是数组首元素,即 ‘a’,字符 ‘a’ 的 ASCII值 为 97。因为 s t r l e n strlen strlen 函数接受的是指针类型的参数,所以 s t r l e n strlen strlen 把 ‘a’ 当做地址 0x00000097,该地址位于操作系统的内核,无法访问。答案:程序无法运行printf("%d\n", strlen(arr[1]));
这一题与上面一题同理,只是这里传的是 ‘b’。答案:程序无法运行printf("%d\n", strlen(&arr));
& a r r arr arr 是指向整个数组的数组指针,虽然类型不同,但数值相同,分析方法与 s t r l e n ( a r r ) strlen(arr) strlen(arr)一样。答案:随机值printf("%d\n", strlen(&arr + 1));
& a r r arr arr 为数组指针,加一跳过了整个数组,即跳过了 6 个字符,也是与上面一样,直到遇到 ‘\0’ 才停止。答案:随机值 - 6printf("%d\n", strlen(&arr[0] + 1))
字符指针,指向首元素地址,+1 指向第二个元素,一直往后数,直到 ‘\0’ 。答案:随机值 - 1
2.4、题组四
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));
答案:
7 4/8 1 1 4/8 4/8 4/8
printf("%d\n", sizeof(arr));
数组名单独放,表示整个数组的大小,输入的是字符串,后面还跟着 ‘\0’ 。答案:7printf("%d\n", sizeof(arr + 0));
数组名不是单独放,后面还有一个“+0”,为指向首元素地址的指针。答案:4/8printf("%d\n", sizeof(*arr));
首元素地址解引用,表示数组首元素类型,类型为 c h a r char char 。答案:1printf("%d\n", sizeof(arr[1]));
表示数组第二个元素,等价于 *(arr + i)。答案:1printf("%d\n", sizeof(&arr));
数组指针,指向整个数组。答案:4/8printf("%d\n", sizeof(&arr + 1));
数组指针,+1表 示跳过整个数组,但依然是数组指针类型。答案:4/8printf("%d\n", sizeof(&arr[0] + 1));
字符指针,指向数组第二个元素。答案:4/8
2.5、题组五
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));
答案:
6 6 程序无法运行 程序无法运行 6 随机值 5
printf("%d\n", strlen(arr));
正常传址, s t r l e n strlen strlen 计算该字符串的大小。答案:6printf("%d\n", strlen(arr + 0));
与上一题类似, s t r l e n strlen strlen 不会像 s i z e o f sizeof sizeof 那样要区分数组名是否单独存放,对 s t r l e n strlen strlen 来说,数组名只有地址这一个选项。答案:6。printf("%d\n", strlen(*arr));
这道题前面做过类似的,这里传的是 ‘a’, s t r l e n strlen strlen 把‘a’(97)当做地址,但该地址位于操作系统的内核,用户无法访问。答案:程序无法运行printf("%d\n", strlen(arr[1]));
与上面一题类似。答案:程序无法运行printf("%d\n", strlen(&arr));
传递数组指针,但数值上等于数组首元素地址,从起始位置开始一个个往后计数,直到一段遇到 ‘\0’ 停止。答案:6。printf("%d\n", strlen(&arr + 1));
数组指针,+1 则跳过整个数组,从数组末尾开始计数,什么时候遇到 ‘\0’ 什么时候停下。答案:随机值printf("%d\n", strlen(&arr[0] + 1));
字符指针,+1 跳过首元素,第二个元素开始往后计数,直到遇到 ‘f‘ 后面的 ‘\0’。答案:5。
2.6、题组六
comst 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));
答案:
4/8 4/8 1 1 4/8 4/8 4/8
printf("%d\n", sizeof(p));
p p p 是字符指针,大小为 4/8 字节,很简单。答案:4/8printf("%d\n", sizeof(p + 1));
与上面一样,指针+1 还是指针。答案:4/8printf("%d\n", sizeof(*p));
p p p 本质是存放字符串首元素的地址,即 ‘a’ 的地址, p p p 解引用得到 ‘a’ ,为 c h a r char char 类型,大小为一个字节。答案:1printf("%d\n", sizeof(p[0]));
这一句与上面的是一样的。答案:1printf("%d\n", sizeof(&p));
取出 p p p 的地址,为二级指针,本质还是指针。答案:4/8printf("%d\n", sizeof(&p + 1));
二级指针 +1,本质还是指针变量。答案:4/8printf("%d\n", sizeof(&p[0] + 1));
&p[0] + 1 == &*(p + 0) + 1,即 p + 1,还是指针变量。答案:4/8
2.7、题组七
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 程序无法运行 程序无法运行 随机值 随机值 5
printf("%d\n", strlen(p));
正常传址,正常计算字符串的大小。答案:6printf("%d\n", strlen(p + 1));
从第二个字符开始计算字符串的大小。答案:5printf("%d\n", strlen(*p));
*p就是‘a’-97。答案:程序无法运行printf("%d\n", strlen(p[0]));
p[ 0 ] = *(p + 0) = *p。答案:程序无法运行printf("%d\n", strlen(&p));
&p 取出的是 p 这个指针变量的地址,是二级指针,与字符串 “abcdef” 的关系就不大了。从 p 这个变量的地址开始往后数,什么时候遇到 ‘\0’ 什么时候停,但这一切都是未知。答案:随机值printf("%d\n", strlen(&p + 1));
也是一样的,+1 跳过了 4/8 的字节,但是一切依然是未知的。答案:随机值printf("%d\n", strlen(&p[0] + 1));
&p[ 0 ]为取出首个字符的地址,+1即第二个字符的地址。答案:5
2.8、题组八
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 16 4/8 4 4/8 16 4/8 16 16 16
printf("%d\n", sizeof(a));
数组名单独放在 s i z e o f sizeof sizeof 中,表示整个数组的大小。答案:48printf("%d\n", sizeof(a[0][0]));
表示的是数组第一行的首元素,类型为 i n t int int 。答案:4printf("%d\n", sizeof(a[0]));
a[0] 可看成第一行这个一维数组的数组名,数组名单独放在 s i z e o f sizeof sizeof 中,表示整个数组的大小。答案:16printf("%d\n", sizeof(a[0] + 1));
第一行的数组名并没有单独放在 s i z e o f sizeof sizeof 中,因此这里的 a[0] 表示的是第一行首元素的地址,+1 则为第二个元素地址,但都是指针变量。答案:4/8printf("%d\n", sizeof(*(a[0] + 1)));
a[0] + 1上面已经分析过了,即第一行第二个元素,现在进行解引用,为 i n t int int 类型。答案:4printf("%d\n", sizeof(a + 1));
数组名不是单独放置,这里的 a a a 表示的是数组首元素的地址,二维数组首元素为第一行,+1指向第二行的地址,但都是指针变量。答案:4/8printf("%d\n", sizeof(*(a + 1)));
a + 1上面已经分析过了,即第二行的地址,现在对其解引用,即整个第二行,类型为 i n t int int[ 3 ]。答案:16printf("%d\n", sizeof(&a[0] + 1));
a[0] 为第一行数组的数组名,对其进行 & 操作,取出的是整个第一行数组的地址,+1即指向第二行数组的指针。也可以这样看 &a[0] + 1 = &(*(a + 0)) + 1 = & * a + 1 = a + 1,而 a + 1 前面已经分析过了。答案:4/8printf("%d\n", sizeof(*(&a[0] + 1)));
&a[0] + 1 上面刚刚分析过,为指向第二行数组的指针,对其进行解引用,为第二行数组。答案:16printf("%d\n", sizeof(*a));
这里 a a a 不是单独放,代表首元素的地址,即第一行数组的地址,对其进行解引用,得到第一行数组。答案:16printf("%d\n", sizeof(a[3]));
a[3] 与 a[0] 是一样的,因为 s i z e o f sizeof sizeof 并不会对里面的表达式进行实际的匀运算,所以并不算错。答案:16