一篇文章彻底让你弄懂sizeof 和strlen,快来看!

一篇文章彻底让你弄懂sizeof 和strlen,快来看!

前言:
大家在初步学习C语言的时候,可能对于sizeof和strlen的使用有一些混淆,今天我用一篇文章带大家学懂sizeof和strlen。

文章目录

  • 一篇文章彻底让你弄懂sizeof 和strlen,快来看!
    • 1.sizeof和strlen的讲解部分
      • ①sizeof
      • ②strlen
    • 2.实操例题
      • e.g.1 一维数组
      • e.g.2 字符数组
      • e.g.3 二维数组
    • 3.总结

1.sizeof和strlen的讲解部分

①sizeof

a.sizeof既是C语言的关键字也是C语言的操作符,用来计算括号里的变量sizeof(a)或者变量类型sizeof(int)所占内存空间大小,单位是字节。

int main()
{
	int i = 10;
	int arr[] = { 1, 2 };
    int arr[10] = { 1, 2 };
	size_t sz = sizeof(arr);//size_t是专门为sizeof设计的变量类型,返回值是unsigned int
	printf("%zd\n", sizeof(i));//变量
	printf("%zd\n", sizeof(int));//变量类型
	printf("%zd\n", sz);//%zd是专门针对size_t的占位符,8
    printf("%zd\n", sizeof(arr1));//40,如果已经定义了数组长度,那就按照定义的来求。
	return 0;
}

b.sizeof关注的是括号里的表达式的值属性,也就是根据类型决定sizeof的值,sizeof里面如果是一个计算的式子,这个式子都不会执行,因为在编译链接的时候已经把sizeof里面的类型确定了,在可执行文件里计算时,不执行这个式子。

int main()
{
	short a = 10;
	printf("%zd\n", sizeof(a = a + 10));//由类型决定,2
	printf("%d\n", a);//10
}

为了更加生动,我给大家看一下这个程序的汇编语言,也就是编译连接后的语句。

一篇文章彻底让你弄懂sizeof 和strlen,快来看!_第1张图片
我们可以看到在变成可执行.exe文件后,sizeof的括号里面已经变成了2,不需要再进行算术计算了,a也就是10。

c.重中之重:

arr数组名代表的是数组首元素地址,只有两种特殊情况,sizeof(arr)和&arr,一个是求整个数组所占内存空间大小,另一个是取整个数组的地址。

②strlen

a.strlen是库函数,头文件是#include ,是一个专门求字符串长度的函数,所以大家不要用它来求整型数组了,虽然得出的结果也可以解释,但是不对,不合理,不可以!

size_t strlen(const char* p);

b.stlen是从这个函数参数p向后一直寻找到’\0’为止,统计出’\0’之前的字符数。一个字符一个字符读,也就是1个字节一个字节读。

c.因为会一直向后找到’\0’为止,所以可能会存在越界访问,这也就解释了我上文为什么说strlen求整型数组得到的结果也可以解释。

我们来看一组对比,就更加清晰了。

#include 
int main()
{
     char arr1[3] = {'a', 'b', 'c'};
     char arr2[] = "abc";
     printf("%d\n", strlen(arr1));
     printf("%d\n", strlen(arr2));//3

     printf("%d\n", sizeof(arr1));//3
     printf("%d\n", sizeof(arr2));//4,因为还有一个'\0'
     return 0;
}

一篇文章彻底让你弄懂sizeof 和strlen,快来看!_第2张图片

至于第一个35,这个在不同的设备,不同的IDE上一定是不同的,因为内存分配是不一样的,我们可以看下面的这张内存分配图,每一行是八个字节,起始的字节就是数组的首元素,我们可以一直向后找到00,00之前一共有35个字节,所以答案是35。
一篇文章彻底让你弄懂sizeof 和strlen,快来看!_第3张图片
有了上面的经验,相信大家也对于strlen为什么不能求整型数组的长度明白了,举个例子。

int main()
{
	int arr1[] = { 1, 2, 3 };
	int arr2[] = { 0, 1, 2 };
	printf("%zd\n", strlen(arr1));//1
	printf("%zd\n", strlen(arr2));//0
	return 0;
}

一篇文章彻底让你弄懂sizeof 和strlen,快来看!_第4张图片
解释:
第一个数组排列:X64平台上,01 00 00 00 00 00 00 00,当读到第二个字节00就结束了,所以是1。
第二个数组首字节直接就是0,所以是0。

2.实操例题

e.g.1 一维数组

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//整个数组所占内存空间大小,4 * 4 = 16
	printf("%d\n", sizeof(a + 0));//a是首元素地址,所以是求指针变量的大小,4/8
	printf("%d\n", sizeof(*a));//*a = 1, 所以是4
	printf("%d\n", sizeof(a + 1));//指针变量,4/8
	printf("%d\n", sizeof(a[1]));//a[1] = 2,4
	printf("%d\n", sizeof(&a));//地址,也是指针变量,4/8
	printf("%d\n", sizeof(*&a));//sizeof(a),16
	printf("%d\n", sizeof(&a + 1));//跳过了一个数组的地址,4/8
	printf("%d\n", sizeof(&a[0]));//4/8
	printf("%d\n", sizeof(&a[0] + 1));//&arr[1],4/8
	return 0;
}

e.g.2 字符数组

int main()
{
    char arr[] = {'a','b','c','d','e','f'};
    printf("%d\n", sizeof(arr));//6
    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
    printf("%d\n", strlen(arr));//随机值,因为目前不确定到哪里遇到字节00
	printf("%d\n", strlen(arr+0));//随机值,和上一个一样
	printf("%d\n", strlen(*arr));//无法输出,非法访问,访问冲突,*arr = a,访问以a的ASCII码值97为地址既地址为0000000000000061的内存空间,但是其实是没有权限访问的
	printf("%d\n", strlen(arr[1]));//无法输出×,下文都以此代表访问冲突
	printf("%d\n", strlen(&arr));//随机值,和strlen(arr)是一个值
	printf("%d\n", strlen(&arr+1));//随机值,比strlen(arr)小6
	printf("%d\n", strlen(&arr[0]+1));//随机值,比strlen(arr)小1
    return 0;
}

为了方便看清运行结果,我将其中两个访问冲突的先注释掉了,我们直接来验证运行结果。
一篇文章彻底让你弄懂sizeof 和strlen,快来看!_第5张图片

int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//7,有'\0'
	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
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//6
	printf("%d\n", strlen(*arr));//×
	printf("%d\n", strlen(arr[1]));//×
	printf("%d\n", strlen(&arr));//6,指针取得也是第一个元素的地址
	printf("%d\n", strlen(&arr + 1));//随机值
	printf("%d\n", strlen(&arr[0] + 1));//5
	return 0;
}

3.这是最难的字符串数组的题

int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));//4/8
	printf("%d\n", sizeof(p + 1));//4/8
	printf("%d\n", sizeof(*p));//1,*p解引用出来的就是a,这是字符指针变量的用法,p直接用才是字符串
	printf("%d\n", sizeof(p[0]));//p[0]就是a,所以是1
	printf("%d\n", sizeof(&p));//4/8
	printf("%d\n", sizeof(&p + 1));//4/8
	printf("%d\n", sizeof(&p[0] + 1));//4/8
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//5
	printf("%d\n", strlen(*p));//×,*p = a,错误
	printf("%d\n", strlen(p[0]));//× p[0] = a ,错误
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p + 1));//随机值
	printf("%d\n", strlen(&p[0] + 1));//随机值
	return 0;
}

本题总结:
1.使用了字符指针变量存放常量字符串这种用法,有关这种用法我在上一篇博客,链接:link中详细讲解了。
2.针对上述三个题,我们一定要谨记,strlen里面接收的参数一定要是地址!!!

e.g.3 二维数组

int main()
{
	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]));//第一行数组,16
	printf("%d\n", sizeof(a[0] + 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)));//16,第二行数组
	printf("%d\n", sizeof(&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]));//16
	return 0;
}

3.总结

1.后面的例题涉及到了一部分指针和数组名的使用,总之记住一句话,除了sizeof(arr)和&arr这两种一模一样的形式时,数组名代表的是整个数组的地址(包括sizeof(*arr)这样也不属于这两种特殊类型),其余数组名代表的都是首元素地址。
2.其实sizeof和strlen本质上是完全不同的,只不过他们都与长度、数组和指针联系起来了。而我们需要掌握的就是他们在各种不同用法之间抓住区别。

你可能感兴趣的:(算法,c语言,开发语言,数据结构)