【C语言】sizeof和库函数strlen讲解

目录

例题一:整型数组与sizeof

例题二: 字符数组与sizeof

例题三:字符数组与strlen

例题四:字符串数组与sizeof

例题五:字符串数组与strlen

例题六:字符指针与sizeof和strlen

例题七:二维数组与sizeof


sizeof:这是一个操作符,用来计算一个类型数据的大小,单位是字节

strlen:这是一个库函数,用来计算一个字符串的长度,本质上计算的是字符串中'\0'这个字符前元素的个数

以下我们通过几个例题来讲解其区别

例题一:整型数组与sizeof

#include<stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	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));

	return 0;
}

分析之前,我们先回顾以下数组名相关知识:

一般情况下,数组名代表数组首元素地址,有两个例外

  1. sizeof(数组名) ,这里数组名表示整个数组,计算的是整个数组的大小;需要注意的是,括号中必须是单独的数组名
  2. &数组名,这里数组名表示整个数组,取出的是整个数组的地址,主要在&数组名+/-整数时体现
  • sizeof(a) = 16

这里a表示的是整个数组,计算整个数组的大小=元素个数*每个元素大小=4*4byte=16byte

  • sizeof(a+0) = 4

这里a表示的是数组首元素的地址,也就是说a是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(*a) = 4

这里a表示的是首元素的地址,a指向首元素,对一个指针解引用,就得到其所指向的内容,即*a = a[0] = 1,因为1是一个整型,所以其大小为4

  • sizeof(a + 1) = 4

这里a表示的是首元素的地址,也就是说a是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(a[1]) = 4

a[1]即数组下标为1的元素2,这是一个整型元素,所以其大小为4

  • sizeof(&a) = 4

这里a表示的是整个数组名,&a得到整个数组的地址,地址就是一个指针,所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(*&a) = 16

法一:*和&相互抵消,sizeof(a) = 16

法二:先&a得到的是整个数组的地址,再*(整个数组的地址)得到整个数组,最后计算整个数组的大小 = 16

  • sizeof(&a + 1) = 4

这里a表示的是整个数组,&a得到整个数组的地址,地址就是指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&a[0]) = 4

a[0]即数组中下标为0的元素,&a[0]得到这个元素的地址,地址即指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&a[0] + 1) = 4

&a[0]是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

输出如下: 

例题二: 字符数组与sizeof

#include<stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	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));

	return 0;
}

 输出如下:

分析:

  • sizeof(arr) = 6

这里arr表示的是整个数组,计算整个数组的大小=元素个数*每个元素大小=6*1byte=6byte

  • sizeof(arr+0) = 4

这里arr表示的是数组首元素的地址,也就是说arr是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(*arr) = 1

这里arr表示的是首元素的地址,arr指向首元素,对一个指针解引用,就得到其所指向的内容,即*arr = arr[0] = 'a',因为'a'是一个字符型,所以其大小为1

  • sizeof(arr[1]) = 1

arr[1]即数组下标为1的元素'b',这是一个字符型元素,所以其大小为1

  • sizeof(&arr) = 4

这里arr表示的是整个数组名,&arr得到整个数组的地址,地址就是一个指针,所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&arr + 1) = 4

这里arr表示的是整个数组,&arr得到整个数组的地址,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&arr[0] + 1) = 4

&arr[0]是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

输出如下: 

例题三:字符数组与strlen

#include<stdio.h>
#include<string.h>
int main()
{
	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));

	return 0;
}

分析之前,我们先来看一下arr数组创建完成之后在内存中的布局 

分析:

  • strlen(arr) 

strlen计算的是从arr这个地址开始向后,’\0'字符前的元素个数

arr数组中存放了6个元素,没有'\0'这个元素,所以strlen函数会继续往后查找,直到找到'\0'这个字符,返回其之前额元素个数;对于在数组开辟空间以外的地址,其中存放的内容是未知的,'\0'的位置是未知的,所以这里输出随机值

  • strlen(arr+0) 

和strlen(arr)同理,arr+0仍然指向数组首元素,所以其输出和strlen(arr)一样的随机值

  • strlen(*arr) 

这里arr表示的是首元素的地址,arr指向首元素,对一个指针解引用,就得到其所指向的内容,即*arr = arr[0] = 'a' = 97(ASCII码值)

对于strlen库函数,我们看搜索一下其定义

对于strlen函数来说,其参数为一个指针类型,不论我们传参为什么,它都会把参数当作指针类型处理,strlen(*arr) = strlen(a),这时,strlen函数会把字符'a'的ASCII码值97当作一个地址进行内存访问,然而对于计算机来说,低地址(虚拟地址)处的空间是不允许用户访问的,所以程序会报错或崩溃

  • strlen(arr[1]) 

与strlen(*arr)同理,strlen(arr[1]) = strlen(b),strlen函数会把字符'a'的ASCII码值98当作一个地址进行内存访问,程序会报错或崩溃

  • strlen(&arr) 

这里数组名表示的是整个数组的地址,但是我们在之前已经介绍过:&arr和arr得到的地址的值是相同的,strlen函数仍然从这个地址向后查找,直到遇到'\0'字符返回元素个数,所以strlen(&arr)和strlen(arr)输出一样的随机值

  • strlen(&arr + 1) 

这里arr表示的是整个数组,&arr得到整个数组的地址,&arr+1跳过整个数组

可以发现,从&arr+1的位置向后查找,与从arr指向的位置向后查找,找到'\0'字符时,相差元素的个数就是这个数组的元素个数,所以 strlen(&arr + 1) 输出的随机值 = strlen(arr)输出的随机值-数组元素的个数

  • strlen(&arr[0] + 1) 

&arr[0]指向数组首元素的地址,&arr[0]+1跳过一个元素,指向数组的第二个元素

可以发现,从&arr[0]+1的位置向后查找,与从arr指向的位置向后查找,找到'\0'字符时,相差元素的个数为1,所以 strlen(&arr + 1) 输出的随机值 = strlen(arr)输出的随机值-1

因为代码中strlen(arr[1]) 和strlen(*arr)存在bug,所以我们把这俩行注释掉再输出: 

例题四:字符串数组与sizeof

#include<stdio.h>
int main()
{
	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));

	return 0;
}

对于字符串来说,其末尾默认带有结束标志'\0'

我们先来看一下arr数组创建完成之后在内存中的布局 

分析:

  • sizeof(arr) = 7

这里arr表示的是整个数组,计算整个数组的大小=元素个数*每个元素大小=7*1byte=7byte

一定要注意字符串的结束标志'\0'也是数组的元素,占据一个字节空间

  • sizeof(arr+0) = 4

这里arr表示的是数组首元素的地址,也就是说arr是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(*arr) = 1

这里arr表示的是首元素的地址,arr指向首元素,对一个指针解引用,就得到其所指向的内容,即*arr = arr[0] = 'a',因为'a'是一个字符型,所以其大小为1

  • sizeof(arr[1]) = 1

arr[1]即数组下标为1的元素'b',这是一个字符型元素,所以其大小为1

  • sizeof(&arr) = 4

这里arr表示的是整个数组名,&arr得到整个数组的地址,地址就是一个指针,所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&arr + 1) = 4

这里arr表示的是整个数组,&arr得到整个数组的地址,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

  • sizeof(&arr[0] + 1) = 4

&arr[0]是一个指针,指针和±整数还是指针

所以这里计算的是一个指针的大小,在32位平台下为4byte,在64位平台下为8

输出如下: 

例题五:字符串数组与strlen

#include<stdio.h>
#include<string.h>
int main()
{
	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));

	return 0;
}

 以下是arr数组在内存中的布局

分析:

  • strlen(arr)  = 6

strlen计算的是从arr这个地址开始向后,’\0'字符前的元素个数

显然,'\0'前字符的个数为6

  • strlen(arr+0)  = 6

和strlen(arr)同理,arr+0仍然指向数组首元素,所以其输出和strlen(arr)一样 = 6

  • strlen(*arr) 

例题三已经介绍过,对于strlen函数来说,其参数为一个指针类型,不论我们传参为什么,它都会把参数当作指针类型处理,strlen(*arr) = strlen(a),这时,strlen函数会把字符'a'的ASCII码值97当作一个地址进行内存访问,然而对于计算机来说,低地址(虚拟地址)处的空间是不允许用户访问的,所以程序会报错或崩溃

  • strlen(arr[1]) 

与strlen(*arr)同理,strlen(arr[1]) = strlen(b),strlen函数会把字符'b'的ASCII码值98当作一个地址进行内存访问,程序会报错或崩溃

  • strlen(&arr) 

strlen(&arr) = strlen(arr) = 6

  • strlen(&arr + 1)  随机值

这里arr表示的是整个数组,&arr得到整个数组的地址,&arr+1跳过整个数组

从&arr+1的位置向后查找,就不属于arr数组开辟的空间了,这块空间里的内容是未知的,所以 strlen(&arr + 1) 输出随机值

  • strlen(&arr[0] + 1)  = 5

&arr[0]指向数组首元素的地址,&arr[0]+1跳过一个元素,指向数组的第二个元素

 从&arr[0]+1的位置向后查找,'\0'前的字符个数为5

因为代码中strlen(arr[1]) 和strlen(*arr)存在bug,所以我们把这俩行注释掉再输出: 

例题六:字符指针与sizeof和strlen

 

#include<stdio.h>
#include<string.h>
int main()
{
	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));
	printf("---------------------\n");
	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));

	return 0;
}

我们先来回顾以下字符指针相关内容:

对于char* p  = "abcdef";我们是把这个常量字符串的首字符的地址赋值给指针变量 p,如下图

分析:

  • sizeof(p) = 4/8byte

p是一个char*类型的变量

一个指针变量在32位平台上大小为4byte,64位平台上大小为4byte

  • sizeof(p + 1) = 4/8byte

指针±整数还是指针,只不过指向的元素不同

sizeof(p + 1) = sizeof(p) = 4/8byte

  • sizeof(*p) = 1

p中存放的是常量字符串的首字符的地址,*p找到的就是这个常量字符串的首字符'a'

sizeof(*p) = sizeof(a) = sizeof(char) = 1

  • sizeof(p[0]) = 1

p[0] <==> * (p+0)  <==> *p

sizeof(p[0]) = sizeof(*p) = 1

  • sizeof(&p) = 4/8byte

p是一个指针变量,&p就得到一个二级指针变量指p,二级指针变量也是指针

sizeof(&p) = 4/8byte

  • sizeof(&p + 1) = 4/8byte

p是一个指针变量,&p得到一个二级指针变量指p,二级指针变量也是指针,指针±整数还是指针

sizeof(&p) = 4/8byte

  • sizeof(&p[0] + 1) = 4/8byte

&p[0] + 1 = &(*(p+0)) + 1 = &(*p) + 1 = p + 1

所以sizeof(&p[0] + 1) = sizeof(p+1) = 4/8byte

  • strlen(p) = 6

strlen(p)求得是从p指向的位置开始,向后统计字符个数,直到遇到字符'\0'

显然,'\0'字符前元素的个数为6

  • strlen(p + 1) = 5

strlen(p + 1) = 5

求得是从p指向的位置开始,向后统计字符个数,直到遇到字符'\0'

显然,'\0'字符前元素的个数为5

  • strlen(*p)

p指向的是常量字符串首字符,*p得到首字符a,所以strlen(*p) = strlen(a)

strlen函数会把字符'a'的ASCII码值97当作一个地址进行内存访问,程序会报错或崩溃

  • strlen(p[0])

p[0] = *(p+0) = *p,所以strlen(p[0])和strlen(*p)一样,程序会报错或崩溃

  • strlen(&p)

p是一个指针变量,&p得到一个二级指针变量pp指p,对于这个二级指针变量,strlen统计得是其所指向的元素p(一个指针)的四个字节中'\0'字符前的元素个数,这个地址对我们来说是随机的,所以strlen(&p)输出随机值

 

  • strlen(&p + 1)

和strlen(&p)同理,输出随机值

  • strlen(&p[0] + 1)

&p[0] + 1 = &(*(p+0)) + 1 = &(*p) + 1 = p + 1

strlen(&p[0] + 1) = strlen(p + 1) = 5

因为代码中strlen(arr[1]) 和strlen(*arr)存在bug,所以我们把这俩行注释掉再输出:

例题七:二维数组与sizeof

#include<stdio.h>
int main()
{
	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]));

	return 0;
}

分析之前,我们先来回顾一下二维数组名相关知识:

二维数组名a表示的是第一行一维数组的地址

a[0],a[1]......a[i]分别表示数组的第i+1行元素的首地址

a[0]+j 指向数组第一行的第j个元素 

分析:

  • sizeof(a) = 48byte

这里数组名表示的是整个数组的地址,sizeof(a)求得是整个数组的大小 = 3*4*4byte =48byte

  • sizeof(a[0][0]) = 4byte

a[0][0]表示的是第一行的第一个元素,为整型元素,sizeof(a[0][0]) = 4byte

  • sizeof(a[0]) = 16byte

a[0]就相当于一个一维数组的数组名,sizeof(数组名)求得是整个数组的大小

sizeof(a[0])  = 4*4byte = 16

  • sizeof(a[0] + 1) = 4/8byte

a[0]表示的是数组的第一行一维数组首元素的地址,是一个指针,a[0] + 1跳过一个整型数据(a[0]相当于一个整型的指针,指向第一行的第一个元素,整型指针+1跳过一个整型),指向第一行的第二个元素,所以a[0] + 1是指向第一行第二个元素的指针,sizeof(指针)=4byte

  • sizeof(*(a[0] + 1)) = 4byte

因为a[0] + 1是指向第一行第二个元素的指针,所以*(a[0] + 1)就表示第一行第二个元素

这是一个整型数据,sizeof(*(a[0] + 1)) = 4byte

  • sizeof(a + 1) = 4/8byte

二维数组名a表示的是第一行一维数组的地址,所以a是一个指向有四个整型元素数组的数组指针,a+1跳过一个四个整型元素的数组,是指向二维数组第二行的指针,如下图

sizeof(a + 1) = sizeof(指针) = 4/8byte

  • sizeof(*(a + 1)) = 16byte

因为a+1是指向数组第二行的指针,所以*(a + 1)表示二维数组的第二行

所以sizeof(a + 1)计算的是二维数组第二行一维数组的大小 = 4*4byte = 16byte

  • sizeof(&a[0] + 1) = 4/8byte

a[0]是第一行一位数组的数组名,&a[0]得到第一行整个一维数组的地址,&a[0] + 1跳过这个一维数组,指向了二维数组的第二行,仍然是一个指针,所以sizeof(&a[0] + 1) = 4/8byte

  • sizeof(*(&a[0] + 1)) =16byte

因为&a[0]+1是指向数组第二行的指针,所以*(&a[0] + 1)表示二维数组的第二行

所以sizeof(*(&a[0] + 1))计算的是二维数组第二行一维数组的大小 = 4*4byte = 16byte

  • sizeof(*a) = 16byte

二维数组名a表示的是第一行一维数组的地址,*a就是二维数组的第一行

所以sizeof(*a)计算二维数组第一行的大小 = 4*4byte = 16byte

  • sizeof(a[3]) = 16byte;

a[3]表示的就是二维数组的第三行,相当于一个数组名,sizeof(数组名)求整个数组的大小

所以sizeof(a[3]) = 4*4byte = 16byte

注:这里不存在数组越界,因为我们只是计算大小,并没有访问

输出如下:

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值