sizeof 和 strlen的对比
sizeof | strlen |
sizeof是操作符 | strlen是库函数,使用需要包含头文件 string.h |
sizeof计算操作数所占内存的大小,单位是字节
| srtlen是求字符串长度的,统计的是 \0 之前字符的个数 |
不关注内存中存放什么数
据
|
关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界
|
sizeof和strlen的返回值类型是 size_t,它的本质是无符号整形,输出是%zd
还可以用 %u(无符号十进制整数),%ud(unsigned int),%ld(long int),%lld(long long int)
%lu(unsigned long int),%llu(unsigned long long int) 输出,不然会有警告。
从上文的代码,我们还可以得出一个结论。
sizeof 在计算大小的时候,其实是根据类型推算的。
sizeof的操作数如果是一个表达式,表达式的不参与计算的。
例题精讲
一维数组
sizeof(a) | 16字节 | sizeof(a)中的a代表整个数组的元素,所以计算出整个数组的大小 |
sizeof(a + 0) | 4/8字节 | 数组名a并没有单独放在sizeof内部,所以a就是数组首元素的地址 |
sizeof(*a) | 4字节 | a就是数组首元素的地址,*a==*&a[0]==a[0] |
sizeof(a+1) | 4/8字节 | a+1==&a[0]+1==&a[1] |
sizeof(a[1]) | 4字节 | 计算第2个元素的大小 |
sizeof(&a) | 4/8字节 | &a代表整个数组的地址,在一维数组中也等同于行地址 |
sizeof(*&a) | 16字节 | *&a==a==&a[0],由于转化为a时,恰巧在sizeof中,sizeof(a)中的a为整个数组的元素 |
sizeof(&a+1) | 4/8字节 | &a+1是跳过整个数组后的地址,是地址大小就是4/8个字节 |
sizeof(&a[0]) | 4/8字节 | 首元素的地址 |
sizeof(&a[0]+1) | 4/8字节 | 第二个元素的地址 |
字符数组
例一
sizeof(arr) | 6字节 | 这里的arr代表整个数组的元素 |
sizeof(arr + 0) | 4/8字节 | 这里的arr是数组首元素的地址,arr+0 还是首元素的地址 |
sizeof(*arr) | 1字节 | *arr==*&arr[0]==arr[0] |
sizeof(arr[1]) | 1字节 | arr[1]就是数组的第二个元素 |
sizeof(&arr) | 4/8字节 | &arr整个数组的地址,在一维数组中也等同于行地址 |
sizeof(&arr + 1) | 4/8字节 | &arr+1 是跳过整个数组后的地址,是地址大小就是4/8个字节 |
sizeof(&arr[0] + 1) | 4/8字节 | &a[0]+1==&a[1],第二个元素的地址 |
例二
strlen(arr) | 随机值 | 数组中没有明确给出\0,会一直在内存中往下找\0 |
strlen(arr + 0) | 随机值 | arr+0==arr==&arr[0],理由同上 |
strlen(*arr) | err | *arr==arr[0]=='a'-->97,把97当成地址非法访问--err |
strlen(arr[1]) | err | arr[1]==‘b’-->98,把98当成地址非法访问--err |
strlen(&arr) | 随机值 | &arr是整个数组的地址但大小和数组首元素地址&a[0]相同,这里会从&a[0]地址处往下找\0 |
strlen(&arr + 1) | 随机值 | &arr+1跨越了整个数组的地址,从整个数组后一个地址找\0 |
strlen(&arr[0] + 1) | 随机值 | &arr[0]+1==&arr[1],从第二个元素的地址开始找\0 |
例三
sizeof(arr) | 7字节 | 整个数组的大小 |
sizeof(arr + 0) | 4/8字节 | arr==&arr[0],arr + 0 还是首元素的地址 |
sizeof(*arr) | 1字节 | arr表示数组首元素的地址,*arr就是首元素 |
sizeof(arr[1]) | 1字节 | arr[1]是第二个元素 |
sizeof(&arr) | 4/8字节 | &arr是整个数组的地址,在一维数组中也等同于行地址 |
sizeof(&arr + 1) | 4/8字节 | &arr是数组的地址,&arr+1就是跳过整个数组的那个地址 |
sizeof(&arr[0] + 1) | 4/8字节 | &arr[0]+1==&arr[1],第二个元素的地址 |
例四
strlen(arr) | 6 | arr是首元素的地址 |
strlen(arr + 0) | 6 | arr+1 也是首元素的地址 |
strlen(*arr) | err | *arr==arr[0] 非法访问 |
strlen(arr[1]) | err | 非法访问 |
strlen(&arr) | 6 | &arr是整个数组的地址但大小和数组首元素地址&a[0]相同,这里会从&a[0]地址处往下找\0 |
strlen(&arr + 1) | 随机值 | 跳过整个数组后的地址,从这里开始向后找\0 |
strlen(&arr[0] + 1) | 5 | &arr[0] + 1==&arr[1],从第二个元素的地址往后找\0 |
例五
sizeof(p) | 4/8字节 | p指向‘a’的地址 |
sizeof(p + 1) | 4/8字节 | p+1是‘b’的地址 |
sizeof(*p) | 1字节 | *p 是首字符‘a' |
sizeof(p[0]) | 1字节 | p[0]==*(p+0)==a |
sizeof(&p) | 4/8字节 | &p是p的地址 |
sizeof(&p + 1) | 4/8字节 | &p + 1也是地址,&p+1是跳过p变量后的地址 |
sizeof(&p[0] + 1) | 4/8字节 | &p[0]+1==&*(p+0)+1=='b'的地址 |
例六
strlen(p) | 6 | p中存放的是a的地址,从a的地址开始向后访问 |
strlen(p + 1) | 5 | p+1=='b'的地址,从b的地址开始向后访问 |
strlen(*p) | err | *p=='a' 非法访问 |
strlen(p[0]) | err | p[0]==*(p+0)==*p=='a' 非法访问 |
strlen(&p) | 随机值 | &p是p的地址,从p所占空间的起始位置开始查找的 |
strlen(&p + 1) | 随机值 | 从p所占空间的起始位置的下一位开始查找的 |
strlen(&p[0] + 1) | 5 | &p[0]+1==&*(p+0)+1==p+1=='b'的地址 |
⼆维数组
sizeof(a) | 48字节 | 计算的是整个二维数组的大小 |
sizeof(a[0][0]) | 4字节 | a[0][0]是第一行第一个元素 |
sizeof(a[0]) | 16字节 | a[0]其实就是第一行的数组名,计算的是第一行的大小,a[0]==&a[0][0],但这里作为收缩的数组名,单独在sizeof中表示这一行数组的所有元素 |
sizeof(a[0]+1) | 4/8字节 | a[0]+1==&a[0][0]+1==&a[0][1] |
sizeof(*(a[0]+1)) | 4字节 | *(a[0]+1)==a[0][1] |
sizeof(a+1) | 4/8字节 | a+1==&a[0]+1==&a[1],就是第二行的地址 |
sizeof(*(a+1)) | 16字节 | *(a+1)==*(&a[0]+1)==*&a[1]==a[1],转换到这里,就不能再转换了 这里恰巧构成sizeof(a[1]),a[1]单独在sizeof中表示这一行所有元素 |
sizeof(&a[0]+1) | 4/8字节 | &a[0]+1==&a[1] |
sizeof(*(&a[0]+1) | 16字节 | *(&a[0]+1)==a[1],构成sizeof(a[1]),表示这一行所有元素 |
sizeof(*a) | 16字节 | *a=a[0],构成sizeof(a[0]),表示这一行的所有元素 |
sizeof(a[3]) | 16字节 | 越界访问,但sizeof不管,依旧往下找4个元素凑成一行计算大小 |
指针运算笔试题解析
第一题
解答: &a+1==跨过这个数组之后的地址,强制转化int *类型赋给int 指针ptr
*(a+1)==*(&a[0]+1)==*(&a[1])==a[1] 所以第一空为 1
*(ptr - 1)==*(a[5]后一个的地址 - 1)==*&a[5] 所以第二空为 5
注意: &a+1和a+1,这两个 1 的意义是不一样的,前+1是+1×20字节,后+1是-1×4字节
&a+1的地址是指向一块的地址是20字节的地址,(int *)转化后变为一个字节的地址
ptr指向&a[5]后一个的地址
第二题
解答: p是一个结构体指针,将十六进制数据 100000强制类型转化为一个结构体地址赋给p
由于x86条件下,地址是32位,所以p赋得的地址是0x00100000
第一问 p+ 0x1,这里的1是十六进制数据,看着面生不太熟,所以我们把它转成十进制数据,还是1,所以就是 p+1,这里的1就是20字节,因为题目已经给出结构体大小为20字节的条件,结构体型的指针+1就等于+20字节。0x00100000+20==0x00100000+0x14==0x00100014,打印出来就是001000014
第二问 (unsigned long)p + 0x1,p==0x00100000,这里的p==0x00100000被强制转换为无符号整形数据了,结果就是0x00100000+0x1==0x00100001,打印出来00100001
第三问 (unsigned int*)p + 0x1,p==0x00100000,这里的p==0x00100000被强制转换为无符号整形地址类型了,后面0x1转成十进制数据还是1,这里的1就是四个字节,因为无符号整形是四个字节,地址就加1就加4个字节了。p+1==0x00100000+4==0x00100000+0x4==0x00100004,打印出来就是00100004
注意 后面的 0x1的类型是由前面p的类型决定的,对于初学者来说0x1看着面生直接化成十进制去看。
第三题
解答:给a[3][2]赋值时用到了逗号表达式,所以赋值的情况为
a[0][0]=1,a[0][1]=3,a[1][0]=5,,a[1][1]=0,a[2][0]=0,a[2][1]=0
p==a[0]==&a[0][0], p[0]==*(p+0)==*(&a[0][0])==a[0][0]==1
所以答案为1
第四题
解答:定义了一个容量为四个房间(16个字节)的数组指针,二维数组是25个元素(100个字节),一行5个元素(20个字节),需要一个容量为5个房间的数组指针去装,但现在是一个容量为4个房间的数组指针去装。所以p+1==p+4个元素 而不是一行(5个元素)
第一空 &p[4][2]==&*(p[4]+2)==&*(*(p+4)+2)==&*(&a[3][2]+2)==&a[3][4]
所以就是 &a[3][4]-&a[4][2]== -4 (地址减地址是相隔元素的数量)
以十六进制打印出来 负数的十六进制需要化成补码再进行转化
-4 原码 10000000 00000000 00000000 00000100
反码 111111111 111111111 111111111 11111011
补码 111111111 111111111 111111111 11111100
F F F F F F F C
所以答案就是 FFFFFFFC
第二空 以十进制打印 就是 -4
第五题
解答: &aa==整个数组的地址 &aa+1==数组最后一位元素的地址+1后的地址
aa==&a[0] *(aa+1)==*(&a[1])==a[1]==&a[1][0]
第一空 *(pr1-1)==*(数组最后一位元素的地址+1后的地址-1)==*(&aa[1][4])==aa[1][4]==10
第二空 *(ptr2-1)==*(&a[1][0]-1)==a[1][0]==5
第六题
解答:这里建立了一个一级指针数组a,a[0]='w'的地址(第一个字符串),a[1]='a'的地址(第二个字符串),a[3]='a'的地址(第三个字符串)。
建立了一个二级指针pa,pa==a==&a[0]
*pa==*a==a[0]=='w'的地址
相当于printf("%s\n",'w'的地址); 结果就是打印出第二个字符串 at
第七题
这是最复杂的一题,我只能以我理解的角度为大家讲解
解答: 各级指针内的地址分配如上图所示
第一问 **++cpp==**++(&cp[0])==**(&cp[0]+1)==**&cp[1]==*&c[2]=='p'的地址
答案就是从p的地址开始输出字符串 即 POINT
第二问 由于第一问cpp实现了自加,它指向了&cp[1] cpp=&cp[1]
*--*++cpp+3==*--*(&cp[1]+1)+3==*--*&cp[2]+3==*--&c[1]+3==*&c[0]+3==c[0]+3
=='E'的地址(第一个字符串第一个'E')+3=='E'的地址(第一个字符串第二个'E')
答案就是从'E'的地址(第一个字符串第二个'E')开始输出 即 ER
第三问 由于第二问cpp又实现了自加,它这次指向了&cp[2] cpp=&cp[2]
*cpp[-2]+3==**(cpp-2)+3==**(&cp[2]-2)+3==**&cp[0]+3==*cp[0]+3==*&c[3]+3
==c[3]+3=='F'的地址+3=='S'的地址
答案就是从'S'的地址开始输出 即 ST
第四问 cpp[-1][-1]+1==*(*(cpp-1)-1)+1==*(*(&cp[1])-1)+1==*(&c[1])+1==c[1]+1
=='N'的地址+1=='E'的地址(第二个字符串)
答案就是从'E'的地址(第二个字符串)开始输出 即 EW