sizeof和strlen的区别及使用详解——C语言

sizeof和strlen的区别

  1. 作用的区别:sizeof是用于计算类型所占空间大小(单位为字节)的一个操作符。strlen是用于统计字符串长度的函数。
  2. 类型的区别:sizeof是操作符,strlen是C语言库函数。
    我们需要了解到的是,C语言代码到可执行程序大体上分为三步:
    编译——链接——运行
    而sizeof在编译期间就完成运算了,strlen则是在运行的时候才开始计算。

使用案例分析

首先我们需要明白数组名在各个情况中的含义:

数组名的意义:

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

一维数组案例

	//一维数组
	int a[] = {
    
     1,2,3,4 };
	printf("%d\n", sizeof(a));//a是数组名,单独放在sizeof中,表示整个数组,数组有4个元素,每个元素为整型,则计算结果为4*4=16
	printf("%d\n", sizeof(a + 0));//a是数组名,但是sizeof中方的是(a+0),此时a没有单独放在sizeof中,不在表示整个数组,而是数组首元素的地址,地址相加减后的结果也是地址,所以结果为4/8(32位平台指针大小为4个字节,64位平台则为8个字节)
	printf("%d\n", sizeof(*a));//a是数组名,与*结合,不属于单独放在sizeof中。所以a代表首元素地址,*a则为首元素,即a[0],为整型,所以结果为4
	printf("%d\n", sizeof(a + 1));//a没有单独放在sizeof中,则表示首元素地址,a+1则表示a[1]的地址,结果为4/8
	printf("%d\n", sizeof(a[1]));//4
	printf("%d\n", sizeof(&a));//a与&相结合,a表示整个数组,所以&a就代表整个数组的地址,结果为4/8
	printf("%d\n", sizeof(*&a));//&a中的a代表整个数组,所以&a就代表整个数组的地址,*后就是整个数组,所以结果为16
	printf("%d\n", sizeof(&a + 1));//&a代表的是整个数组的地址,&a+1则跳过整个数组不够同样是数组,所以结果为4/8
	printf("%d\n", sizeof(&a[0]));//&a[0]表示a[0]的地址,结果为4/8
	printf("%d\n", sizeof(&a[0] + 1));//&a[0]+1==&a[1],结果为4/8

字符数组(非字符串)

	char arr[] = {
    
     'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//arr单独放在sizeof中,表示整个数组,所以结果为6
	printf("%d\n", sizeof(arr + 0));//arr代表首元素地址,arr+0还是首元素地址,结果为4/8
	printf("%d\n", sizeof(*arr));//arr代表首元素地址,*arr代表首元素即'a'是char类型,所以结果为1
	printf("%d\n", sizeof(arr[1]));//arr[1]即'b',为char类型,所以结果为1
	printf("%d\n", sizeof(&arr));//&arr是,arr表示整个数组,计算结果为4/8
	printf("%d\n", sizeof(&arr + 1));//arr表示整个数组,&arr+1则跳过整个数组,结果为4/8
	printf("%d\n", sizeof(&arr[0] + 1));//相当于arr[1],计算结果为4/8
	printf("%d\n", strlen(arr));//由于strlen的工作原理是找'\0',而该数组中没有'\0'所以结果为 随机值
	printf("%d\n", strlen(arr + 0));//同上
	printf("%d\n", strlen(*arr));//*arr的结果为'a',ASCII码值为97,所以strlen函数从97这个地址开始找'\0',会出错err
	printf("%d\n", strlen(arr[1]));//同上
	printf("%d\n", strlen(&arr));//&arr代表整个数组的地址,但是与首元素地址的值是一样的,结果为随机值
	printf("%d\n", strlen(&arr + 1));//&arr代表整个数组,&arr+1跳过整个数组,但是也是地址所以结果为随机值
	printf("%d\n", strlen(&arr[0] + 1));//相当于&arr[1],运算结果为随机值但是比上一行代码多5

其中需要讲解的就是printf("%d\n", strlen(*arr));这段代码,我们可以通过调试来证明注释中的想法:
在这里插入图片描述

这里错误显示读取位置 0x00000061 时发生访问冲突
我解释一下,这里是以16进制显示的数字,所以是61,转换成10进制就是97。

字符数组(字符串)

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//arr单独放在sizeof内部,表示整个数组,由于字符串最后会补'\0',所以结果为7
	printf("%d\n", sizeof(arr + 0));//arr+0==&arr[0],结果为4/8
	printf("%d\n", sizeof(*arr));//*arr==arr[0],类型为char,结果为1
	printf("%d\n", sizeof(arr[1]));//类型为char,结果为1
	printf("%d\n", sizeof(&arr));//表示整个数组的地址,结果为4/8
	printf("%d\n", sizeof(&arr + 1));//跳过整个数组,但是也是表示地址,结果为4/8
	printf("%d\n", sizeof(&arr[0] + 1));//相当于&arr[1],结果为4/8
	printf("%d\n", strlen(arr));//strlen计算字符串长度,结果为6
	printf("%d\n", strlen(arr + 0));//同样是从首元素开始统计,结果为6
	printf("%d\n", strlen(*arr));//*arr表示arr[0],从地址97开始计算,属于非法访问内存,err
	printf("%d\n", strlen(arr[1]));//从地址arr[1]开始计算,即从地址98开始计算,属于非法访问内存,err
	printf("%d\n", strlen(&arr));//&arr代表整个数组,但是内容与首元素地址一样,结果为6
	printf("%d\n", strlen(&arr + 1));//表示跳过该数组后开始计算,属于非法访问内存,err,(由于vs的内存越界访问检测非常不严谨,在vs中运行结果为随机值)
	printf("%d\n", strlen(&arr[0] + 1));//从&arr[1]开始计算,结果为5

字符数组(常量字符串)

	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==*(p+0)==p[0],都表示数组的首元素,为1
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));//地址的地址也是地址,结果为4/8
	printf("%d\n", sizeof(&p + 1));//地址+1的结果也是地址,所以为4/8
	printf("%d\n", sizeof(&p[0] + 1));//相当于&p[1],结果为4/8
	printf("%d\n", strlen(p));//计算字符串长度,为6
	printf("%d\n", strlen(p + 1));//从p[1]开始计算字符串长度,结果为5
	printf("%d\n", strlen(*p));//从地址'a'开始计算字符串长度,即从内存97开始计算字符串长度,属于非法访问内存,err
	printf("%d\n", strlen(p[0]));//同上
	printf("%d\n", strlen(&p));//从p的地址开始计算字符串长度,随机值
	printf("%d\n", strlen(&p + 1));//从p+1的地址开始计算字符串长度,随机值
	printf("%d\n", strlen(&p[0] + 1));//从p[1]开始计算字符串长度,结果为5

其中需要讲解的是printf("%d\n", strlen(&p));这段代码,图解:
在这里插入图片描述

二维数组

首先我们需要明白二维数组的存储规则。
举一个例子,int a[3][4]; 二维数组可以理解成一维数组的数组。
在这里插入图片描述
接下来看题

	//二维数组
	int a[3][4] = {
    
     0 };
	printf("%d\n", sizeof(a));//a是数组名,单独放在sizeof内部代表整个数组,结果为3*4*4=48
	printf("%d\n", sizeof(a[0][0]));//a[0][0]代表数组的第一行第一列的元素,为整型,结果为4
	printf("%d\n", sizeof(a[0]));//a[0]是二维数组第一行的数组名,单独放在sizeof内部,表示整个第一行数组,一行有4列,所以结果为4*4=16
	printf("%d\n", sizeof(a[0] + 1));//a[0]是二维数组的第一行的数组名,没有单独放在sizeof中,a[0]表示第一行的首元素地址,a[0]+1,相当于&a[0][1],结果为4/8
	printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1==&a[0][1],*后找到a[0][1],为整型,结果为4
	printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,没有单独放在sizeof中,则表示二维数组的首元素地址即&a[0],a+1==&a[1],结果为4/8
	printf("%d\n", sizeof(*(a + 1)));//a+1是一个数组指针,指向二维数组的第二行即a[1],对数组指针解引用,访问整个数组,相当于sizeof(a[1])结果为16
	printf("%d\n", sizeof(&a[0] + 1));//相当于&a[1],结果为4/8
	printf("%d\n", sizeof(*(&a[0] + 1)));//相当于sizeof(a[1]),结果为16
	printf("%d\n", sizeof(*a));//a没有单独放在sizeof中,则a代表a[0]即第一行的地址,*后拿到一个数组,结果为16
	printf("%d\n", sizeof(a[3]));//虽然a[3]已经越界了,但是sizeof中并不会访问该地址,它只是计算类型占空间的大小,结果为16

需要讲解的是printf("%d\n", sizeof(a[3]));
我们需要了解以下知识点:任何表达式有两个属性1.值属性,2.类型属性。
举一个例子:3+9;这个表达式的值属性是12,类型属性是int。
而sizeof只看表达式的类型属性,并不会对表达式进行计算,所以就不会发生越界访问的问题。

总结

sizeof与strlen是C语言使用中经常用到的操作符和表达式。如果没有深入了解他们的区别和使用方式的话,很容易出错。本篇博客主要介绍了两者的区别和一些使用的案例,希望对大家有帮助。

猜你喜欢

转载自blog.csdn.net/m0_72482689/article/details/126775731