深入辨析sizeof和strlen

日刷百题,题刷百日!
归纳编程学习的感悟,
记录奋斗路上的点滴,
希望能帮到一样刻苦的你!
如有不足欢迎指正!
共同学习交流!
🌎欢迎各位→点赞 👍+ 收藏⭐️ + 留言​📝
冰冻三尺非一日之寒,水滴石穿非一日之功。

一起加油! 

一、sizeof和strlen的对比

1.1 sizeof

在学习操作符的时候,我们学习了 sizeof sizeof 计算变量所占内存内存空间大小的,单位是 字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的大小。
sizeof的返回类型size_t。
sizeof 只关注占⽤内存空间的大小,不在乎内存中存放什么数据。
比如:
#include <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));//输出4,表示变量a的内存空间4个字节
printf("%d\n", sizeof a);//输出4,没有()表示sizeof是操作符,不是库函数
printf("%d\n", sizeof(int));//输出4,表示int的内存空间大小
return 0;
}

注意:1、sizeof计算大小,根据类型推算。

           2、sizeof的操作数如果是一个表达式,表达式不参与计算.

举例:

#include <stdio.h>
int main()
{
  short a=10;//2个字节
  int i=20;//4个字节
  int n=sizeof(a=i+4);//表达式具有俩个属性,值属性和类型属性,sizeof()里面类型属性在编译和链接时已经确定,而表达式的值属性在代码运算时才确认;所以编译和链接时表达式不参与计算,以a的类型为准
  printf("%d\n",sizeof(a));//输出2
  printf("%d\n",n);//输出2
return 0;
}

输出:

1.2 strlen

strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:
 size_t strlen ( const char * str );

strlen的参数类型是字符指针,返回类型是无符号整型

统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。
strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。
#include <stdio.h>
int main()
{
char arr1[3] = {'a', 'b', 'c'};
char arr2[] = "abc";
printf("%d\n", strlen(arr1));//输出随机值,arr1为a的地址,往后找'\0',没有'\0',所以随机值
printf("%d\n", strlen(arr2));//输出3,字符串的实际为a b c \0

printf("%d\n", sizeof(arr1));//输出3,arr1为整个数组,计算数组大小,一共3个字节
printf("%d\n", sizeof(arr2));//输出为4,arr2为整个数组,计算数组大小,数组内存有4个字节
return 0;
}

1.3 sizeof 和 strlen的对比

sizeof
1. sizeof是操作符
2. sizeof计算操作数所占内存的大小,单位是字节
3. 不关注内存中存放什么数据
strlen
1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串长度的,统计的是 \0 之前字符的隔个数
3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能 会越界

二、sizeof和strlen习题及解析

2.1一维数组中sizeof的解析

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//16
printf("%d\n",sizeof(a+0));
//a没有单独放在sizeof内部,所以代表首元素地址,地址的大小为4/8个字节
printf("%d\n",sizeof(*a));
//*a代表首元素,大小是4个字节
printf("%d\n",sizeof(a+1));
//a是首元素地址,a+1代表第二个元素地址,地址的大小为4/8个字节
printf("%d\n",sizeof(a[1]));
//代表第二个元素大小,为4个字节
printf("%d\n",sizeof(&a));
//&a为整个数组地址,地址大小为4/8个字节
printf("%d\n",sizeof(*&a));
//*与&抵消,也就是sizeof(a),a代表整个数组,数组大小为16个字节
printf("%d\n",sizeof(&a+1));
//&a为整个数组地址,&a+1表示跳过整个数组,是数组后面的地址,地址大小为4/8个字节
printf("%d\n",sizeof(&a[0]));
//首元素的地址,地址大小为4/8个字节
printf("%d\n",sizeof(&a[0]+1));
//表示跳过一个元素,第二个元素的地址,地址大小为4/8个字节
在x86环境下输出:                                                                                                                        

2.2字符数组中sizeof和strlen的解析

2.2.1

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//整个数组大小,输出6
printf("%d\n", sizeof(arr+0));
//arr+0为首元素地址,地址大小为4/8个字节
printf("%d\n", sizeof(*arr));
//首元素,大小为1个字节
printf("%d\n", sizeof(arr[1]));
//第二个元素,元素类型为char,char大小为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));
//&arr[0]首元素地址,&arr[0]+1为第二个元素地址,地址大小为4/8个字节

在x86环境下输出:                                                                                                                    

2.2.2

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
//从首元素地址开始找\0,输出为随机值
printf("%d\n", strlen(arr+0));
//arr+0为首元素地址,同上,输出为随机值
printf("%d\n", strlen(*arr));
//*arr为字符a,ASCII值为97,strlen参数为指针,所以97被当成地址,此情况属于非法访问
printf("%d\n", strlen(arr[1]));
//第二个元素'b',ASCII值为98,strlen参数为指针,所以97被当成地址,此情况属于非法访问
printf("%d\n", strlen(&arr));
//&arr为整个数组地址,从首元素地址开始找\0,输出为随机值
printf("%d\n", strlen(&arr+1));
//&arr为跳过整个数组,数组后面的地址,输出为随机值
printf("%d\n", strlen(&arr[0]+1));
//第二个元素地址,从第二个元素向后找\0,输出为随机值

2.2.3

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
//7
printf("%d\n", sizeof(arr+0));
//首元素地址,地址大小为4/8个字节
printf("%d\n", sizeof(*arr));
//首元素大小为1个字节
printf("%d\n", sizeof(arr[1]));
//第二个元素大小为1个字节
printf("%d\n", sizeof(&arr));
//整个数组地址,地址大小为4/8个字节
printf("%d\n", sizeof(&arr+1));
//为整个数组后面的地址,地址大小为4/8个字节
printf("%d\n", sizeof(&arr[0]+1));
//第二个元素地址,地址大小为4/8个字节

在x86环境下输出:

2.2.4

char arr[] = "abcdef";
printf("%d\n", strlen(arr));
//6
printf("%d\n", strlen(arr+0));
//从首元素往后找'\0',输出6
printf("%d\n", strlen(*arr));
//'a'的ASCII值为97,strlen将97看出地址访问,非法访问
printf("%d\n", strlen(arr[1]));
//第二个元素'b'的ASCII值为98,strlen将98看出地址访问,非法访问
printf("%d\n", strlen(&arr));
//&arr为整个数组的地址,也是首元素地址,从首元素往后找'\0',输出6
printf("%d\n", strlen(&arr+1));
//&arr+1为整个数组后的地址,随机值
printf("%d\n", strlen(&arr[0]+1));
//第二个元素的地址,从第二个元素往后找'\0',输出5

2.2.5

char *p = "abcdef";
//p里面放着字符串首元素地址
printf("%d\n", sizeof(p));
//p为指针,也就是地址,地址的大小为4/8个字节大小
printf("%d\n", sizeof(p+1));
//p为首元素地址,p+1为第二个元素地址,地址的大小为4/8个字节大小
printf("%d\n", sizeof(*p));
//*p为'a',大小为1个字节
printf("%d\n", sizeof(p[0]));
//首元素大小为1个字节
printf("%d\n", sizeof(&p));
//为首元素地址的地址,地址的大小为4/8个字节大小
printf("%d\n", sizeof(&p+1));
//跳过一个P后的地址,地址的大小为4/8个字节大小
printf("%d\n", sizeof(&p[0]+1));
//第二个字符的地址,地址的大小为4/8个字节大小

在x86环境下输出:

2.2.6

char *p = "abcdef";
printf("%d\n", strlen(p));
//首元素地址,从首元素往后找'\0',输出为6
printf("%d\n", strlen(p+1));
//p+1为第二个元素地址,从第二个元素往后找'\0',输出为5
printf("%d\n", strlen(*p));
//*p为字符a,'a'的ASCII值为97,strlen将97看出地址访问,非法访问
printf("%d\n", strlen(p[0]));
//p[0]为字符a,'a'的ASCII值为97,strlen将97看出地址访问,非法访问
printf("%d\n", strlen(&p));
//&p为p的地址,p里面存放是首元素地址,从p所占空间起始位置开始查找,随机值
printf("%d\n", strlen(&p+1));
//跳过p后的地址,从跳过p后所占空间起始位置开始查找,随机值
printf("%d\n", strlen(&p[0]+1));
//第二个元素的地址,从第二个元素开始查找,输出为5

2.3 二维数组中sizeof的解析

int a[3][4] = {0};
printf("%d\n",sizeof(a));
//计算整个数组大小,大小48个字节
printf("%d\n",sizeof(a[0][0]));
//首元素大小,4个字节
printf("%d\n",sizeof(a[0]));
//a[0]为第一行数组名,计算第一行大小,16个字节
printf("%d\n",sizeof(a[0]+1));
//a[0]没有单独放在sizeof内部,所以表示首元素地址,+1表示第一行第二个元素的地址,地址的大小为4/8个字节
printf("%d\n",sizeof(*(a[0]+1)));
//表示计算第一行第二个元素大小,4个字节
printf("%d\n",sizeof(a+1));
//表示计算第二行地址的大小,地址的大小为4/8个字节
printf("%d\n",sizeof(*(a+1)));
//相当于a[1],计算第二行元素大小,16个字节
printf("%d\n",sizeof(&a[0]+1));
//&a[0]第一行的地址,+1第二行的地址,地址的大小为4/8个字节
printf("%d\n",sizeof(*(&a[0]+1)));
//第二行地址的解引用,16个字节
printf("%d\n",sizeof(*a));
//计算第一行的大小,16个字节
printf("%d\n",sizeof(a[3]));
//计算第某行的大小,因为sizeof内部不参与运算,16个字节

在x86环境下输出:

总结:
1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的大小。
2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址

三、关于sizeof 和 strlen易错题

练习3.1

下面代码的结果是:

#include <stdio.h>
int i;
int main()
{
    i--;
    if (i > sizeof(i))
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

A. >

B. <

C. 不输出

D. 程序有问题

解析:C语言中,0为假,非0即为真。

全局变量,没有给初始值时,编译其会默认将其初始化为0。

i的初始值为0,i--结果-1,i为整形,sizeof(i)求i类型大小是4,按照此分析来看,结果应该选择B,但是sizeof的返回值类型实际为无符号整形,因此编译器会自动将左侧i自动转换为无符号整形的数据,-1对应的无符号整形是一个非常大的数字,为ff ff ff ff,故实际应该选择A

这道题其实很隐蔽,真是虾仁猪心!!!

因此:选择A

练习3.2

下面代码的结果是:( )

#include <stdio.h>
#include <string.h>

int main()
{
    char arr[] = {'b', 'i', 't'};
    printf("%d\n", strlen(arr));
	return 0;
}

A. 3

B. 4

C. 随机值

D. 5

解析:strlen是用来获取字符串的有效长度的,结尾标记'\0'不包含在内。

strlen获取的规则非常简单:从前往后依次检测,直到遇到'\0'是就终止检测。

而上题中arr是一个字符数组,不是一个有效的字符串,因为后面没有放置'\0',因此strlen在求解时,从首元素开始将有效字符检测完之后,还会继续向后检测,直到遇到'\0'是才终止,因此答案为不确定,就看紧跟在't'之后的第一个'\0'在什么位置。

因此:答案选C

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日刷百题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值