深入辨析sizeof和strlen

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 深入辨析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;
}

输出:


5d851b4e6670573376d72e24fc43cdb6_d27bee726e0d41e0974f26ae30c287f9.png


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环境下输出:

599bcb27a2cb5ff65ab98559dbd8caa2_5e0e5070558c433ca70dbf6c11bdfa96.png

                                                                                                                     

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环境下输出:                                                                                                                    

0d158214a03b8af7810e6e14c707616c_0325da453b78463c8d52c86119ca03d8.png

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环境下输出:


edb475b915f786cea0742659adf78c07_cca2b4fb76714fc7bf8ce348f923e8c0.png


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环境下输出:


61772b71db2ad3b59e6ef1920cb2e621_c6710f9f15d54114a9b97b85f49eec44.png


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环境下输出:


83a6a66d953baa72991ec7d222216496_30dbf6d7c8b94ea5b8a3cbdc4cf25701.png


总结:

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


1a1aacbe653a91d2f145226ee4956dd1_2645927deb88413284981a3416d0c15d.png


6e7b878b537aa109340b47642b33ed05_b4118f838eed48d29fa3d3762366a51d.png


练习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

相关文章
|
8月前
|
编译器 C语言
sizeof,sizeof与strlen的区别
sizeof,sizeof与strlen的区别
76 0
sizeof,sizeof与strlen的区别
|
7月前
|
C语言
sizeof与strlen的使用及其区别
sizeof与strlen的使用及其区别
sizeof与strlen的使用及其区别
|
8月前
|
存储 编译器
strlen()和sizeof()的区别
strlen()和sizeof()的区别
65 0
strlen和sizeof详解
strlen和sizeof详解
97 0
|
8月前
|
Serverless
sizeof和strlen的区别【详解】
sizeof和strlen的区别【详解】
65 0
|
C语言
strlen和sizeof的区别
strlen和sizeof的区别
94 0
|
存储 C语言
sizeof和strlen求取数组&指针之辨析
sizeof和strlen求取数组&指针之辨析
97 0
strlen与sizeof的区别
strlen与sizeof的区别
101 0
sizeof和strlen的对比
sizeof和strlen的对比
58 0
sizeof与strlen区别
sizeof是关键字,参数可以是各种数据(包括函数,类型,对象,数组,指针……)用于计算数据所占字节大小 strlen是函数,参数类型必须是字符型指针(char *),用于计算字符串,从字符串的第一个地址开始遍历,直到遇到‘\0’停止
89 0